mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-04-20 23:12:13 +00:00
Merge pull request #690 from Stremio/feat/open-external-shell
feat: impl openExternal for shell compatibility
This commit is contained in:
commit
024df8eeec
18 changed files with 176 additions and 75 deletions
9
package-lock.json
generated
9
package-lock.json
generated
|
|
@ -36,7 +36,7 @@
|
||||||
"react-i18next": "^12.1.1",
|
"react-i18next": "^12.1.1",
|
||||||
"react-is": "18.2.0",
|
"react-is": "18.2.0",
|
||||||
"spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
|
"spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
|
||||||
"stremio-translations": "github:Stremio/stremio-translations#b13b3e2653bd0dcf644d2a20ffa32074fe6532dd",
|
"stremio-translations": "github:Stremio/stremio-translations#378218c9617f3e763ba5f6755e4d39c1c158747d",
|
||||||
"url": "0.11.0",
|
"url": "0.11.0",
|
||||||
"use-long-press": "^3.1.5"
|
"use-long-press": "^3.1.5"
|
||||||
},
|
},
|
||||||
|
|
@ -12470,10 +12470,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/stremio-translations": {
|
"node_modules/stremio-translations": {
|
||||||
"version": "1.44.7",
|
"version": "1.44.9",
|
||||||
"resolved": "git+ssh://git@github.com/Stremio/stremio-translations.git#b13b3e2653bd0dcf644d2a20ffa32074fe6532dd",
|
"resolved": "git+ssh://git@github.com/Stremio/stremio-translations.git#378218c9617f3e763ba5f6755e4d39c1c158747d",
|
||||||
"integrity": "sha512-OtRAM3j9ie89llgI379p4utCbgnNMswE+LtL/lyLRVLfm5B+jpBLp4ozpU25iQg0O4tvN+OHBjXZ870CCHtZMA==",
|
"integrity": "sha512-3GboN8JS2LgrdIVK/gW+n6r1kLrGG+D/tWkRv8PJo2mZLzh49HTzS2u7XXUSkNmA4AGUyEf8QRjyBhlOg8JNTQ=="
|
||||||
"license": "MIT"
|
|
||||||
},
|
},
|
||||||
"node_modules/string_decoder": {
|
"node_modules/string_decoder": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
"react-i18next": "^12.1.1",
|
"react-i18next": "^12.1.1",
|
||||||
"react-is": "18.2.0",
|
"react-is": "18.2.0",
|
||||||
"spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
|
"spatial-navigation-polyfill": "github:Stremio/spatial-navigation#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
|
||||||
"stremio-translations": "github:Stremio/stremio-translations#b13b3e2653bd0dcf644d2a20ffa32074fe6532dd",
|
"stremio-translations": "github:Stremio/stremio-translations#378218c9617f3e763ba5f6755e4d39c1c158747d",
|
||||||
"url": "0.11.0",
|
"url": "0.11.0",
|
||||||
"use-long-press": "^3.1.5"
|
"use-long-press": "^3.1.5"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ const { useTranslation } = require('react-i18next');
|
||||||
const { Router } = require('stremio-router');
|
const { Router } = require('stremio-router');
|
||||||
const { Core, Shell, Chromecast, DragAndDrop, KeyboardShortcuts, ServicesProvider } = require('stremio/services');
|
const { Core, Shell, Chromecast, DragAndDrop, KeyboardShortcuts, ServicesProvider } = require('stremio/services');
|
||||||
const { NotFound } = require('stremio/routes');
|
const { NotFound } = require('stremio/routes');
|
||||||
const { ToastProvider, TooltipProvider, CONSTANTS, withCoreSuspender } = require('stremio/common');
|
const { PlatformProvider, ToastProvider, TooltipProvider, CONSTANTS, withCoreSuspender } = require('stremio/common');
|
||||||
const ServicesToaster = require('./ServicesToaster');
|
const ServicesToaster = require('./ServicesToaster');
|
||||||
const DeepLinkHandler = require('./DeepLinkHandler');
|
const DeepLinkHandler = require('./DeepLinkHandler');
|
||||||
const SearchParamsHandler = require('./SearchParamsHandler');
|
const SearchParamsHandler = require('./SearchParamsHandler');
|
||||||
|
|
@ -162,18 +162,20 @@ const App = () => {
|
||||||
services.core.error instanceof Error ?
|
services.core.error instanceof Error ?
|
||||||
<ErrorDialog className={styles['error-container']} />
|
<ErrorDialog className={styles['error-container']} />
|
||||||
:
|
:
|
||||||
<ToastProvider className={styles['toasts-container']}>
|
<PlatformProvider>
|
||||||
<TooltipProvider className={styles['tooltip-container']}>
|
<ToastProvider className={styles['toasts-container']}>
|
||||||
<ServicesToaster />
|
<TooltipProvider className={styles['tooltip-container']}>
|
||||||
<DeepLinkHandler />
|
<ServicesToaster />
|
||||||
<SearchParamsHandler />
|
<DeepLinkHandler />
|
||||||
<RouterWithProtectedRoutes
|
<SearchParamsHandler />
|
||||||
className={styles['router']}
|
<RouterWithProtectedRoutes
|
||||||
viewsConfig={routerViewsConfig}
|
className={styles['router']}
|
||||||
onPathNotMatch={onPathNotMatch}
|
viewsConfig={routerViewsConfig}
|
||||||
/>
|
onPathNotMatch={onPathNotMatch}
|
||||||
</TooltipProvider>
|
/>
|
||||||
</ToastProvider>
|
</TooltipProvider>
|
||||||
|
</ToastProvider>
|
||||||
|
</PlatformProvider>
|
||||||
:
|
:
|
||||||
<div className={styles['loader-container']} />
|
<div className={styles['loader-container']} />
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const ModalDialog = require('stremio/common/ModalDialog');
|
const ModalDialog = require('stremio/common/ModalDialog');
|
||||||
const { withCoreSuspender } = require('stremio/common/CoreSuspender');
|
const { usePlatform, withCoreSuspender } = require('stremio/common/CoreSuspender');
|
||||||
const { useServices } = require('stremio/services');
|
const { useServices } = require('stremio/services');
|
||||||
const AddonDetailsWithRemoteAndLocalAddon = withRemoteAndLocalAddon(require('./AddonDetails'));
|
const AddonDetailsWithRemoteAndLocalAddon = withRemoteAndLocalAddon(require('./AddonDetails'));
|
||||||
const useAddonDetails = require('./useAddonDetails');
|
const useAddonDetails = require('./useAddonDetails');
|
||||||
|
|
@ -43,6 +43,7 @@ function withRemoteAndLocalAddon(AddonDetails) {
|
||||||
|
|
||||||
const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => {
|
const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => {
|
||||||
const { core } = useServices();
|
const { core } = useServices();
|
||||||
|
const platform = usePlatform();
|
||||||
const addonDetails = useAddonDetails(transportUrl);
|
const addonDetails = useAddonDetails(transportUrl);
|
||||||
const modalButtons = React.useMemo(() => {
|
const modalButtons = React.useMemo(() => {
|
||||||
const cancelButton = {
|
const cancelButton = {
|
||||||
|
|
@ -68,7 +69,7 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => {
|
||||||
label: 'Configure',
|
label: 'Configure',
|
||||||
props: {
|
props: {
|
||||||
onClick: (event) => {
|
onClick: (event) => {
|
||||||
window.open(transportUrl.replace('manifest.json', 'configure'));
|
platform.openExternal(transportUrl.replace('manifest.json', 'configure'));
|
||||||
if (typeof onCloseRequest === 'function') {
|
if (typeof onCloseRequest === 'function') {
|
||||||
onCloseRequest({
|
onCloseRequest({
|
||||||
type: 'configure',
|
type: 'configure',
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,8 @@ const EXTERNAL_PLAYERS = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const WHITELISTED_HOSTS = ['stremio.com', 'strem.io', 'stremio.zendesk.com', 'google.com', 'youtube.com', 'twitch.tv', 'twitter.com', 'netflix.com', 'adex.network', 'amazon.com', 'forms.gle'];
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
CHROMECAST_RECEIVER_APP_ID,
|
CHROMECAST_RECEIVER_APP_ID,
|
||||||
SUBTITLES_SIZES,
|
SUBTITLES_SIZES,
|
||||||
|
|
@ -110,4 +112,5 @@ module.exports = {
|
||||||
TYPE_PRIORITIES,
|
TYPE_PRIORITIES,
|
||||||
ICON_FOR_TYPE,
|
ICON_FOR_TYPE,
|
||||||
EXTERNAL_PLAYERS,
|
EXTERNAL_PLAYERS,
|
||||||
|
WHITELISTED_HOSTS,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
51
src/common/Platform/Platform.tsx
Normal file
51
src/common/Platform/Platform.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import React, { createContext, useContext } from 'react';
|
||||||
|
import { WHITELISTED_HOSTS } from 'stremio/common/CONSTANTS';
|
||||||
|
import useShell from './useShell';
|
||||||
|
import { name, isMobile } from './device';
|
||||||
|
|
||||||
|
interface PlatformContext {
|
||||||
|
name: string;
|
||||||
|
isMobile: boolean;
|
||||||
|
openExternal: (url: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PlatformContext = createContext<PlatformContext | null>(null);
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: JSX.Element;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PlatformProvider = ({ children }: Props) => {
|
||||||
|
const shell = useShell();
|
||||||
|
|
||||||
|
const openExternal = (url: string) => {
|
||||||
|
try {
|
||||||
|
const { hostname } = new URL(url);
|
||||||
|
const isWhitelisted = WHITELISTED_HOSTS.some((host: string) => hostname.endsWith(host));
|
||||||
|
const finalUrl = !isWhitelisted ? `https://www.stremio.com/warning#${encodeURIComponent(url)}` : url;
|
||||||
|
|
||||||
|
if (shell.active) {
|
||||||
|
shell.send('open-external', finalUrl);
|
||||||
|
} else {
|
||||||
|
window.open(finalUrl, '_blank');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to parse external url:', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PlatformContext.Provider value={{ openExternal, name, isMobile }}>
|
||||||
|
{children}
|
||||||
|
</PlatformContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const usePlatform = () => {
|
||||||
|
return useContext(PlatformContext);
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
PlatformProvider,
|
||||||
|
usePlatform
|
||||||
|
};
|
||||||
31
src/common/Platform/device.ts
Normal file
31
src/common/Platform/device.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import Bowser from 'bowser';
|
||||||
|
|
||||||
|
const APPLE_MOBILE_DEVICES = [
|
||||||
|
'iPad Simulator',
|
||||||
|
'iPhone Simulator',
|
||||||
|
'iPod Simulator',
|
||||||
|
'iPad',
|
||||||
|
'iPhone',
|
||||||
|
'iPod',
|
||||||
|
];
|
||||||
|
|
||||||
|
const { userAgent, platform, maxTouchPoints } = globalThis.navigator;
|
||||||
|
|
||||||
|
// this detects ipad properly in safari
|
||||||
|
// while bowser does not
|
||||||
|
const isIOS = APPLE_MOBILE_DEVICES.includes(platform) || (userAgent.includes('Mac') && 'ontouchend' in document);
|
||||||
|
|
||||||
|
// Edge case: iPad is included in this function
|
||||||
|
// Keep in mind maxTouchPoints for Vision Pro might change in the future
|
||||||
|
const isVisionOS = userAgent.includes('Macintosh') || maxTouchPoints === 5;
|
||||||
|
|
||||||
|
const bowser = Bowser.getParser(userAgent);
|
||||||
|
const os = bowser.getOSName().toLowerCase();
|
||||||
|
|
||||||
|
const name = isVisionOS ? 'visionos' : isIOS ? 'ios' : os || 'unknown';
|
||||||
|
const isMobile = ['ios', 'android'].includes(name);
|
||||||
|
|
||||||
|
export {
|
||||||
|
name,
|
||||||
|
isMobile,
|
||||||
|
};
|
||||||
5
src/common/Platform/index.ts
Normal file
5
src/common/Platform/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { PlatformProvider, usePlatform } from './Platform';
|
||||||
|
export {
|
||||||
|
PlatformProvider,
|
||||||
|
usePlatform,
|
||||||
|
};
|
||||||
22
src/common/Platform/useShell.ts
Normal file
22
src/common/Platform/useShell.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
const createId = () => Math.floor(Math.random() * 9999) + 1;
|
||||||
|
|
||||||
|
const useShell = () => {
|
||||||
|
const transport = globalThis?.qt?.webChannelTransport;
|
||||||
|
|
||||||
|
const send = (method: string, ...args: (string | number)[]) => {
|
||||||
|
transport?.send(JSON.stringify({
|
||||||
|
id: createId(),
|
||||||
|
type: 6,
|
||||||
|
object: 'transport',
|
||||||
|
method: 'onEvent',
|
||||||
|
args: [method, ...args],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
active: !!transport,
|
||||||
|
send,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useShell;
|
||||||
|
|
@ -18,6 +18,7 @@ const Multiselect = require('./Multiselect');
|
||||||
const { default: MultiselectMenu } = require('./MultiselectMenu');
|
const { default: MultiselectMenu } = require('./MultiselectMenu');
|
||||||
const { HorizontalNavBar, VerticalNavBar } = require('./NavBar');
|
const { HorizontalNavBar, VerticalNavBar } = require('./NavBar');
|
||||||
const PaginationInput = require('./PaginationInput');
|
const PaginationInput = require('./PaginationInput');
|
||||||
|
const { PlatformProvider, usePlatform } = require('./Platform');
|
||||||
const PlayIconCircleCentered = require('./PlayIconCircleCentered');
|
const PlayIconCircleCentered = require('./PlayIconCircleCentered');
|
||||||
const Popup = require('./Popup');
|
const Popup = require('./Popup');
|
||||||
const SearchBar = require('./SearchBar');
|
const SearchBar = require('./SearchBar');
|
||||||
|
|
@ -45,7 +46,6 @@ const useProfile = require('./useProfile');
|
||||||
const useStreamingServer = require('./useStreamingServer');
|
const useStreamingServer = require('./useStreamingServer');
|
||||||
const useTorrent = require('./useTorrent');
|
const useTorrent = require('./useTorrent');
|
||||||
const useTranslate = require('./useTranslate');
|
const useTranslate = require('./useTranslate');
|
||||||
const platform = require('./platform');
|
|
||||||
const EventModal = require('./EventModal');
|
const EventModal = require('./EventModal');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
@ -68,6 +68,8 @@ module.exports = {
|
||||||
HorizontalNavBar,
|
HorizontalNavBar,
|
||||||
VerticalNavBar,
|
VerticalNavBar,
|
||||||
PaginationInput,
|
PaginationInput,
|
||||||
|
PlatformProvider,
|
||||||
|
usePlatform,
|
||||||
PlayIconCircleCentered,
|
PlayIconCircleCentered,
|
||||||
Popup,
|
Popup,
|
||||||
SearchBar,
|
SearchBar,
|
||||||
|
|
@ -98,6 +100,5 @@ module.exports = {
|
||||||
useStreamingServer,
|
useStreamingServer,
|
||||||
useTorrent,
|
useTorrent,
|
||||||
useTranslate,
|
useTranslate,
|
||||||
platform,
|
|
||||||
EventModal,
|
EventModal,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
// Copyright (C) 2017-2024 Smart code 203358507
|
|
||||||
|
|
||||||
// this detects ipad properly in safari
|
|
||||||
// while bowser does not
|
|
||||||
const iOS = () => {
|
|
||||||
return [
|
|
||||||
'iPad Simulator',
|
|
||||||
'iPhone Simulator',
|
|
||||||
'iPod Simulator',
|
|
||||||
'iPad',
|
|
||||||
'iPhone',
|
|
||||||
'iPod'
|
|
||||||
].includes(navigator.platform)
|
|
||||||
|| (navigator.userAgent.includes('Mac') && 'ontouchend' in document);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Bowser = require('bowser');
|
|
||||||
|
|
||||||
const browser = Bowser.parse(window.navigator?.userAgent || '');
|
|
||||||
|
|
||||||
// Edge case: iPad is included in this function
|
|
||||||
// Keep in mind maxTouchPoints for Vision Pro might change in the future
|
|
||||||
const isVisionProUser = () => {
|
|
||||||
const isMacintosh = navigator.userAgent.includes('Macintosh');
|
|
||||||
const hasFiveTouchPoints = navigator.maxTouchPoints === 5;
|
|
||||||
return isMacintosh && hasFiveTouchPoints;
|
|
||||||
};
|
|
||||||
|
|
||||||
const name = isVisionProUser() ? 'visionos' : (iOS() ? 'ios' : (browser?.os?.name || 'unknown').toLowerCase());
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
name,
|
|
||||||
isMobile: () => {
|
|
||||||
return name === 'ios' || name === 'android';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -5,7 +5,7 @@ const PropTypes = require('prop-types');
|
||||||
const classnames = require('classnames');
|
const classnames = require('classnames');
|
||||||
const { useTranslation } = require('react-i18next');
|
const { useTranslation } = require('react-i18next');
|
||||||
const { default: Icon } = require('@stremio/stremio-icons/react');
|
const { default: Icon } = require('@stremio/stremio-icons/react');
|
||||||
const { AddonDetailsModal, Button, Image, Multiselect, MainNavBars, TextInput, SearchBar, SharePrompt, ModalDialog, useBinaryState, withCoreSuspender } = require('stremio/common');
|
const { AddonDetailsModal, Button, Image, Multiselect, MainNavBars, TextInput, SearchBar, SharePrompt, ModalDialog, usePlatform, useBinaryState, withCoreSuspender } = require('stremio/common');
|
||||||
const Addon = require('./Addon');
|
const Addon = require('./Addon');
|
||||||
const useInstalledAddons = require('./useInstalledAddons');
|
const useInstalledAddons = require('./useInstalledAddons');
|
||||||
const useRemoteAddons = require('./useRemoteAddons');
|
const useRemoteAddons = require('./useRemoteAddons');
|
||||||
|
|
@ -15,6 +15,7 @@ const styles = require('./styles');
|
||||||
|
|
||||||
const Addons = ({ urlParams, queryParams }) => {
|
const Addons = ({ urlParams, queryParams }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const platform = usePlatform();
|
||||||
const installedAddons = useInstalledAddons(urlParams);
|
const installedAddons = useInstalledAddons(urlParams);
|
||||||
const remoteAddons = useRemoteAddons(urlParams);
|
const remoteAddons = useRemoteAddons(urlParams);
|
||||||
const [addonDetailsTransportUrl, setAddonDetailsTransportUrl] = useAddonDetailsTransportUrl(urlParams, queryParams);
|
const [addonDetailsTransportUrl, setAddonDetailsTransportUrl] = useAddonDetailsTransportUrl(urlParams, queryParams);
|
||||||
|
|
@ -59,7 +60,7 @@ const Addons = ({ urlParams, queryParams }) => {
|
||||||
setAddonDetailsTransportUrl(event.dataset.addon.transportUrl);
|
setAddonDetailsTransportUrl(event.dataset.addon.transportUrl);
|
||||||
}, [setAddonDetailsTransportUrl]);
|
}, [setAddonDetailsTransportUrl]);
|
||||||
const onAddonConfigure = React.useCallback((event) => {
|
const onAddonConfigure = React.useCallback((event) => {
|
||||||
window.open(event.dataset.addon.transportUrl.replace('manifest.json', 'configure'));
|
platform.openExternal(event.dataset.addon.transportUrl.replace('manifest.json', 'configure'));
|
||||||
}, []);
|
}, []);
|
||||||
const closeAddonDetails = React.useCallback(() => {
|
const closeAddonDetails = React.useCallback(() => {
|
||||||
setAddonDetailsTransportUrl(null);
|
setAddonDetailsTransportUrl(null);
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,18 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const { useRouteFocused } = require('stremio-router');
|
const { useRouteFocused } = require('stremio-router');
|
||||||
const { ModalDialog } = require('stremio/common');
|
const { ModalDialog, usePlatform } = require('stremio/common');
|
||||||
const CredentialsTextInput = require('../CredentialsTextInput');
|
const CredentialsTextInput = require('../CredentialsTextInput');
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
|
|
||||||
const PasswordResetModal = ({ email, onCloseRequest }) => {
|
const PasswordResetModal = ({ email, onCloseRequest }) => {
|
||||||
const routeFocused = useRouteFocused();
|
const routeFocused = useRouteFocused();
|
||||||
|
const platform = usePlatform();
|
||||||
const [error, setError] = React.useState('');
|
const [error, setError] = React.useState('');
|
||||||
const emailRef = React.useRef(null);
|
const emailRef = React.useRef(null);
|
||||||
const goToPasswordReset = React.useCallback(() => {
|
const goToPasswordReset = React.useCallback(() => {
|
||||||
emailRef.current.value.length > 0 && emailRef.current.validity.valid ?
|
emailRef.current.value.length > 0 && emailRef.current.validity.valid ?
|
||||||
window.open('https://www.strem.io/reset-password/' + emailRef.current.value, '_blank')
|
platform.openExternal('https://www.strem.io/reset-password/' + emailRef.current.value, '_blank')
|
||||||
:
|
:
|
||||||
setError('Invalid email');
|
setError('Invalid email');
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ const PropTypes = require('prop-types');
|
||||||
const classnames = require('classnames');
|
const classnames = require('classnames');
|
||||||
const { default: Icon } = require('@stremio/stremio-icons/react');
|
const { default: Icon } = require('@stremio/stremio-icons/react');
|
||||||
const { t } = require('i18next');
|
const { t } = require('i18next');
|
||||||
const { Button, Image, useProfile, platform, useToast, Popup, useBinaryState } = require('stremio/common');
|
const { Button, Image, useProfile, usePlatform, useToast, Popup, useBinaryState } = require('stremio/common');
|
||||||
const { useServices } = require('stremio/services');
|
const { useServices } = require('stremio/services');
|
||||||
const { useRouteFocused } = require('stremio-router');
|
const { useRouteFocused } = require('stremio-router');
|
||||||
const StreamPlaceholder = require('./StreamPlaceholder');
|
const StreamPlaceholder = require('./StreamPlaceholder');
|
||||||
|
|
@ -14,6 +14,7 @@ const styles = require('./styles');
|
||||||
const Stream = ({ className, videoId, videoReleased, addonName, name, description, thumbnail, progress, deepLinks, ...props }) => {
|
const Stream = ({ className, videoId, videoReleased, addonName, name, description, thumbnail, progress, deepLinks, ...props }) => {
|
||||||
const profile = useProfile();
|
const profile = useProfile();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
const platform = usePlatform();
|
||||||
const { core } = useServices();
|
const { core } = useServices();
|
||||||
const routeFocused = useRouteFocused();
|
const routeFocused = useRouteFocused();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ const React = require('react');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const classnames = require('classnames');
|
const classnames = require('classnames');
|
||||||
const { useTranslation } = require('react-i18next');
|
const { useTranslation } = require('react-i18next');
|
||||||
const { useToast } = require('stremio/common');
|
const { usePlatform, useToast } = require('stremio/common');
|
||||||
const { useServices } = require('stremio/services');
|
const { useServices } = require('stremio/services');
|
||||||
const Option = require('./Option');
|
const Option = require('./Option');
|
||||||
const styles = require('./styles');
|
const styles = require('./styles');
|
||||||
|
|
@ -12,6 +12,7 @@ const styles = require('./styles');
|
||||||
const OptionsMenu = ({ className, stream, playbackDevices }) => {
|
const OptionsMenu = ({ className, stream, playbackDevices }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { core } = useServices();
|
const { core } = useServices();
|
||||||
|
const platform = usePlatform();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const [streamingUrl, downloadUrl] = React.useMemo(() => {
|
const [streamingUrl, downloadUrl] = React.useMemo(() => {
|
||||||
return stream !== null ?
|
return stream !== null ?
|
||||||
|
|
@ -48,7 +49,7 @@ const OptionsMenu = ({ className, stream, playbackDevices }) => {
|
||||||
}, [streamingUrl, downloadUrl]);
|
}, [streamingUrl, downloadUrl]);
|
||||||
const onDownloadVideoButtonClick = React.useCallback(() => {
|
const onDownloadVideoButtonClick = React.useCallback(() => {
|
||||||
if (streamingUrl || downloadUrl) {
|
if (streamingUrl || downloadUrl) {
|
||||||
window.open(streamingUrl || downloadUrl);
|
platform.openExternal(streamingUrl || downloadUrl);
|
||||||
}
|
}
|
||||||
}, [streamingUrl, downloadUrl]);
|
}, [streamingUrl, downloadUrl]);
|
||||||
const onExternalDeviceRequested = React.useCallback((deviceId) => {
|
const onExternalDeviceRequested = React.useCallback((deviceId) => {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ const { useTranslation } = require('react-i18next');
|
||||||
const { default: Icon } = require('@stremio/stremio-icons/react');
|
const { default: Icon } = require('@stremio/stremio-icons/react');
|
||||||
const { useRouteFocused } = require('stremio-router');
|
const { useRouteFocused } = require('stremio-router');
|
||||||
const { useServices } = require('stremio/services');
|
const { useServices } = require('stremio/services');
|
||||||
const { Button, Checkbox, MainNavBars, Multiselect, ColorInput, TextInput, ModalDialog, useProfile, useStreamingServer, useBinaryState, withCoreSuspender, useToast } = require('stremio/common');
|
const { Button, Checkbox, MainNavBars, Multiselect, ColorInput, TextInput, ModalDialog, useProfile, usePlatform, useStreamingServer, useBinaryState, withCoreSuspender, useToast } = require('stremio/common');
|
||||||
const useProfileSettingsInputs = require('./useProfileSettingsInputs');
|
const useProfileSettingsInputs = require('./useProfileSettingsInputs');
|
||||||
const useStreamingServerSettingsInputs = require('./useStreamingServerSettingsInputs');
|
const useStreamingServerSettingsInputs = require('./useStreamingServerSettingsInputs');
|
||||||
const useDataExport = require('./useDataExport');
|
const useDataExport = require('./useDataExport');
|
||||||
|
|
@ -25,6 +25,7 @@ const Settings = () => {
|
||||||
const profile = useProfile();
|
const profile = useProfile();
|
||||||
const [dataExport, loadDataExport] = useDataExport();
|
const [dataExport, loadDataExport] = useDataExport();
|
||||||
const streamingServer = useStreamingServer();
|
const streamingServer = useStreamingServer();
|
||||||
|
const platform = usePlatform();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const {
|
const {
|
||||||
interfaceLanguageSelect,
|
interfaceLanguageSelect,
|
||||||
|
|
@ -90,7 +91,7 @@ const Settings = () => {
|
||||||
}, []);
|
}, []);
|
||||||
const toggleTraktOnClick = React.useCallback(() => {
|
const toggleTraktOnClick = React.useCallback(() => {
|
||||||
if (!isTraktAuthenticated && profile.auth !== null && profile.auth.user !== null && typeof profile.auth.user._id === 'string') {
|
if (!isTraktAuthenticated && profile.auth !== null && profile.auth.user !== null && typeof profile.auth.user._id === 'string') {
|
||||||
window.open(`https://www.strem.io/trakt/auth/${profile.auth.user._id}`);
|
platform.openExternal(`https://www.strem.io/trakt/auth/${profile.auth.user._id}`);
|
||||||
setTraktAuthStarted(true);
|
setTraktAuthStarted(true);
|
||||||
} else {
|
} else {
|
||||||
core.transport.dispatch({
|
core.transport.dispatch({
|
||||||
|
|
@ -102,15 +103,18 @@ const Settings = () => {
|
||||||
}
|
}
|
||||||
}, [isTraktAuthenticated, profile.auth]);
|
}, [isTraktAuthenticated, profile.auth]);
|
||||||
const subscribeCalendarOnClick = React.useCallback(() => {
|
const subscribeCalendarOnClick = React.useCallback(() => {
|
||||||
const url = `webcal://www.strem.io/calendar/${profile.auth.user._id}.ics`;
|
if (!profile.auth) return;
|
||||||
window.open(url);
|
|
||||||
|
const protocol = platform.name === 'ios' ? 'webcal' : 'https';
|
||||||
|
const url = `${protocol}://www.strem.io/calendar/${profile.auth.user._id}.ics`;
|
||||||
|
platform.openExternal(url);
|
||||||
toast.show({
|
toast.show({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
title: 'Calendar has been added to your default caldendar app',
|
title: platform.name === 'ios' ? t('SETTINGS_SUBSCRIBE_CALENDAR_IOS_TOAST') : t('SETTINGS_SUBSCRIBE_CALENDAR_TOAST'),
|
||||||
timeout: 25000
|
timeout: 25000
|
||||||
});
|
});
|
||||||
//Stremio 4 emits not documented event subscribeCalendar
|
// Stremio 4 emits not documented event subscribeCalendar
|
||||||
}, []);
|
}, [profile.auth]);
|
||||||
const exportDataOnClick = React.useCallback(() => {
|
const exportDataOnClick = React.useCallback(() => {
|
||||||
loadDataExport();
|
loadDataExport();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -181,7 +185,7 @@ const Settings = () => {
|
||||||
}, [isTraktAuthenticated, traktAuthStarted]);
|
}, [isTraktAuthenticated, traktAuthStarted]);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (dataExport.exportUrl !== null && typeof dataExport.exportUrl === 'string') {
|
if (dataExport.exportUrl !== null && typeof dataExport.exportUrl === 'string') {
|
||||||
window.open(dataExport.exportUrl);
|
platform.openExternal(dataExport.exportUrl);
|
||||||
}
|
}
|
||||||
}, [dataExport.exportUrl]);
|
}, [dataExport.exportUrl]);
|
||||||
React.useLayoutEffect(() => {
|
React.useLayoutEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { useTranslation } = require('react-i18next');
|
const { useTranslation } = require('react-i18next');
|
||||||
const { useServices } = require('stremio/services');
|
const { useServices } = require('stremio/services');
|
||||||
const { CONSTANTS, interfaceLanguages, languageNames, platform } = require('stremio/common');
|
const { CONSTANTS, usePlatform, interfaceLanguages, languageNames } = require('stremio/common');
|
||||||
|
|
||||||
const useProfileSettingsInputs = (profile) => {
|
const useProfileSettingsInputs = (profile) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { core } = useServices();
|
const { core } = useServices();
|
||||||
|
const platform = usePlatform();
|
||||||
// TODO combine those useMemo in one
|
// TODO combine those useMemo in one
|
||||||
const interfaceLanguageSelect = React.useMemo(() => ({
|
const interfaceLanguageSelect = React.useMemo(() => ({
|
||||||
options: interfaceLanguages.map(({ name, codes }) => ({
|
options: interfaceLanguages.map(({ name, codes }) => ({
|
||||||
|
|
|
||||||
13
src/types/global.d.ts
vendored
Normal file
13
src/types/global.d.ts
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
interface QtTransport {
|
||||||
|
send: (message: string) => void,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Qt {
|
||||||
|
webChannelTransport: QtTransport,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var qt: Qt | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { };
|
||||||
Loading…
Reference in a new issue