mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 21:27:05 +00:00
Merge pull request #418 from Stremio/mobile-LongPress
add a long press option to the button module based on timeout.
This commit is contained in:
commit
d44f7047dc
7 changed files with 60 additions and 14 deletions
17
package-lock.json
generated
17
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
@ -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",
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
@ -20,21 +21,31 @@ const Video = ({ className, id, title, thumbnail, episode, released, upcoming, w
|
|||
toggleMenu();
|
||||
}
|
||||
}, []);
|
||||
const popupLabelOnKeyDown = React.useCallback((event) => {
|
||||
event.nativeEvent.buttonClickPrevented = true;
|
||||
}, []);
|
||||
const popupLabelOnContextMenu = React.useCallback((event) => {
|
||||
if (!event.nativeEvent.togglePopupPrevented && !event.nativeEvent.ctrlKey) {
|
||||
event.preventDefault();
|
||||
if (event.nativeEvent.pointerType === 'mouse') {
|
||||
toggleMenu();
|
||||
}
|
||||
}
|
||||
}, [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 +143,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>
|
||||
);
|
||||
|
|
@ -162,7 +173,7 @@ const Video = ({ className, id, title, thumbnail, episode, released, upcoming, w
|
|||
href={href}
|
||||
{...props}
|
||||
onClick={popupLabelOnClick}
|
||||
onKeyDown={popupLabelOnKeyDown}
|
||||
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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue