Merge branch 'development' of https://github.com/Stremio/stremio-web into feat/player-options-menu

This commit is contained in:
Tim 2022-11-28 18:20:19 +01:00
commit 8a0b0a93e3
11 changed files with 338 additions and 32 deletions

14
package-lock.json generated
View file

@ -12,7 +12,7 @@
"@babel/runtime": "7.16.0", "@babel/runtime": "7.16.0",
"@sentry/browser": "6.13.3", "@sentry/browser": "6.13.3",
"@stremio/stremio-colors": "5.0.1", "@stremio/stremio-colors": "5.0.1",
"@stremio/stremio-core-web": "0.44.6", "@stremio/stremio-core-web": "0.44.7",
"@stremio/stremio-icons": "4.0.0", "@stremio/stremio-icons": "4.0.0",
"@stremio/stremio-video": "0.0.24", "@stremio/stremio-video": "0.0.24",
"a-color-picker": "1.2.1", "a-color-picker": "1.2.1",
@ -2699,9 +2699,9 @@
"integrity": "sha512-Dt3PYmy1DZ473QNs99KYXVWQPHtpIl37VUY0+gCEvvuCqE1fRrZIJtZ9KbysUKonvO7WwdQDztgcW0iGoc1dEA==" "integrity": "sha512-Dt3PYmy1DZ473QNs99KYXVWQPHtpIl37VUY0+gCEvvuCqE1fRrZIJtZ9KbysUKonvO7WwdQDztgcW0iGoc1dEA=="
}, },
"node_modules/@stremio/stremio-core-web": { "node_modules/@stremio/stremio-core-web": {
"version": "0.44.6", "version": "0.44.7",
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.6.tgz", "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.7.tgz",
"integrity": "sha512-Mxc6oRKgTuXU80JEacJIe4TphccZUJkyHTMUZnUx9sotVetGX+EJsyvr+HLKNMDGJHx5xcwGT/BUikdyQR/Lpw==", "integrity": "sha512-hkeYLfL1On4TMBHn87Onrp93aeRuTh4YXMKdDR1Vz5YikPOiPEq/JRoLLmmSSsFEdifs6Egu+A0qiggTttepOA==",
"dependencies": { "dependencies": {
"@babel/runtime": "7.16.0" "@babel/runtime": "7.16.0"
} }
@ -16716,9 +16716,9 @@
"integrity": "sha512-Dt3PYmy1DZ473QNs99KYXVWQPHtpIl37VUY0+gCEvvuCqE1fRrZIJtZ9KbysUKonvO7WwdQDztgcW0iGoc1dEA==" "integrity": "sha512-Dt3PYmy1DZ473QNs99KYXVWQPHtpIl37VUY0+gCEvvuCqE1fRrZIJtZ9KbysUKonvO7WwdQDztgcW0iGoc1dEA=="
}, },
"@stremio/stremio-core-web": { "@stremio/stremio-core-web": {
"version": "0.44.6", "version": "0.44.7",
"resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.6.tgz", "resolved": "https://registry.npmjs.org/@stremio/stremio-core-web/-/stremio-core-web-0.44.7.tgz",
"integrity": "sha512-Mxc6oRKgTuXU80JEacJIe4TphccZUJkyHTMUZnUx9sotVetGX+EJsyvr+HLKNMDGJHx5xcwGT/BUikdyQR/Lpw==", "integrity": "sha512-hkeYLfL1On4TMBHn87Onrp93aeRuTh4YXMKdDR1Vz5YikPOiPEq/JRoLLmmSSsFEdifs6Egu+A0qiggTttepOA==",
"requires": { "requires": {
"@babel/runtime": "7.16.0" "@babel/runtime": "7.16.0"
} }

View file

@ -15,7 +15,7 @@
"@babel/runtime": "7.16.0", "@babel/runtime": "7.16.0",
"@sentry/browser": "6.13.3", "@sentry/browser": "6.13.3",
"@stremio/stremio-colors": "5.0.1", "@stremio/stremio-colors": "5.0.1",
"@stremio/stremio-core-web": "0.44.6", "@stremio/stremio-core-web": "0.44.7",
"@stremio/stremio-icons": "4.0.0", "@stremio/stremio-icons": "4.0.0",
"@stremio/stremio-video": "0.0.24", "@stremio/stremio-video": "0.0.24",
"a-color-picker": "1.2.1", "a-color-picker": "1.2.1",
@ -24,7 +24,6 @@
"classnames": "2.3.1", "classnames": "2.3.1",
"eventemitter3": "4.0.7", "eventemitter3": "4.0.7",
"filter-invalid-dom-props": "2.1.0", "filter-invalid-dom-props": "2.1.0",
"langs": "^2.0.0",
"hat": "0.0.3", "hat": "0.0.3",
"langs": "^2.0.0", "langs": "^2.0.0",
"lodash.debounce": "4.0.8", "lodash.debounce": "4.0.8",

View file

@ -14,5 +14,6 @@
&:global(.disabled) { &:global(.disabled) {
pointer-events: none; pointer-events: none;
opacity: 0.25;
} }
} }

View file

@ -4,6 +4,7 @@ const CHROMECAST_RECEIVER_APP_ID = '1634F54B';
const SUBTITLES_SIZES = [75, 100, 125, 150, 175, 200, 250]; const SUBTITLES_SIZES = [75, 100, 125, 150, 175, 200, 250];
const SUBTITLES_FONTS = ['Roboto', 'Arial', 'Halvetica', 'Times New Roman', 'Verdana', 'Courier', 'Lucida Console', 'sans-serif', 'serif', 'monospace']; const SUBTITLES_FONTS = ['Roboto', 'Arial', 'Halvetica', 'Times New Roman', 'Verdana', 'Courier', 'Lucida Console', 'sans-serif', 'serif', 'monospace'];
const SEEK_TIME_DURATIONS = [5000, 10000, 15000, 20000, 25000, 30000]; const SEEK_TIME_DURATIONS = [5000, 10000, 15000, 20000, 25000, 30000];
const NEXT_VIDEO_POPUP_DURATIONS = [0, 5000, 10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000, 50000];
const CATALOG_PREVIEW_SIZE = 10; const CATALOG_PREVIEW_SIZE = 10;
const CATALOG_PAGE_SIZE = 100; const CATALOG_PAGE_SIZE = 100;
const NONE_EXTRA_VALUE = 'None'; const NONE_EXTRA_VALUE = 'None';
@ -25,12 +26,26 @@ const TYPE_PRIORITIES = {
adult: 1, adult: 1,
other: -Infinity other: -Infinity
}; };
const ICON_FOR_TYPE = new Map([
['movie', 'ic_movies'],
['series', 'ic_series'],
['channel', 'ic_channels'],
['tv', 'ic_tv'],
['book', 'ic_book'],
['game', 'ic_games'],
['music', 'ic_music'],
['adult', 'ic_adult'],
['radio', 'ic_radio'],
['podcast', 'ic_podcast'],
['other', 'ic_movies'],
]);
module.exports = { module.exports = {
CHROMECAST_RECEIVER_APP_ID, CHROMECAST_RECEIVER_APP_ID,
SUBTITLES_SIZES, SUBTITLES_SIZES,
SUBTITLES_FONTS, SUBTITLES_FONTS,
SEEK_TIME_DURATIONS, SEEK_TIME_DURATIONS,
NEXT_VIDEO_POPUP_DURATIONS,
CATALOG_PREVIEW_SIZE, CATALOG_PREVIEW_SIZE,
CATALOG_PAGE_SIZE, CATALOG_PAGE_SIZE,
NONE_EXTRA_VALUE, NONE_EXTRA_VALUE,
@ -39,5 +54,6 @@ module.exports = {
IMDB_LINK_CATEGORY, IMDB_LINK_CATEGORY,
SHARE_LINK_CATEGORY, SHARE_LINK_CATEGORY,
WRITERS_LINK_CATEGORY, WRITERS_LINK_CATEGORY,
TYPE_PRIORITIES TYPE_PRIORITIES,
ICON_FOR_TYPE
}; };

View file

@ -10,22 +10,9 @@ const Image = require('stremio/common/Image');
const Multiselect = require('stremio/common/Multiselect'); const Multiselect = require('stremio/common/Multiselect');
const PlayIconCircleCentered = require('stremio/common/PlayIconCircleCentered'); const PlayIconCircleCentered = require('stremio/common/PlayIconCircleCentered');
const useBinaryState = require('stremio/common/useBinaryState'); const useBinaryState = require('stremio/common/useBinaryState');
const { ICON_FOR_TYPE } = require('stremio/common/CONSTANTS');
const styles = require('./styles'); const styles = require('./styles');
const ICON_FOR_TYPE = new Map([
['movie', 'ic_movies'],
['series', 'ic_series'],
['channel', 'ic_channels'],
['tv', 'ic_tv'],
['book', 'ic_book'],
['game', 'ic_games'],
['music', 'ic_music'],
['adult', 'ic_adult'],
['radio', 'ic_radio'],
['podcast', 'ic_podcast'],
['other', 'ic_movies'],
]);
const MetaItem = React.memo(({ className, type, name, poster, posterShape, playIcon, progress, options, deepLinks, dataset, optionOnSelect, ...props }) => { const MetaItem = React.memo(({ className, type, name, poster, posterShape, playIcon, progress, options, deepLinks, dataset, optionOnSelect, ...props }) => {
const [menuOpen, onMenuOpen, onMenuClose] = useBinaryState(false); const [menuOpen, onMenuOpen, onMenuClose] = useBinaryState(false);
const href = React.useMemo(() => { const href = React.useMemo(() => {

View file

@ -0,0 +1,102 @@
// Copyright (C) 2017-2022 Smart code 203358507
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const Icon = require('@stremio/stremio-icons/dom');
const { Image, Button, CONSTANTS } = require('stremio/common');
const styles = require('./styles');
const NextVideoPopup = ({ className, metaItem, nextVideo, onDismiss, onPlayNextVideoRequested }) => {
const watchNowButtonRef = React.useRef(null);
const [animationEnded, setAnimationEnded] = React.useState(false);
const videoName = React.useMemo(() => {
const title = nextVideo && nextVideo.title || metaItem && metaItem.title;
return nextVideo !== null &&
typeof nextVideo.season === 'number' &&
typeof nextVideo.episode === 'number' ?
`${title} (S${nextVideo.season}E${nextVideo.episode})`
:
title;
}, [metaItem, nextVideo]);
const onAnimationEnd = React.useCallback(() => {
setAnimationEnded(true);
}, []);
const renderPosterFallback = React.useCallback(() => {
return metaItem !== null && typeof metaItem.type === 'string' ?
<Icon
className={styles['placeholder-icon']}
icon={CONSTANTS.ICON_FOR_TYPE.has(metaItem.type) ? CONSTANTS.ICON_FOR_TYPE.get(metaItem.type) : CONSTANTS.ICON_FOR_TYPE.get('other')}
/>
:
null;
}, [metaItem]);
const onDismissButtonClick = React.useCallback(() => {
if (typeof onDismiss === 'function') {
onDismiss();
}
}, [onDismiss]);
const onWatchNowButtonClick = React.useCallback(() => {
if (typeof onPlayNextVideoRequested === 'function') {
onPlayNextVideoRequested();
}
}, [onPlayNextVideoRequested]);
React.useLayoutEffect(() => {
if (animationEnded === true && watchNowButtonRef.current !== null) {
watchNowButtonRef.current.focus();
}
}, [animationEnded]);
return (
<div className={classnames(className, styles['next-video-popup-container'])} onAnimationEnd={onAnimationEnd}>
<div className={styles['poster-container']}>
<Image
className={styles['poster-image']}
src={nextVideo?.thumbnail}
alt={' '}
fallbackSrc={metaItem?.poster}
renderFallback={renderPosterFallback}
/>
</div>
<div className={styles['info-container']}>
<div className={styles['details-container']}>
{
typeof videoName === 'string' ?
<div className={styles['name']}>
{ videoName }
</div>
:
null
}
{
nextVideo !== null && typeof nextVideo.overview === 'string' ?
<div className={styles['description']}>
{ nextVideo.overview }
</div>
:
null
}
</div>
<div className={styles['buttons-container']}>
<Button className={styles['button-container']} onClick={onDismissButtonClick}>
<Icon className={styles['icon']} icon={'ic_x'} />
<div className={styles['label']}>Dismiss</div>
</Button>
<Button ref={watchNowButtonRef} className={classnames(styles['button-container'], styles['play-button'])} onClick={onWatchNowButtonClick}>
<Icon className={styles['icon']} icon={'ic_play'} />
<div className={styles['label']}>Watch Now</div>
</Button>
</div>
</div>
</div>
);
};
NextVideoPopup.propTypes = {
className: PropTypes.string,
metaItem: PropTypes.object,
nextVideo: PropTypes.object,
onDismiss: PropTypes.func,
onPlayNextVideoRequested: PropTypes.func
};
module.exports = NextVideoPopup;

View file

@ -0,0 +1,5 @@
// Copyright (C) 2017-2022 Smart code 203358507
const NextVideoPopup = require('./NextVideoPopup');
module.exports = NextVideoPopup;

View file

@ -0,0 +1,124 @@
// Copyright (C) 2017-2022 Smart code 203358507
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
.next-video-popup-container {
display: flex;
flex-direction: row;
height: 16rem;
width: 40rem;
animation: slide-fade-in 0.5s ease-in;
@keyframes slide-fade-in {
0% {
opacity: 0;
transform: translateX(calc(40rem + 2rem));
}
100% {
opacity: 1;
transform: translateX(0);
}
}
.poster-container {
flex: 1 1 40%;
display: flex;
justify-content: center;
align-items: center;
background-color: @color-background;
.poster-image {
flex: none;
width: 100%;
height: 100%;
object-position: center;
object-fit: cover;
}
.placeholder-icon {
flex: none;
width: 80%;
height: 50%;
fill: @color-background-light3-90;
}
}
.info-container {
flex: 1 1 70%;
display: flex;
flex-direction: column;
.details-container {
flex: auto;
padding: 1.5rem 1.5rem;
.name {
flex: none;
align-self: stretch;
max-height: 2.4em;
font-weight: 600;
margin-bottom: 0.5rem;
color: @color-surface-light5-90;
}
.description {
color: @color-surface-light5-50;
}
}
.buttons-container {
display: flex;
flex-direction: row;
.spacing {
flex: 0 0 50%;
}
.button-container {
flex: 0 0 50%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 3.5rem;
&.play-button {
background-color: @color-accent3;
.icon {
fill: @color-surface-light5-90;
}
.label {
color: @color-surface-light5-90;
}
&:hover, &:focus {
background-color: @color-accent3-light1;
}
}
.icon {
flex: none;
width: 1.4rem;
height: 1.4rem;
margin-right: 1rem;
fill: @color-secondaryvariant1-90;
}
.label {
flex: none;
max-height: 2.4em;
font-size: 1.1rem;
font-weight: 500;
color: @color-secondaryvariant1-90;
}
&:hover, &:focus {
background-color: @color-background-light2;
}
}
}
}
}

View file

@ -11,6 +11,7 @@ const { HorizontalNavBar, Button, useFullscreen, useBinaryState, useToast, useSt
const Icon = require('@stremio/stremio-icons/dom'); const Icon = require('@stremio/stremio-icons/dom');
const BufferingLoader = require('./BufferingLoader'); const BufferingLoader = require('./BufferingLoader');
const ControlBar = require('./ControlBar'); const ControlBar = require('./ControlBar');
const NextVideoPopup = require('./NextVideoPopup');
const InfoMenu = require('./InfoMenu'); const InfoMenu = require('./InfoMenu');
const OptionsMenu = require('./OptionsMenu'); const OptionsMenu = require('./OptionsMenu');
const VideosMenu = require('./VideosMenu'); const VideosMenu = require('./VideosMenu');
@ -45,6 +46,8 @@ const Player = ({ urlParams, queryParams }) => {
const [infoMenuOpen, , closeInfoMenu, toggleInfoMenu] = useBinaryState(false); const [infoMenuOpen, , closeInfoMenu, toggleInfoMenu] = useBinaryState(false);
const [speedMenuOpen, , closeSpeedMenu, toggleSpeedMenu] = useBinaryState(false); const [speedMenuOpen, , closeSpeedMenu, toggleSpeedMenu] = useBinaryState(false);
const [videosMenuOpen, , closeVideosMenu, toggleVideosMenu] = useBinaryState(false); const [videosMenuOpen, , closeVideosMenu, toggleVideosMenu] = useBinaryState(false);
const [nextVideoPopupOpen, openNextVideoPopup, closeNextVideoPopup] = useBinaryState(false);
const nextVideoPopupDismissed = React.useRef(false);
const defaultSubtitlesSelected = React.useRef(false); const defaultSubtitlesSelected = React.useRef(false);
const defaultAudioTrackSelected = React.useRef(false); const defaultAudioTrackSelected = React.useRef(false);
const [error, setError] = React.useState(null); const [error, setError] = React.useState(null);
@ -108,16 +111,11 @@ const Player = ({ urlParams, queryParams }) => {
ended(); ended();
pushToLibrary(); pushToLibrary();
if (player.nextVideo !== null) { if (player.nextVideo !== null) {
window.location.replace( onPlayNextVideoRequested();
typeof player.nextVideo.deepLinks.player === 'string' ?
player.nextVideo.deepLinks.player
:
player.nextVideo.deepLinks.metaDetailsStreams
);
} else { } else {
window.history.back(); window.history.back();
} }
}, [player.libraryItem, player.nextVideo]); }, [player.nextVideo, onPlayNextVideoRequested]);
const onError = React.useCallback((error) => { const onError = React.useCallback((error) => {
console.error('Player', error); console.error('Player', error);
if (error.critical) { if (error.critical) {
@ -190,6 +188,20 @@ const Player = ({ urlParams, queryParams }) => {
const onSubtitlesOffsetChanged = React.useCallback((offset) => { const onSubtitlesOffsetChanged = React.useCallback((offset) => {
updateSettings({ subtitlesOffset: offset }); updateSettings({ subtitlesOffset: offset });
}, [updateSettings]); }, [updateSettings]);
const onDismissNextVideoPopup = React.useCallback(() => {
closeNextVideoPopup();
nextVideoPopupDismissed.current = true;
}, []);
const onPlayNextVideoRequested = React.useCallback(() => {
if (player.nextVideo !== null) {
window.location.replace(
typeof player.nextVideo.deepLinks.player === 'string' ?
player.nextVideo.deepLinks.player
:
player.nextVideo.deepLinks.metaDetailsStreams
);
}
}, [player.nextVideo]);
const onVideoClick = React.useCallback(() => { const onVideoClick = React.useCallback(() => {
if (videoState.paused !== null) { if (videoState.paused !== null) {
if (videoState.paused) { if (videoState.paused) {
@ -333,6 +345,15 @@ const Player = ({ urlParams, queryParams }) => {
pausedChanged(videoState.paused); pausedChanged(videoState.paused);
} }
}, [videoState.paused]); }, [videoState.paused]);
React.useEffect(() => {
if (!!settings.bingeWatching && player.nextVideo !== null && !nextVideoPopupDismissed.current) {
if (videoState.time !== null && videoState.duration !== null && videoState.time < videoState.duration && (videoState.duration - videoState.time) <= settings.nextVideoNotificationDuration) {
openNextVideoPopup();
} else {
closeNextVideoPopup();
}
}
}, [player.nextVideo, videoState.time, videoState.duration]);
React.useEffect(() => { React.useEffect(() => {
if (!defaultSubtitlesSelected.current) { if (!defaultSubtitlesSelected.current) {
const findTrackByLang = (tracks, lang) => tracks.find((track) => track.lang === lang || langs.where('1', track.lang)?.[2] === lang); const findTrackByLang = (tracks, lang) => tracks.find((track) => track.lang === lang || langs.where('1', track.lang)?.[2] === lang);
@ -363,6 +384,7 @@ const Player = ({ urlParams, queryParams }) => {
React.useEffect(() => { React.useEffect(() => {
defaultSubtitlesSelected.current = false; defaultSubtitlesSelected.current = false;
defaultAudioTrackSelected.current = false; defaultAudioTrackSelected.current = false;
nextVideoPopupDismissed.current = false;
}, [videoState.stream]); }, [videoState.stream]);
React.useEffect(() => { React.useEffect(() => {
if ((!Array.isArray(videoState.subtitlesTracks) || videoState.subtitlesTracks.length === 0) && if ((!Array.isArray(videoState.subtitlesTracks) || videoState.subtitlesTracks.length === 0) &&
@ -514,6 +536,7 @@ const Player = ({ urlParams, queryParams }) => {
closeInfoMenu(); closeInfoMenu();
closeSpeedMenu(); closeSpeedMenu();
closeVideosMenu(); closeVideosMenu();
onDismissNextVideoPopup();
break; break;
} }
} }
@ -533,7 +556,7 @@ const Player = ({ urlParams, queryParams }) => {
}; };
}, []); }, []);
return ( return (
<div className={classnames(styles['player-container'], { [styles['immersed']]: immersed && !casting && videoState.paused !== null && !videoState.paused && !subtitlesMenuOpen && !infoMenuOpen && !speedMenuOpen && !videosMenuOpen && !optionsMenuOpen })} <div className={classnames(styles['player-container'], { [styles['immersed']]: immersed && !casting && videoState.paused !== null && !videoState.paused && !subtitlesMenuOpen && !infoMenuOpen && !speedMenuOpen && !videosMenuOpen && !nextVideoPopupOpen && !optionsMenuOpen })}
onMouseDown={onContainerMouseDown} onMouseDown={onContainerMouseDown}
onMouseMove={onContainerMouseMove} onMouseMove={onContainerMouseMove}
onMouseOver={onContainerMouseMove} onMouseOver={onContainerMouseMove}
@ -617,6 +640,18 @@ const Player = ({ urlParams, queryParams }) => {
onMouseMove={onBarMouseMove} onMouseMove={onBarMouseMove}
onMouseOver={onBarMouseMove} onMouseOver={onBarMouseMove}
/> />
{
nextVideoPopupOpen ?
<NextVideoPopup
className={classnames(styles['layer'], styles['menu-layer'])}
metaItem={player.metaItem !== null && player.metaItem.type === 'Ready' ? player.metaItem.content : null}
nextVideo={player.nextVideo}
onDismiss={onDismissNextVideoPopup}
onPlayNextVideoRequested={onPlayNextVideoRequested}
/>
:
null
}
{ {
subtitlesMenuOpen ? subtitlesMenuOpen ?
<SubtitlesMenu <SubtitlesMenu

View file

@ -30,6 +30,7 @@ const Settings = () => {
subtitlesOutlineColorInput, subtitlesOutlineColorInput,
audioLanguageSelect, audioLanguageSelect,
seekTimeDurationSelect, seekTimeDurationSelect,
nextVideoPopupDurationSelect,
bingeWatchingCheckbox, bingeWatchingCheckbox,
playInBackgroundCheckbox, playInBackgroundCheckbox,
playInExternalPlayerCheckbox, playInExternalPlayerCheckbox,
@ -338,6 +339,16 @@ const Settings = () => {
{...bingeWatchingCheckbox} {...bingeWatchingCheckbox}
/> />
</div> </div>
<div className={styles['option-container']}>
<div className={styles['option-name-container']}>
<div className={styles['label']}>Next video popup duration</div>
</div>
<Multiselect
className={classnames(styles['option-input-container'], styles['multiselect-container'])}
disabled={!profile.settings.bingeWatching}
{...nextVideoPopupDurationSelect}
/>
</div>
<div className={styles['option-container']}> <div className={styles['option-container']}>
<div className={styles['option-name-container']}> <div className={styles['option-name-container']}>
<div className={styles['label']}>Play in background</div> <div className={styles['label']}>Play in background</div>

View file

@ -153,6 +153,31 @@ const useProfileSettingsInputs = (profile) => {
}); });
} }
}), [profile.settings]); }), [profile.settings]);
const nextVideoPopupDurationSelect = React.useMemo(() => ({
options: CONSTANTS.NEXT_VIDEO_POPUP_DURATIONS.map((duration) => ({
value: `${duration}`,
label: duration === 0 ? 'Disabled' : `${duration / 1000} seconds`
})),
selected: [`${profile.settings.nextVideoNotificationDuration}`],
renderLabelText: () => {
return profile.settings.nextVideoNotificationDuration === 0 ?
'Disabled'
:
`${profile.settings.nextVideoNotificationDuration / 1000} seconds`;
},
onSelect: (event) => {
core.transport.dispatch({
action: 'Ctx',
args: {
action: 'UpdateSettings',
args: {
...profile.settings,
nextVideoNotificationDuration: parseInt(event.value, 10)
}
}
});
}
}), [profile.settings]);
const bingeWatchingCheckbox = React.useMemo(() => ({ const bingeWatchingCheckbox = React.useMemo(() => ({
checked: profile.settings.bingeWatching, checked: profile.settings.bingeWatching,
onClick: () => { onClick: () => {
@ -237,6 +262,7 @@ const useProfileSettingsInputs = (profile) => {
subtitlesOutlineColorInput, subtitlesOutlineColorInput,
audioLanguageSelect, audioLanguageSelect,
seekTimeDurationSelect, seekTimeDurationSelect,
nextVideoPopupDurationSelect,
bingeWatchingCheckbox, bingeWatchingCheckbox,
playInBackgroundCheckbox, playInBackgroundCheckbox,
playInExternalPlayerCheckbox, playInExternalPlayerCheckbox,