[deploy] Introduces Runtime class for standardized & other native cleanup + replace podcast GA with ahoy (#10026)
This commit is contained in:
parent
cd1db855b6
commit
9da71a47c7
7 changed files with 141 additions and 138 deletions
|
|
@ -1,3 +1,5 @@
|
|||
/* global Runtime */
|
||||
|
||||
function initializeCommentDropdown() {
|
||||
const announcer = document.getElementById('article-copy-link-announcer');
|
||||
|
||||
|
|
@ -7,14 +9,6 @@ function initializeCommentDropdown() {
|
|||
);
|
||||
}
|
||||
|
||||
function isNativeAndroidDevice() {
|
||||
return (
|
||||
navigator.userAgent === 'DEV-Native-android' &&
|
||||
typeof AndroidBridge !== 'undefined' &&
|
||||
AndroidBridge !== null
|
||||
);
|
||||
}
|
||||
|
||||
function removeClass(className) {
|
||||
return (element) => element.classList.remove(className);
|
||||
}
|
||||
|
|
@ -47,7 +41,7 @@ function initializeCommentDropdown() {
|
|||
|
||||
function copyText() {
|
||||
const inputValue = document.getElementById('article-copy-link-input').value;
|
||||
if (isNativeAndroidDevice()) {
|
||||
if (Runtime.isNativeAndroid('copyToClipboard')) {
|
||||
AndroidBridge.copyToClipboard(inputValue);
|
||||
showAnnouncer();
|
||||
} else if (isClipboardSupported()) {
|
||||
|
|
@ -113,6 +107,13 @@ function initializeCommentDropdown() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Android native apps have enhanced sharing capabilities for Articles
|
||||
const articleShowMoreClicked = button.id === 'article-show-more-button';
|
||||
if (articleShowMoreClicked && Runtime.isNativeAndroid('shareText')) {
|
||||
AndroidBridge.shareText(location.href);
|
||||
return;
|
||||
}
|
||||
|
||||
finalizeAbuseReportLink(
|
||||
dropdownContent.querySelector('.report-abuse-link-wrapper'),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -22,12 +22,15 @@
|
|||
* the problem of using a method before it's defined:
|
||||
*/
|
||||
|
||||
/* global ahoy, Runtime */
|
||||
/* eslint no-use-before-define: 0 */
|
||||
/* eslint no-param-reassign: 0 */
|
||||
|
||||
var audioInitialized = false;
|
||||
|
||||
function initializePodcastPlayback() {
|
||||
var deviceType = 'web';
|
||||
|
||||
function getById(name) {
|
||||
return document.getElementById(name);
|
||||
}
|
||||
|
|
@ -100,41 +103,6 @@ function initializePodcastPlayback() {
|
|||
return podcastLiquidTagrecords;
|
||||
}
|
||||
|
||||
function isNativePlayer() {
|
||||
return isNativeIOS() || isNativeAndroid();
|
||||
}
|
||||
|
||||
function isNativeIOS() {
|
||||
return (
|
||||
navigator.userAgent === 'DEV-Native-ios' &&
|
||||
window &&
|
||||
window.webkit &&
|
||||
window.webkit.messageHandlers &&
|
||||
window.webkit.messageHandlers.podcast
|
||||
);
|
||||
}
|
||||
|
||||
function isNativeAndroid() {
|
||||
return (
|
||||
navigator.userAgent === 'DEV-Native-android' &&
|
||||
typeof AndroidBridge !== 'undefined' &&
|
||||
AndroidBridge !== null &&
|
||||
AndroidBridge.podcastMessage !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
function sendNativeMessage(message) {
|
||||
try {
|
||||
if (isNativeIOS()) {
|
||||
window.webkit.messageHandlers.podcast.postMessage(message);
|
||||
} else if (isNativeAndroid()) {
|
||||
AndroidBridge.podcastMessage(JSON.stringify(message));
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err.message); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
function saveMediaState(state) {
|
||||
var currentState = state || currentAudioState();
|
||||
var newState = newAudioState();
|
||||
|
|
@ -189,8 +157,8 @@ function initializePodcastPlayback() {
|
|||
}
|
||||
|
||||
function loadAudio(audio) {
|
||||
if (isNativePlayer()) {
|
||||
sendNativeMessage({
|
||||
if (Runtime.podcastMessage) {
|
||||
Runtime.podcastMessage({
|
||||
action: 'load',
|
||||
url: audio.querySelector('source').src,
|
||||
});
|
||||
|
|
@ -244,8 +212,8 @@ function initializePodcastPlayback() {
|
|||
}
|
||||
saveMediaState(currentState);
|
||||
|
||||
if (isNativePlayer()) {
|
||||
sendNativeMessage({
|
||||
if (Runtime.podcastMessage) {
|
||||
Runtime.podcastMessage({
|
||||
action: 'rate',
|
||||
rate: currentState.playbackRate.toString(),
|
||||
});
|
||||
|
|
@ -285,8 +253,8 @@ function initializePodcastPlayback() {
|
|||
function playAudio(audio) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var currentState = currentAudioState();
|
||||
if (isNativePlayer()) {
|
||||
sendNativeMessage({
|
||||
if (Runtime.podcastMessage) {
|
||||
Runtime.podcastMessage({
|
||||
action: 'play',
|
||||
url: audio.querySelector('source').src,
|
||||
seconds: currentState.currentTime.toString(),
|
||||
|
|
@ -319,23 +287,23 @@ function initializePodcastPlayback() {
|
|||
}
|
||||
|
||||
function sendMetadataMessage() {
|
||||
try {
|
||||
var metadata = JSON.parse(fetchMetadataString());
|
||||
sendNativeMessage({
|
||||
action: 'metadata',
|
||||
episodeName: metadata.episodeName,
|
||||
podcastName: metadata.podcastName,
|
||||
podcastImageUrl: metadata.podcastImageUrl,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('Unable to load Podcast Episode metadata', e); // eslint-disable-line no-console
|
||||
if (Runtime.podcastMessage) {
|
||||
try {
|
||||
var metadata = JSON.parse(fetchMetadataString());
|
||||
Runtime.podcastMessage({
|
||||
action: 'metadata',
|
||||
episodeName: metadata.episodeName,
|
||||
podcastName: metadata.podcastName,
|
||||
podcastImageUrl: metadata.podcastImageUrl,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('Unable to load Podcast Episode metadata', e); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startAudioPlayback(audio) {
|
||||
if (isNativePlayer()) {
|
||||
sendMetadataMessage();
|
||||
}
|
||||
sendMetadataMessage();
|
||||
|
||||
playAudio(audio)
|
||||
.then(function () {
|
||||
|
|
@ -352,8 +320,8 @@ function initializePodcastPlayback() {
|
|||
}
|
||||
|
||||
function pauseAudioPlayback(audio) {
|
||||
if (isNativePlayer()) {
|
||||
sendNativeMessage({ action: 'pause' });
|
||||
if (Runtime.podcastMessage) {
|
||||
Runtime.podcastMessage({ action: 'pause' });
|
||||
} else {
|
||||
audio.pause();
|
||||
}
|
||||
|
|
@ -367,29 +335,21 @@ function initializePodcastPlayback() {
|
|||
window.activePodcast = audio.getAttribute('data-podcast');
|
||||
|
||||
var currentState = currentAudioState();
|
||||
var properties = {
|
||||
episode: window.activeEpisode,
|
||||
podcast: window.activePodcast,
|
||||
deviceType: deviceType,
|
||||
};
|
||||
if (!currentState.playing) {
|
||||
ga(
|
||||
'send',
|
||||
'event',
|
||||
'click',
|
||||
'play podcast',
|
||||
`${window.activePodcast} ${window.activeEpisode}`,
|
||||
null,
|
||||
);
|
||||
properties.action = 'play';
|
||||
changeStatusMessage('initializing...');
|
||||
startAudioPlayback(audio);
|
||||
} else {
|
||||
ga(
|
||||
'send',
|
||||
'event',
|
||||
'click',
|
||||
'pause podcast',
|
||||
`${window.activePodcast} ${window.activeEpisode}`,
|
||||
null,
|
||||
);
|
||||
properties.action = 'pause';
|
||||
pauseAudioPlayback(audio);
|
||||
changeStatusMessage(null);
|
||||
}
|
||||
ahoy.track('Podcast Player Streaming', properties);
|
||||
}
|
||||
|
||||
function muteUnmute(audio) {
|
||||
|
|
@ -408,8 +368,8 @@ function initializePodcastPlayback() {
|
|||
);
|
||||
|
||||
currentState.muted = !currentState.muted;
|
||||
if (isNativePlayer()) {
|
||||
sendNativeMessage({
|
||||
if (Runtime.podcastMessage) {
|
||||
Runtime.podcastMessage({
|
||||
action: 'muted',
|
||||
muted: currentState.muted.toString(),
|
||||
});
|
||||
|
|
@ -422,8 +382,8 @@ function initializePodcastPlayback() {
|
|||
function updateVolume(e, audio) {
|
||||
var currentState = currentAudioState();
|
||||
currentState.volume = e.target.value / 100;
|
||||
if (isNativePlayer()) {
|
||||
sendNativeMessage({ action: 'volume', volume: currentState.volume });
|
||||
if (Runtime.podcastMessage) {
|
||||
Runtime.podcastMessage({ action: 'volume', volume: currentState.volume });
|
||||
} else {
|
||||
audio.volume = currentState.volume;
|
||||
}
|
||||
|
|
@ -463,8 +423,8 @@ function initializePodcastPlayback() {
|
|||
var duration = currentState.duration;
|
||||
currentState.currentTime = duration * percent; // jumps to 29th secs
|
||||
|
||||
if (isNativePlayer()) {
|
||||
sendNativeMessage({
|
||||
if (Runtime.podcastMessage) {
|
||||
Runtime.podcastMessage({
|
||||
action: 'seek',
|
||||
seconds: currentState.currentTime.toString(),
|
||||
});
|
||||
|
|
@ -498,8 +458,8 @@ function initializePodcastPlayback() {
|
|||
getById('audiocontent').innerHTML = '';
|
||||
stopRotatingActivePodcastIfExist();
|
||||
saveMediaState(newAudioState());
|
||||
if (isNativePlayer()) {
|
||||
sendNativeMessage({ action: 'terminate' });
|
||||
if (Runtime.podcastMessage) {
|
||||
Runtime.podcastMessage({ action: 'terminate' });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -540,6 +500,29 @@ function initializePodcastPlayback() {
|
|||
mutationObserver.observe(getById('audiocontent'), { attributes: true });
|
||||
}
|
||||
|
||||
// When Runtime.podcastMessage is undefined we need to execute web logic
|
||||
function initRuntime() {
|
||||
if (Runtime.isNativeIOS('podcast')) {
|
||||
deviceType = 'iOS';
|
||||
Runtime.podcastMessage = function (message) {
|
||||
try {
|
||||
window.webkit.messageHandlers.podcast.postMessage(message);
|
||||
} catch (err) {
|
||||
console.log(err.message); // eslint-disable-line no-console
|
||||
}
|
||||
};
|
||||
} else if (Runtime.isNativeAndroid('podcastMessage')) {
|
||||
deviceType = 'Android';
|
||||
Runtime.podcastMessage = function (message) {
|
||||
try {
|
||||
AndroidBridge.podcastMessage(JSON.stringify(message));
|
||||
} catch (err) {
|
||||
console.log(err.message); // eslint-disable-line no-console
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function initializeMedia() {
|
||||
var currentState = currentAudioState();
|
||||
document.getElementById('audiocontent').innerHTML = currentState.html;
|
||||
|
|
@ -548,7 +531,7 @@ function initializePodcastPlayback() {
|
|||
audioInitialized = false;
|
||||
return;
|
||||
}
|
||||
if (!isNativeIOS()) {
|
||||
if (Runtime.podcastMessage) {
|
||||
audio.currentTime = currentState.currentTime || 0;
|
||||
}
|
||||
loadAudio(audio);
|
||||
|
|
@ -574,6 +557,7 @@ function initializePodcastPlayback() {
|
|||
saveMediaState(currentState);
|
||||
}
|
||||
|
||||
initRuntime();
|
||||
spinPodcastRecord();
|
||||
findAndApplyOnclickToRecords();
|
||||
if (!audioInitialized) {
|
||||
|
|
|
|||
|
|
@ -12,11 +12,9 @@
|
|||
/* eslint no-use-before-define: 0 */
|
||||
/* eslint no-param-reassign: 0 */
|
||||
/* eslint no-useless-escape: 0 */
|
||||
/* global jwplayer */
|
||||
/* global ahoy */
|
||||
/* global jwplayer, ahoy, Runtime */
|
||||
|
||||
function initializeVideoPlayback() {
|
||||
var nativeBridgeMessage;
|
||||
var currentTime = '0';
|
||||
var deviceType = 'web';
|
||||
var lastEvent = '';
|
||||
|
|
@ -25,25 +23,6 @@ function initializeVideoPlayback() {
|
|||
return document.getElementById(name);
|
||||
}
|
||||
|
||||
function isNativeIOS() {
|
||||
return (
|
||||
navigator.userAgent === 'DEV-Native-ios' &&
|
||||
window &&
|
||||
window.webkit &&
|
||||
window.webkit.messageHandlers &&
|
||||
window.webkit.messageHandlers.video
|
||||
);
|
||||
}
|
||||
|
||||
function isNativeAndroid() {
|
||||
return (
|
||||
navigator.userAgent === 'DEV-Native-android' &&
|
||||
typeof AndroidBridge !== 'undefined' &&
|
||||
AndroidBridge !== null &&
|
||||
AndroidBridge.videoMessage !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
function getParameterByName(name, url) {
|
||||
if (!url) url = window.location.href;
|
||||
name = name.replace(/[\[\]]/g, '\\$&');
|
||||
|
|
@ -138,7 +117,7 @@ function initializeVideoPlayback() {
|
|||
getById('pause-butt').classList.add('active');
|
||||
getById('play-butt').classList.remove('active');
|
||||
|
||||
nativeBridgeMessage({
|
||||
Runtime.videoMessage({
|
||||
action: 'play',
|
||||
url: metadata.video_source_url,
|
||||
seconds: currentTime,
|
||||
|
|
@ -174,18 +153,18 @@ function initializeVideoPlayback() {
|
|||
var seconds = timeToSeconds(getParameterByName('t') || '0');
|
||||
var metadata = videoMetadata(videoSource);
|
||||
|
||||
if (isNativeIOS()) {
|
||||
if (Runtime.isNativeIOS('video')) {
|
||||
deviceType = 'iOS';
|
||||
nativeBridgeMessage = function (message) {
|
||||
Runtime.videoMessage = function (message) {
|
||||
try {
|
||||
window.webkit.messageHandlers.video.postMessage(message);
|
||||
} catch (err) {
|
||||
console.log(err.message); // eslint-disable-line no-console
|
||||
}
|
||||
};
|
||||
} else if (isNativeAndroid()) {
|
||||
} else if (Runtime.isNativeAndroid('videoMessage')) {
|
||||
deviceType = 'Android';
|
||||
nativeBridgeMessage = function (message) {
|
||||
Runtime.videoMessage = function (message) {
|
||||
try {
|
||||
AndroidBridge.videoMessage(JSON.stringify(message));
|
||||
} catch (err) {
|
||||
|
|
|
|||
54
app/assets/javascripts/initializers/runtime.js
Normal file
54
app/assets/javascripts/initializers/runtime.js
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* This class helps managing native feature support. Can easily be referenced
|
||||
* from anywhere in JavaScript with:
|
||||
*
|
||||
* if (Runtime.isNativeiOS('video')) { ... }
|
||||
*
|
||||
* if (Runtime.isNativeAndroid('podcastMessage')) { ... }
|
||||
*/
|
||||
class Runtime {
|
||||
/**
|
||||
* Checks the device for iOS (webkit) native feature support
|
||||
*
|
||||
* @function isNativeIOS
|
||||
* @param {string} namespace Specifies support for a specific feature
|
||||
* (i.e. video, podcast, etc)
|
||||
* @returns {boolean} true if current environment support native features
|
||||
*/
|
||||
static isNativeIOS(namespace = null) {
|
||||
const nativeCheck =
|
||||
/DEV-Native-ios|forem-native-ios/i.test(navigator.userAgent) &&
|
||||
navigator.userAgent === '' &&
|
||||
window &&
|
||||
window.webkit &&
|
||||
window.webkit.messageHandlers;
|
||||
|
||||
let namespaceCheck = true;
|
||||
if (nativeCheck && namespace) {
|
||||
namespaceCheck = window.webkit.messageHandlers[namespace] != undefined;
|
||||
}
|
||||
|
||||
return nativeCheck && namespaceCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the device for Android native feature support
|
||||
*
|
||||
* @function isNativeAndroid
|
||||
* @param {string} namespace Specifies support for a specific feature
|
||||
* (i.e. videoMessage, podcastMessage, etc)
|
||||
* @returns {boolean} true if current environment support native features
|
||||
*/
|
||||
static isNativeAndroid(namespace = null) {
|
||||
const nativeCheck =
|
||||
/DEV-Native-android|forem-native-android/i.test(navigator.userAgent) &&
|
||||
AndroidBridge != undefined;
|
||||
|
||||
let namespaceCheck = true;
|
||||
if (nativeCheck && namespace) {
|
||||
namespaceCheck = AndroidBridge[namespace] != undefined;
|
||||
}
|
||||
|
||||
return nativeCheck && namespaceCheck;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
/* global Runtime */
|
||||
|
||||
import { h } from 'preact';
|
||||
import { useReducer } from 'preact/hooks';
|
||||
import { generateMainImage } from '../actions';
|
||||
|
|
@ -5,14 +7,6 @@ import { validateFileInputs } from '../../packs/validateFileInputs';
|
|||
import { ClipboardButton } from './ClipboardButton';
|
||||
import { Button, Spinner } from '@crayons';
|
||||
|
||||
function isNativeAndroid() {
|
||||
return (
|
||||
navigator.userAgent === 'DEV-Native-android' &&
|
||||
typeof AndroidBridge !== 'undefined' &&
|
||||
AndroidBridge !== null
|
||||
);
|
||||
}
|
||||
|
||||
function isClipboardSupported() {
|
||||
return (
|
||||
typeof navigator.clipboard !== 'undefined' && navigator.clipboard !== null
|
||||
|
|
@ -108,7 +102,7 @@ export const ImageUploader = () => {
|
|||
'image-markdown-copy-link-input',
|
||||
);
|
||||
|
||||
if (isNativeAndroid()) {
|
||||
if (Runtime.isNativeAndroid('copyToClipboard')) {
|
||||
AndroidBridge.copyToClipboard(imageMarkdownInput.value);
|
||||
dispatch({
|
||||
type: 'show_copied_image_message',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/* global Runtime */
|
||||
|
||||
import { h, Component } from 'preact';
|
||||
import PropTypes from 'prop-types';
|
||||
import { isNativeAndroid } from '../../../utilities/validateAndroidNative';
|
||||
|
||||
import { Button } from '@crayons';
|
||||
|
||||
|
|
@ -46,7 +47,7 @@ export default class InvitationLinkManager extends Component {
|
|||
'chat-channel-unviation-url',
|
||||
);
|
||||
|
||||
if (isNativeAndroid()) {
|
||||
if (Runtime.isNativeAndroid('copyToClipboard')) {
|
||||
AndroidBridge.copyToClipboard(this.imageMarkdownInput.value);
|
||||
this.setState({ showImageCopiedMessage: true });
|
||||
} else if (isClipboardSupported()) {
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
/**
|
||||
* This function is used to check that is Device is android native or not
|
||||
*/
|
||||
export function isNativeAndroid() {
|
||||
return (
|
||||
navigator.userAgent === 'DEV-Native-android' &&
|
||||
typeof AndroidBridge !== 'undefined' &&
|
||||
AndroidBridge !== null
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue