mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 21:27:05 +00:00
Merge branch 'development' into development
This commit is contained in:
commit
e3f2a5a5da
17 changed files with 163 additions and 67 deletions
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
|
@ -24,5 +24,5 @@ jobs:
|
|||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./build
|
||||
destination_dir: ${{ github.ref_name != 'development' && github.ref_name || '' }}
|
||||
destination_dir: ${{ github.ref_name }}
|
||||
allow_empty_commit: true
|
||||
|
|
|
|||
31
package-lock.json
generated
31
package-lock.json
generated
|
|
@ -12,7 +12,7 @@
|
|||
"@babel/runtime": "7.16.0",
|
||||
"@sentry/browser": "6.13.3",
|
||||
"@stremio/stremio-colors": "5.0.1",
|
||||
"@stremio/stremio-core-web": "0.44.18",
|
||||
"@stremio/stremio-core-web": "0.44.20",
|
||||
"@stremio/stremio-icons": "4.0.0",
|
||||
"@stremio/stremio-video": "0.0.24",
|
||||
"a-color-picker": "1.2.1",
|
||||
|
|
@ -37,7 +37,8 @@
|
|||
"react-is": "18.2.0",
|
||||
"spatial-navigation-polyfill": "https://github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
|
||||
"stremio-translations": "https://github.com/Stremio/stremio-translations.git#92675658de92113c5888cf5e57003e468e8b8c9c",
|
||||
"url": "0.11.0"
|
||||
"url": "0.11.0",
|
||||
"use-long-press": "^3.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.16.0",
|
||||
|
|
@ -2702,9 +2703,9 @@
|
|||
"integrity": "sha512-Dt3PYmy1DZ473QNs99KYXVWQPHtpIl37VUY0+gCEvvuCqE1fRrZIJtZ9KbysUKonvO7WwdQDztgcW0iGoc1dEA=="
|
||||
},
|
||||
"node_modules/@stremio/stremio-core-web": {
|
||||
"version": "0.44.18",
|
||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.18.tgz",
|
||||
"integrity": "sha512-g89XSIfLIsvN+FIscvBP9t5ywLP1uhGT9jED97e37ScXKCVedOL9ibnn1DJIeUj8U+ezJdbHFx4zWpnMfJdU2A==",
|
||||
"version": "0.44.20",
|
||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.20.tgz",
|
||||
"integrity": "sha512-dcqs9svqe9iQHDIyIr7ML42H5Oa2GNjIy3Ngp/TSMPd0UIQ/kJ4ZU/qoZk17r/McI20FEzYIdGFdCcif7c/n9g==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.16.0"
|
||||
}
|
||||
|
|
@ -13664,6 +13665,14 @@
|
|||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||
},
|
||||
"node_modules/use-long-press": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/use-long-press/-/use-long-press-3.1.5.tgz",
|
||||
"integrity": "sha512-bnwk2SlvLLpeJPkNYSGkc59q5YNV9V/fLDkSOAF2p7Xt0zw3iYHEmgEGkNYkK7zEIEyRFi5CczKsT7MN99UzVQ==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sidecar": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
|
||||
|
|
@ -16795,9 +16804,9 @@
|
|||
"integrity": "sha512-Dt3PYmy1DZ473QNs99KYXVWQPHtpIl37VUY0+gCEvvuCqE1fRrZIJtZ9KbysUKonvO7WwdQDztgcW0iGoc1dEA=="
|
||||
},
|
||||
"@stremio/stremio-core-web": {
|
||||
"version": "0.44.18",
|
||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.18.tgz",
|
||||
"integrity": "sha512-g89XSIfLIsvN+FIscvBP9t5ywLP1uhGT9jED97e37ScXKCVedOL9ibnn1DJIeUj8U+ezJdbHFx4zWpnMfJdU2A==",
|
||||
"version": "0.44.20",
|
||||
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.20.tgz",
|
||||
"integrity": "sha512-dcqs9svqe9iQHDIyIr7ML42H5Oa2GNjIy3Ngp/TSMPd0UIQ/kJ4ZU/qoZk17r/McI20FEzYIdGFdCcif7c/n9g==",
|
||||
"requires": {
|
||||
"@babel/runtime": "7.16.0"
|
||||
}
|
||||
|
|
@ -25104,6 +25113,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"use-long-press": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/use-long-press/-/use-long-press-3.1.5.tgz",
|
||||
"integrity": "sha512-bnwk2SlvLLpeJPkNYSGkc59q5YNV9V/fLDkSOAF2p7Xt0zw3iYHEmgEGkNYkK7zEIEyRFi5CczKsT7MN99UzVQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"use-sidecar": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
"@babel/runtime": "7.16.0",
|
||||
"@sentry/browser": "6.13.3",
|
||||
"@stremio/stremio-colors": "5.0.1",
|
||||
"@stremio/stremio-core-web": "0.44.18",
|
||||
"@stremio/stremio-core-web": "0.44.20",
|
||||
"@stremio/stremio-icons": "4.0.0",
|
||||
"@stremio/stremio-video": "0.0.24",
|
||||
"a-color-picker": "1.2.1",
|
||||
|
|
@ -40,7 +40,8 @@
|
|||
"react-is": "18.2.0",
|
||||
"spatial-navigation-polyfill": "https://github.com/Stremio/spatial-navigation.git#64871b1422466f5f45d24ebc8bbd315b2ebab6a6",
|
||||
"stremio-translations": "https://github.com/Stremio/stremio-translations.git#92675658de92113c5888cf5e57003e468e8b8c9c",
|
||||
"url": "0.11.0"
|
||||
"url": "0.11.0",
|
||||
"use-long-press": "^3.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.16.0",
|
||||
|
|
|
|||
|
|
@ -6,14 +6,17 @@ const { useTranslation } = require('react-i18next');
|
|||
const { Router } = require('stremio-router');
|
||||
const { Core, Shell, Chromecast, DragAndDrop, KeyboardShortcuts, ServicesProvider } = require('stremio/services');
|
||||
const { NotFound } = require('stremio/routes');
|
||||
const { ToastProvider, CONSTANTS } = require('stremio/common');
|
||||
const { ToastProvider, CONSTANTS, withCoreSuspender } = require('stremio/common');
|
||||
const ServicesToaster = require('./ServicesToaster');
|
||||
const DeepLinkHandler = require('./DeepLinkHandler');
|
||||
const DefaultSettingsHandler = require('./DefaultSettingsHandler');
|
||||
const ErrorDialog = require('./ErrorDialog');
|
||||
const withProtectedRoutes = require('./withProtectedRoutes');
|
||||
const routerViewsConfig = require('./routerViewsConfig');
|
||||
const styles = require('./styles');
|
||||
|
||||
const RouterWithProtectedRoutes = withCoreSuspender(withProtectedRoutes(Router));
|
||||
|
||||
const App = () => {
|
||||
const { i18n } = useTranslation();
|
||||
const onPathNotMatch = React.useCallback(() => {
|
||||
|
|
@ -154,7 +157,7 @@ const App = () => {
|
|||
<ServicesToaster />
|
||||
<DeepLinkHandler />
|
||||
<DefaultSettingsHandler />
|
||||
<Router
|
||||
<RouterWithProtectedRoutes
|
||||
className={styles['router']}
|
||||
viewsConfig={routerViewsConfig}
|
||||
onPathNotMatch={onPathNotMatch}
|
||||
|
|
|
|||
29
src/App/withProtectedRoutes.js
Normal file
29
src/App/withProtectedRoutes.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const React = require('react');
|
||||
const { Intro } = require('stremio/routes');
|
||||
const { useProfile } = require('stremio/common');
|
||||
|
||||
const withProtectedRoutes = (Component) => {
|
||||
return function withProtectedRoutes(props) {
|
||||
const profile = useProfile();
|
||||
const previousAuthRef = React.useRef(profile.auth);
|
||||
React.useEffect(() => {
|
||||
if (previousAuthRef.current !== null && profile.auth === null) {
|
||||
window.location = '#/intro';
|
||||
}
|
||||
previousAuthRef.current = profile.auth;
|
||||
}, [profile]);
|
||||
const onRouteChange = React.useCallback((routeConfig) => {
|
||||
if (profile.auth !== null && routeConfig.component === Intro) {
|
||||
window.location.replace('#/');
|
||||
return true;
|
||||
}
|
||||
}, [profile]);
|
||||
return (
|
||||
<Component {...props} onRouteChange={onRouteChange} />
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = withProtectedRoutes;
|
||||
|
|
@ -4,15 +4,20 @@ const React = require('react');
|
|||
const PropTypes = require('prop-types');
|
||||
const classnames = require('classnames');
|
||||
const styles = require('./styles');
|
||||
const { useLongPress } = require('use-long-press');
|
||||
|
||||
const Button = React.forwardRef(({ className, href, disabled, children, ...props }, ref) => {
|
||||
const Button = React.forwardRef(({ className, href, disabled, children, onLongPress, ...props }, ref) => {
|
||||
const longPress = useLongPress(onLongPress, { detect: 'pointer' });
|
||||
const onKeyDown = React.useCallback((event) => {
|
||||
if (typeof props.onKeyDown === 'function') {
|
||||
props.onKeyDown(event);
|
||||
}
|
||||
|
||||
if (event.key === 'Enter' && !event.nativeEvent.buttonClickPrevented) {
|
||||
event.currentTarget.click();
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
if (!event.nativeEvent.buttonClickPrevented) {
|
||||
event.currentTarget.click();
|
||||
}
|
||||
}
|
||||
}, [props.onKeyDown]);
|
||||
const onMouseDown = React.useCallback((event) => {
|
||||
|
|
@ -36,7 +41,8 @@ const Button = React.forwardRef(({ className, href, disabled, children, ...props
|
|||
className: classnames(className, styles['button-container'], { 'disabled': disabled }),
|
||||
href,
|
||||
onKeyDown,
|
||||
onMouseDown
|
||||
onMouseDown,
|
||||
...longPress()
|
||||
},
|
||||
children
|
||||
);
|
||||
|
|
@ -50,7 +56,8 @@ Button.propTypes = {
|
|||
disabled: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
onKeyDown: PropTypes.func,
|
||||
onMouseDown: PropTypes.func
|
||||
onMouseDown: PropTypes.func,
|
||||
onLongPress: PropTypes.func,
|
||||
};
|
||||
|
||||
module.exports = Button;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ const NavMenuContent = ({ onClick }) => {
|
|||
<div className={styles['email-container']}>
|
||||
<div className={styles['email-label']}>{profile.auth === null ? t('ANONYMOUS_USER') : profile.auth.user.email}</div>
|
||||
</div>
|
||||
<Button className={styles['logout-button-container']} title={profile.auth === null ? `${t('LOG_IN')} / ${t('SIGN_UP')}` : t('LOG_OUT')} href={'#/intro'} onClick={logoutButtonOnClick}>
|
||||
<Button className={styles['logout-button-container']} title={profile.auth === null ? `${t('LOG_IN')} / ${t('SIGN_UP')}` : t('LOG_OUT')} href={profile.auth === null ? '#/intro' : null} onClick={profile.auth !== null ? logoutButtonOnClick : null}>
|
||||
<div className={styles['logout-label']}>{profile.auth === null ? `${t('LOG_IN')} / ${t('SIGN_UP')}` : t('LOG_OUT')}</div>
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -47,16 +47,23 @@ const Popup = ({ open, direction, renderLabel, renderMenu, dataset, onCloseReque
|
|||
onCloseRequest(closeEvent);
|
||||
}
|
||||
break;
|
||||
case 'pointerdown':
|
||||
if (event.target !== document.documentElement && !labelRef.current.contains(event.target)) {
|
||||
onCloseRequest(closeEvent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
if (routeFocused && open) {
|
||||
window.addEventListener('keydown', onCloseEvent);
|
||||
window.addEventListener('mousedown', onCloseEvent);
|
||||
window.addEventListener('pointerdown', onCloseEvent);
|
||||
}
|
||||
return () => {
|
||||
window.removeEventListener('keydown', onCloseEvent);
|
||||
window.removeEventListener('mousedown', onCloseEvent);
|
||||
window.removeEventListener('pointerdown', onCloseEvent);
|
||||
};
|
||||
}, [routeFocused, open, onCloseRequest, dataset]);
|
||||
React.useLayoutEffect(() => {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@
|
|||
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
|
||||
|
||||
.label-container {
|
||||
// IOS specific
|
||||
// prevents showing the default context-menu when long pressing an anchor in safari.
|
||||
-webkit-touch-callout: none !important;
|
||||
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ const Route = require('../Route');
|
|||
const routeConfigForPath = require('./routeConfigForPath');
|
||||
const urlParamsForPath = require('./urlParamsForPath');
|
||||
|
||||
const Router = ({ className, onPathNotMatch, ...props }) => {
|
||||
const Router = ({ className, onPathNotMatch, onRouteChange, ...props }) => {
|
||||
const viewsConfig = React.useMemo(() => props.viewsConfig, []);
|
||||
const [views, setViews] = React.useState(() => {
|
||||
return Array(viewsConfig.length).fill(null);
|
||||
|
|
@ -42,37 +42,40 @@ const Router = ({ className, onPathNotMatch, ...props }) => {
|
|||
const urlParams = urlParamsForPath(routeConfig, typeof pathname === 'string' ? pathname : '');
|
||||
const routeViewIndex = viewsConfig.findIndex((vc) => vc.includes(routeConfig));
|
||||
const routeIndex = viewsConfig[routeViewIndex].findIndex((rc) => rc === routeConfig);
|
||||
setViews((views) => {
|
||||
return views
|
||||
.slice(0, viewsConfig.length)
|
||||
.map((view, index) => {
|
||||
if (index < routeViewIndex) {
|
||||
return view;
|
||||
} else if (index === routeViewIndex) {
|
||||
return {
|
||||
key: `${routeViewIndex}${routeIndex}`,
|
||||
component: routeConfig.component,
|
||||
urlParams: view !== null && isEqual(view.urlParams, urlParams) ?
|
||||
view.urlParams
|
||||
:
|
||||
urlParams,
|
||||
queryParams: view !== null && isEqual(Array.from(view.queryParams.entries()), Array.from(queryParams.entries())) ?
|
||||
view.queryParams
|
||||
:
|
||||
queryParams
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
});
|
||||
const handled = typeof onRouteChange === 'function' && onRouteChange(routeConfig, urlParams, queryParams);
|
||||
if (!handled) {
|
||||
setViews((views) => {
|
||||
return views
|
||||
.slice(0, viewsConfig.length)
|
||||
.map((view, index) => {
|
||||
if (index < routeViewIndex) {
|
||||
return view;
|
||||
} else if (index === routeViewIndex) {
|
||||
return {
|
||||
key: `${routeViewIndex}${routeIndex}`,
|
||||
component: routeConfig.component,
|
||||
urlParams: view !== null && isEqual(view.urlParams, urlParams) ?
|
||||
view.urlParams
|
||||
:
|
||||
urlParams,
|
||||
queryParams: view !== null && isEqual(Array.from(view.queryParams.entries()), Array.from(queryParams.entries())) ?
|
||||
view.queryParams
|
||||
:
|
||||
queryParams
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
window.addEventListener('hashchange', onLocationHashChange);
|
||||
onLocationHashChange();
|
||||
return () => {
|
||||
window.removeEventListener('hashchange', onLocationHashChange);
|
||||
};
|
||||
}, [onPathNotMatch]);
|
||||
}, [onPathNotMatch, onRouteChange]);
|
||||
return (
|
||||
<div className={classnames(className, 'routes-container')}>
|
||||
{
|
||||
|
|
@ -93,6 +96,7 @@ const Router = ({ className, onPathNotMatch, ...props }) => {
|
|||
Router.propTypes = {
|
||||
className: PropTypes.string,
|
||||
onPathNotMatch: PropTypes.func,
|
||||
onRouteChange: PropTypes.func,
|
||||
viewsConfig: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.exact({
|
||||
regexp: PropTypes.instanceOf(RegExp).isRequired,
|
||||
urlParamsNames: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,12 @@ const Discover = ({ urlParams, queryParams }) => {
|
|||
const [inputsModalOpen, openInputsModal, closeInputsModal] = useBinaryState(false);
|
||||
const [addonModalOpen, openAddonModal, closeAddonModal] = useBinaryState(false);
|
||||
const [selectedMetaItemIndex, setSelectedMetaItemIndex] = React.useState(0);
|
||||
const metasContainerRef = React.useRef();
|
||||
React.useEffect(() => {
|
||||
if (discover.catalog?.content.type === 'Loading') {
|
||||
metasContainerRef.current.scrollTop = 0;
|
||||
}
|
||||
}, [discover.catalog]);
|
||||
const selectedMetaItem = React.useMemo(() => {
|
||||
return discover.catalog !== null &&
|
||||
discover.catalog.content.type === 'Ready' &&
|
||||
|
|
@ -122,7 +128,7 @@ const Discover = ({ urlParams, queryParams }) => {
|
|||
</div>
|
||||
:
|
||||
discover.catalog.content.type === 'Loading' ?
|
||||
<div className={classnames(styles['meta-items-container'], 'animation-fade-in')}>
|
||||
<div ref={metasContainerRef} className={classnames(styles['meta-items-container'], 'animation-fade-in')}>
|
||||
{Array(CONSTANTS.CATALOG_PAGE_SIZE).fill(null).map((_, index) => (
|
||||
<div key={index} className={styles['meta-item-placeholder']}>
|
||||
<div className={styles['poster-container']} />
|
||||
|
|
@ -133,7 +139,7 @@ const Discover = ({ urlParams, queryParams }) => {
|
|||
))}
|
||||
</div>
|
||||
:
|
||||
<div className={classnames(styles['meta-items-container'], 'animation-fade-in')} onScroll={onScroll} onFocusCapture={metaItemsOnFocusCapture}>
|
||||
<div ref={metasContainerRef} className={classnames(styles['meta-items-container'], 'animation-fade-in')} onScroll={onScroll} onFocusCapture={metaItemsOnFocusCapture}>
|
||||
{discover.catalog.content.content.map((metaItem, index) => (
|
||||
<MetaItem
|
||||
key={index}
|
||||
|
|
@ -164,6 +170,7 @@ const Discover = ({ urlParams, queryParams }) => {
|
|||
released={selectedMetaItem.released}
|
||||
description={selectedMetaItem.description}
|
||||
deepLinks={selectedMetaItem.deepLinks}
|
||||
links={selectedMetaItem.links}
|
||||
trailerStreams={selectedMetaItem.trailerStreams}
|
||||
inLibrary={selectedMetaItem.inLibrary}
|
||||
toggleInLibrary={selectedMetaItem.inLibrary ? removeFromLibrary : addToLibrary}
|
||||
|
|
|
|||
|
|
@ -144,12 +144,6 @@ const Intro = ({ queryParams }) => {
|
|||
dispatch({ type: 'error', error: 'You must accept the Terms of Service' });
|
||||
return;
|
||||
}
|
||||
core.transport.dispatch({
|
||||
action: 'Ctx',
|
||||
args: {
|
||||
action: 'Logout'
|
||||
}
|
||||
});
|
||||
window.location = '#/';
|
||||
}, [state.termsAccepted]);
|
||||
const signup = React.useCallback(() => {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
const classnames = require('classnames');
|
||||
const { t } = require('i18next');
|
||||
const { useServices } = require('stremio/services');
|
||||
const { useRouteFocused } = require('stremio-router');
|
||||
const Icon = require('@stremio/stremio-icons/dom');
|
||||
|
|
@ -14,27 +15,36 @@ const Video = ({ className, id, title, thumbnail, episode, released, upcoming, w
|
|||
const { core } = useServices();
|
||||
const routeFocused = useRouteFocused();
|
||||
const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false);
|
||||
const popupLabelOnClick = React.useCallback((event) => {
|
||||
if (!event.nativeEvent.togglePopupPrevented && event.nativeEvent.ctrlKey) {
|
||||
event.preventDefault();
|
||||
toggleMenu();
|
||||
const popupLabelOnMouseUp = React.useCallback((event) => {
|
||||
if (!event.nativeEvent.togglePopupPrevented) {
|
||||
if (event.nativeEvent.ctrlKey || event.nativeEvent.button === 2) {
|
||||
event.preventDefault();
|
||||
toggleMenu();
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
const popupLabelOnKeyDown = React.useCallback((event) => {
|
||||
event.nativeEvent.buttonClickPrevented = true;
|
||||
}, []);
|
||||
const popupLabelOnContextMenu = React.useCallback((event) => {
|
||||
if (!event.nativeEvent.togglePopupPrevented && !event.nativeEvent.ctrlKey) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}, [toggleMenu]);
|
||||
const popupLabelOnLongPress = React.useCallback((event) => {
|
||||
if (event.nativeEvent.pointerType !== 'mouse' && !event.nativeEvent.togglePopupPrevented) {
|
||||
toggleMenu();
|
||||
}
|
||||
}, [toggleMenu]);
|
||||
const popupMenuOnPointerDown = React.useCallback((event) => {
|
||||
event.nativeEvent.togglePopupPrevented = true;
|
||||
}, []);
|
||||
const popupMenuOnContextMenu = React.useCallback((event) => {
|
||||
event.nativeEvent.togglePopupPrevented = true;
|
||||
}, []);
|
||||
const popupMenuOnClick = React.useCallback((event) => {
|
||||
event.nativeEvent.togglePopupPrevented = true;
|
||||
}, []);
|
||||
const popupMenuOnKeyDown = React.useCallback((event) => {
|
||||
event.nativeEvent.buttonClickPrevented = true;
|
||||
}, []);
|
||||
const toggleWatchedOnClick = React.useCallback((event) => {
|
||||
event.preventDefault();
|
||||
closeMenu();
|
||||
|
|
@ -132,12 +142,12 @@ const Video = ({ className, id, title, thumbnail, episode, released, upcoming, w
|
|||
}, []);
|
||||
const renderMenu = React.useMemo(() => function renderMenu() {
|
||||
return (
|
||||
<div className={styles['context-menu-content']} onContextMenu={popupMenuOnContextMenu} onClick={popupMenuOnClick}>
|
||||
<div className={styles['context-menu-content']} onPointerDown={popupMenuOnPointerDown} onContextMenu={popupMenuOnContextMenu} onClick={popupMenuOnClick} onKeyDown={popupMenuOnKeyDown}>
|
||||
<Button className={styles['context-menu-option-container']} title={'Watch'}>
|
||||
<div className={styles['context-menu-option-label']}>Watch</div>
|
||||
<div className={styles['context-menu-option-label']}>{t('CTX_WATCH')}</div>
|
||||
</Button>
|
||||
<Button className={styles['context-menu-option-container']} title={watched ? 'Mark as non-watched' : 'Mark as watched'} onClick={toggleWatchedOnClick}>
|
||||
<div className={styles['context-menu-option-label']}>{watched ? 'Mark as non-watched' : 'Mark as watched'}</div>
|
||||
<div className={styles['context-menu-option-label']}>{watched ? t('CTX_MARK_NON_WATCHED') : t('CTX_MARK_WATCHED')}</div>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -161,8 +171,8 @@ const Video = ({ className, id, title, thumbnail, episode, released, upcoming, w
|
|||
scheduled={scheduled}
|
||||
href={href}
|
||||
{...props}
|
||||
onClick={popupLabelOnClick}
|
||||
onKeyDown={popupLabelOnKeyDown}
|
||||
onMouseUp={popupLabelOnMouseUp}
|
||||
onLongPress={popupLabelOnLongPress}
|
||||
onContextMenu={popupLabelOnContextMenu}
|
||||
open={menuOpen}
|
||||
onCloseRequest={closeMenu}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
flex: none;
|
||||
|
||||
.thumbnail {
|
||||
pointer-events: none;
|
||||
display: block;
|
||||
width: 7.5rem;
|
||||
height: 5rem;
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@
|
|||
}
|
||||
|
||||
.background-image {
|
||||
pointer-events: none;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
|||
|
|
@ -585,11 +585,24 @@ const Player = ({ urlParams, queryParams }) => {
|
|||
}
|
||||
}
|
||||
};
|
||||
const onWheel = ({ deltaY }) => {
|
||||
if (deltaY > 0) {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && !optionsMenuOpen && !statisticsMenuOpen && videoState.volume !== null) {
|
||||
onVolumeChangeRequested(videoState.volume - 5);
|
||||
}
|
||||
} else {
|
||||
if (!subtitlesMenuOpen && !infoMenuOpen && !videosMenuOpen && !speedMenuOpen && !optionsMenuOpen && !statisticsMenuOpen && videoState.volume !== null) {
|
||||
onVolumeChangeRequested(videoState.volume + 5);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (routeFocused) {
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
window.addEventListener('wheel', onWheel);
|
||||
}
|
||||
return () => {
|
||||
window.removeEventListener('keydown', onKeyDown);
|
||||
window.removeEventListener('wheel', onWheel);
|
||||
};
|
||||
}, [player.metaItem, player.selected, streamingServer.statistics, settings.seekTimeDuration, routeFocused, subtitlesMenuOpen, infoMenuOpen, videosMenuOpen, speedMenuOpen, optionsMenuOpen, statisticsMenuOpen, videoState.paused, videoState.time, videoState.volume, videoState.audioTracks, videoState.subtitlesTracks, videoState.extraSubtitlesTracks, videoState.playbackSpeed, toggleSubtitlesMenu, toggleInfoMenu, toggleVideosMenu, toggleStatisticsMenu]);
|
||||
React.useLayoutEffect(() => {
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ const Settings = () => {
|
|||
</div>
|
||||
{
|
||||
profile.auth !== null ?
|
||||
<Button className={styles['logout-button-container']} title={ t('LOG_OUT') } href={'#/intro'} onClick={logoutButtonOnClick}>
|
||||
<Button className={styles['logout-button-container']} title={ t('LOG_OUT') } onClick={logoutButtonOnClick}>
|
||||
<div className={styles['logout-label']}>{ t('LOG_OUT') }</div>
|
||||
</Button>
|
||||
:
|
||||
|
|
@ -237,7 +237,7 @@ const Settings = () => {
|
|||
{
|
||||
profile.auth === null ?
|
||||
<div className={styles['option-container']}>
|
||||
<Button className={classnames(styles['option-input-container'], styles['button-container'])} title={`${t('LOG_IN')} / ${t('SIGN_UP')}`} href={'#/intro'} onClick={logoutButtonOnClick}>
|
||||
<Button className={classnames(styles['option-input-container'], styles['button-container'])} title={`${t('LOG_IN')} / ${t('SIGN_UP')}`} href={'#/intro'}>
|
||||
<div className={styles['label']}>{ t('LOG_IN') } / { t('SIGN_UP') }</div>
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue