From 3a73f3b2bd46c15916e99e1b218179760e04bf3d Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 5 Feb 2020 09:50:59 +0200 Subject: [PATCH 01/56] Addons catalog model name changed --- src/routes/Addons/useAddons.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/Addons/useAddons.js b/src/routes/Addons/useAddons.js index d92afbed9..d623597ed 100644 --- a/src/routes/Addons/useAddons.js +++ b/src/routes/Addons/useAddons.js @@ -45,7 +45,7 @@ const onNewAddonsState = (addons) => { return { action: 'Load', args: { - model: 'CatalogFiltered', + model: 'CatalogWithFilters', args: { request: addons.selectable.catalogs[0].request } @@ -61,7 +61,7 @@ const useAddons = (urlParams) => { return { action: 'Load', args: { - model: 'CatalogFiltered', + model: 'CatalogWithFilters', args: { request: { base: urlParams.transportUrl, @@ -81,7 +81,7 @@ const useAddons = (urlParams) => { return { action: 'Load', args: { - model: 'CatalogFiltered', + model: 'CatalogWithFilters', args: { request: addons.selectable.catalogs[0].request } From c19723bb05ff216bb26307e60a764efc5d151174 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 5 Feb 2020 13:46:07 +0200 Subject: [PATCH 02/56] navigation in addons improved --- src/routes/Addons/Addons.js | 74 ++++++++++-------------- src/routes/Addons/useSelectableInputs.js | 33 ++++------- 2 files changed, 42 insertions(+), 65 deletions(-) diff --git a/src/routes/Addons/Addons.js b/src/routes/Addons/Addons.js index e8519ebec..bc3a70b67 100644 --- a/src/routes/Addons/Addons.js +++ b/src/routes/Addons/Addons.js @@ -1,5 +1,6 @@ const React = require('react'); const PropTypes = require('prop-types'); +const { useRouteFocused } = require('stremio-router'); const Icon = require('stremio-icons/dom'); const { AddonDetailsModal, Button, Multiselect, NavBar, TextInput, SharePrompt, ModalDialog, useBinaryState } = require('stremio/common'); const Addon = require('./Addon'); @@ -7,44 +8,41 @@ const useAddons = require('./useAddons'); const useSelectableInputs = require('./useSelectableInputs'); const styles = require('./styles'); -const navigateToAddonDetails = (addonsCatalogRequest, transportUrl) => { - const queryParams = new URLSearchParams([['addon', transportUrl]]); - if (addonsCatalogRequest !== null) { - const transportUrl = encodeURIComponent(addonsCatalogRequest.base); - const catalogId = encodeURIComponent(addonsCatalogRequest.path.id); - const type = encodeURIComponent(addonsCatalogRequest.path.type_name); - window.location.replace(`#/addons/${transportUrl}/${catalogId}/${type}?${queryParams}`); - } else { - window.location.replace(`#/addons?${queryParams}`); - } -}; - -const clearAddonDetails = (addonsCatalogRequest) => { - if (addonsCatalogRequest !== null) { - const transportUrl = encodeURIComponent(addonsCatalogRequest.base); - const catalogId = encodeURIComponent(addonsCatalogRequest.path.id); - const type = encodeURIComponent(addonsCatalogRequest.path.type_name); - window.location.replace(`#/addons/${transportUrl}/${catalogId}/${type}`); - } else { - window.location.replace('#/addons'); - } -}; - const Addons = ({ urlParams, queryParams }) => { + const routeFocused = useRouteFocused(); + const navigate = React.useCallback((args) => { + if (!routeFocused) { + return; + } + + const nextPath = args.hasOwnProperty('request') ? + `/${encodeURIComponent(args.request.base)}/${encodeURIComponent(args.request.path.id)}/${encodeURIComponent(args.request.path.type_name)}` + : + typeof urlParams.transportUrl === 'string' && typeof urlParams.catalogId === 'string' && typeof urlParams.type === 'string' ? + `/${encodeURIComponent(urlParams.transportUrl)}/${encodeURIComponent(urlParams.catalogId)}/${encodeURIComponent(urlParams.type)}` + : + ''; + const nextQueryParams = new URLSearchParams(queryParams); + if (args.hasOwnProperty('detailsTransportUrl')) { + if (args.detailsTransportUrl === null) { + nextQueryParams.delete('addon'); + } else { + nextQueryParams.set('addon', detailsTransportUrl); + } + } + + window.location.replace(`#/addons${nextPath}?${nextQueryParams}`); + }, [routeFocused, urlParams, queryParams]); const addons = useAddons(urlParams); const detailsTransportUrl = queryParams.get('addon'); - const selectInputs = useSelectableInputs(addons); + const selectInputs = useSelectableInputs(addons, navigate); const [addAddonModalOpen, openAddAddonModal, closeAddAddonModal] = useBinaryState(false); const addAddonUrlInputRef = React.useRef(null); const addAddonOnSubmit = React.useCallback(() => { if (addAddonUrlInputRef.current !== null) { - const addonsCatalogRequest = addons.catalog_resource !== null ? - addons.catalog_resource.request - : - null; - navigateToAddonDetails(addonsCatalogRequest, addAddonUrlInputRef.current.value); + navigate({ detailsTransportUrl: addAddonUrlInputRef.current.value }); } - }, [addons]); + }, [navigate]); const addAddonModalButtons = React.useMemo(() => { return [ { @@ -74,19 +72,11 @@ const Addons = ({ urlParams, queryParams }) => { setSharedTransportUrl(event.dataset.transportUrl); }, []); const onAddonToggle = React.useCallback((event) => { - const addonsCatalogRequest = addons.catalog_resource !== null ? - addons.catalog_resource.request - : - null; - navigateToAddonDetails(addonsCatalogRequest, event.dataset.transportUrl); - }, [addons]); + navigate({ detailsTransportUrl: event.dataset.transportUrl }); + }, [navigate]); const closeAddonDetails = React.useCallback(() => { - const addonsCatalogRequest = addons.catalog_resource !== null ? - addons.catalog_resource.request - : - null; - clearAddonDetails(addonsCatalogRequest); - }, [addons]); + navigate({ detailsTransportUrl: null }); + }, [navigate]); React.useLayoutEffect(() => { closeAddAddonModal(); setSearch(''); diff --git a/src/routes/Addons/useSelectableInputs.js b/src/routes/Addons/useSelectableInputs.js index 913556b69..f8a6f34ed 100644 --- a/src/routes/Addons/useSelectableInputs.js +++ b/src/routes/Addons/useSelectableInputs.js @@ -1,20 +1,6 @@ const React = require('react'); -const navigateWithRequest = (request) => { - const transportUrl = encodeURIComponent(request.base); - const catalogId = encodeURIComponent(request.path.id); - const type = encodeURIComponent(request.path.type_name); - window.location.replace(`#/addons/${transportUrl}/${catalogId}/${type}`); -}; - -const equalWithouExtra = (request1, request2) => { - return request1.base === request2.base && - request1.path.resource === request2.path.resource && - request1.path.type_name === request2.path.type_name && - request1.path.id === request2.path.id; -}; - -const mapSelectableInputs = (addons) => { +const mapSelectableInputs = (addons, navigate) => { const catalogSelect = { title: 'Select catalog', options: addons.selectable.catalogs @@ -23,13 +9,14 @@ const mapSelectableInputs = (addons) => { label: name })), selected: addons.selectable.catalogs - .filter(({ request: { path: { id } } }) => { + .filter(({ request }) => { return addons.catalog_resource !== null && - addons.catalog_resource.request.path.id === id; + addons.catalog_resource.request.base === request.base && + addons.catalog_resource.request.path.id === request.path.id; }) .map(({ request }) => JSON.stringify(request)), onSelect: (event) => { - navigateWithRequest(JSON.parse(event.value)); + navigate({ request: JSON.parse(event.value) }); } }; const typeSelect = { @@ -42,20 +29,20 @@ const mapSelectableInputs = (addons) => { selected: addons.selectable.types .filter(({ request }) => { return addons.catalog_resource !== null && - equalWithouExtra(addons.catalog_resource.request, request); + addons.catalog_resource.request.path.type_name === request.path.type_name; }) .map(({ request }) => JSON.stringify(request)), onSelect: (event) => { - navigateWithRequest(JSON.parse(event.value)); + navigate({ request: JSON.parse(event.value) }); } }; return [catalogSelect, typeSelect]; }; -const useSelectableInputs = (addons) => { +const useSelectableInputs = (addons, navigate) => { const selectableInputs = React.useMemo(() => { - return mapSelectableInputs(addons); - }, [addons]); + return mapSelectableInputs(addons, navigate); + }, [addons, navigate]); return selectableInputs; }; From 7dfb92bb0666453ec946c609c65d3f8a7c7a5499 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 5 Feb 2020 13:46:21 +0200 Subject: [PATCH 03/56] useAddons exports only used props --- src/routes/Addons/useAddons.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/routes/Addons/useAddons.js b/src/routes/Addons/useAddons.js index d623597ed..80f0c9b50 100644 --- a/src/routes/Addons/useAddons.js +++ b/src/routes/Addons/useAddons.js @@ -5,22 +5,22 @@ const { useModelState } = require('stremio/common'); const initAddonsState = () => ({ selectable: { types: [], - catalogs: [], - extra: [], - has_next_page: false, - has_prev_page: false + catalogs: [] }, catalog_resource: null }); const mapAddonsStateWithCtx = (addons, ctx) => { - const selectable = addons.selectable; + const selectable = { + types: addons.selectable.types, + catalogs: addons.selectable.catalogs + }; // TODO replace catalog content if resource catalog id is MY const catalog_resource = addons.catalog_resource !== null && addons.catalog_resource.content.type === 'Ready' ? { - ...addons.catalog_resource, + request: addons.catalog_resource.request, content: { - ...addons.catalog_resource.content, + type: addons.catalog_resource.content.type, content: addons.catalog_resource.content.content.map((addon) => ({ transportUrl: addon.transportUrl, installed: ctx.profile.addons.some(({ transportUrl }) => transportUrl === addon.transportUrl), From dbfec0cae03216f573a0b53242a20a3cf635f89e Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 5 Feb 2020 13:46:34 +0200 Subject: [PATCH 04/56] prototype builtins rule disabled in eslint --- .eslintrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintrc b/.eslintrc index 8f16371b4..7bbe9d0e9 100644 --- a/.eslintrc +++ b/.eslintrc @@ -56,6 +56,7 @@ "max": 1 } ], + "no-prototype-builtins": "off", "no-template-curly-in-string": "error", "no-trailing-spaces": "error", "no-useless-concat": "error", From 7540265a4664ee64037703173fcc646f0d081a2d Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 5 Feb 2020 13:47:43 +0200 Subject: [PATCH 05/56] ModalDialog buttons moved outside of the scroll area --- src/common/ModalDialog/ModalDialog.js | 46 +++++++++++++-------------- src/common/ModalDialog/styles.less | 22 +++++++++---- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/common/ModalDialog/ModalDialog.js b/src/common/ModalDialog/ModalDialog.js index 441907f10..d6f995392 100644 --- a/src/common/ModalDialog/ModalDialog.js +++ b/src/common/ModalDialog/ModalDialog.js @@ -70,30 +70,30 @@ const ModalDialog = ({ className, title, buttons, children, dataset, onCloseRequ
{children} - { - Array.isArray(buttons) && buttons.length > 0 ? -
- {buttons.map(({ className, label, icon, props }, index) => ( - - ))} -
- : - null - }
+ { + Array.isArray(buttons) && buttons.length > 0 ? +
+ {buttons.map(({ className, label, icon, props }, index) => ( + + ))} +
+ : + null + } ); diff --git a/src/common/ModalDialog/styles.less b/src/common/ModalDialog/styles.less index c6b7c04a1..d14526017 100644 --- a/src/common/ModalDialog/styles.less +++ b/src/common/ModalDialog/styles.less @@ -60,15 +60,25 @@ .modal-dialog-content { flex: 1; align-self: stretch; - margin: 2rem 1rem; + margin: 2rem 1rem 0; padding: 0 1rem; overflow-y: auto; - .buttons-container { - margin-top: 1rem; - display: flex; - flex-direction: row; - flex-wrap: wrap; + &:last-child { + margin-bottom: 2rem; + } + } + + .buttons-container { + flex: none; + align-self: stretch; + margin: 1rem 2rem 0; + display: flex; + flex-direction: row; + flex-wrap: wrap; + + &:last-child { + margin-bottom: 2rem; } } } From 30c8df60852ba1b1f533356f7251f3b0107b8033 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 5 Feb 2020 13:53:06 +0200 Subject: [PATCH 06/56] Modal autoFocus default value fixed --- src/router/Modal/Modal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/router/Modal/Modal.js b/src/router/Modal/Modal.js index 4b22a133c..043a6599b 100644 --- a/src/router/Modal/Modal.js +++ b/src/router/Modal/Modal.js @@ -8,7 +8,7 @@ const { useModalsContainer } = require('../ModalsContainerContext'); const Modal = ({ className, autoFocus, disabled, children, ...props }) => { const modalsContainer = useModalsContainer(); return ReactDOM.createPortal( - + {children} , modalsContainer From a413f310c44c10ccde7a483e0c6d9bd6d3872bab Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 5 Feb 2020 14:30:24 +0200 Subject: [PATCH 07/56] open addon details fixed --- src/routes/Addons/Addons.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Addons/Addons.js b/src/routes/Addons/Addons.js index bc3a70b67..51a9eda78 100644 --- a/src/routes/Addons/Addons.js +++ b/src/routes/Addons/Addons.js @@ -27,7 +27,7 @@ const Addons = ({ urlParams, queryParams }) => { if (args.detailsTransportUrl === null) { nextQueryParams.delete('addon'); } else { - nextQueryParams.set('addon', detailsTransportUrl); + nextQueryParams.set('addon', args.detailsTransportUrl); } } From b18091bceb0b791cdd01832fb1b8e08621cacab6 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 5 Feb 2020 14:40:02 +0200 Subject: [PATCH 08/56] AddonDetails adapted to changes in core --- .../AddonDetailsModal/AddonDetailsModal.js | 54 ++++++++++--------- .../AddonDetailsModal/useAddonDetails.js | 20 +++---- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/common/AddonDetailsModal/AddonDetailsModal.js b/src/common/AddonDetailsModal/AddonDetailsModal.js index fd20da40f..4e678c4b6 100644 --- a/src/common/AddonDetailsModal/AddonDetailsModal.js +++ b/src/common/AddonDetailsModal/AddonDetailsModal.js @@ -10,7 +10,7 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => { const { core } = useServices(); const addonDetails = useAddonDetails(transportUrl); const modalButtons = React.useMemo(() => { - if (addonDetails.descriptor === null || addonDetails.descriptor.content.type !== 'Ready') { + if (addonDetails.addon === null || addonDetails.addon.content.type !== 'Ready') { return null; } @@ -25,10 +25,10 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => { }; const installOnClick = (event) => { core.dispatch({ - action: 'AddonOp', + action: 'Ctx', args: { - addonOp: 'Install', - args: addonDetails.descriptor.content.content + action: 'InstallAddon', + args: addonDetails.addon.content.content } }); if (typeof onCloseRequest === 'function') { @@ -41,12 +41,10 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => { }; const uninstallOnClick = (event) => { core.dispatch({ - action: 'AddonOp', + action: 'Ctx', args: { - addonOp: 'Uninstall', - args: { - transport_url: addonDetails.descriptor.content.content.transportUrl - } + action: 'UninstallAddon', + args: addonDetails.addon.content.content.transportUrl } }); if (typeof onCloseRequest === 'function') { @@ -65,7 +63,7 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => { onClick: cancelOnClick } }, - addonDetails.descriptor.content.content.installed ? + addonDetails.addon.content.content.installed ? { className: styles['uninstall-button'], label: 'Uninstall', @@ -87,27 +85,33 @@ const AddonDetailsModal = ({ transportUrl, onCloseRequest }) => { return ( { - addonDetails.descriptor === null || addonDetails.descriptor.content.type === 'Loading' ? + addonDetails.selected === null ?
- Loading addon manifest from {transportUrl} + Loading addon manifest
: - addonDetails.descriptor.content.type === 'Err' ? + addonDetails.addon === null || addonDetails.addon.content.type === 'Loading' ?
- Failed to get addon manifest from {transportUrl} + Loading addon manifest from {addonDetails.selected.transport_url}
: - + addonDetails.addon.content.type === 'Err' ? +
+ Failed to get addon manifest from {addonDetails.selected.transport_url}. + {addonDetails.addon.content.content} +
+ : + }
); diff --git a/src/common/AddonDetailsModal/useAddonDetails.js b/src/common/AddonDetailsModal/useAddonDetails.js index 71b9c2a9d..0775a99df 100644 --- a/src/common/AddonDetailsModal/useAddonDetails.js +++ b/src/common/AddonDetailsModal/useAddonDetails.js @@ -2,24 +2,26 @@ const React = require('react'); const useModelState = require('stremio/common/useModelState'); const initAddonDetailsState = () => ({ - descriptor: null + selected: null, + addon: null }); const mapAddonDetailsStateWithCtx = (addonDetails, ctx) => { - const descriptor = addonDetails.descriptor !== null && addonDetails.descriptor.content.type === 'Ready' ? + const selected = addonDetails.selected; + const addon = addonDetails.addon !== null && addonDetails.addon.content.type === 'Ready' ? { - ...addonDetails.descriptor, + transport_url: addonDetails.addon.transport_url, content: { - ...addonDetails.descriptor.content, + type: addonDetails.addon.content.type, content: { - ...addonDetails.descriptor.content.content, - installed: ctx.content.addons.some((addon) => addon.transportUrl === addonDetails.descriptor.transportUrl), + ...addonDetails.addon.content.content, + installed: ctx.profile.addons.some(({ transportUrl }) => transportUrl === addonDetails.addon.transport_url), } } } : - addonDetails.descriptor; - return { descriptor }; + addonDetails.addon; + return { selected, addon }; }; const useAddonDetails = (transportUrl) => { @@ -28,7 +30,7 @@ const useAddonDetails = (transportUrl) => { return { action: 'Load', args: { - load: 'AddonDetails', + model: 'AddonDetails', args: { transport_url: transportUrl } From 94d4da7b1c8571329caf1b4fdf3cc65ec815982a Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 5 Feb 2020 15:37:41 +0200 Subject: [PATCH 09/56] useProfile moved to common --- src/common/NavBar/NavMenu/NavMenu.js | 2 +- src/common/index.js | 4 +++- src/common/{NavBar/NavMenu => }/useProfile.js | 0 3 files changed, 4 insertions(+), 2 deletions(-) rename src/common/{NavBar/NavMenu => }/useProfile.js (100%) diff --git a/src/common/NavBar/NavMenu/NavMenu.js b/src/common/NavBar/NavMenu/NavMenu.js index 40a846d9d..0a791090f 100644 --- a/src/common/NavBar/NavMenu/NavMenu.js +++ b/src/common/NavBar/NavMenu/NavMenu.js @@ -7,7 +7,7 @@ const Button = require('stremio/common/Button'); const Popup = require('stremio/common/Popup'); const useBinaryState = require('stremio/common/useBinaryState'); const useFullscreen = require('stremio/common/useFullscreen'); -const useProfile = require('./useProfile'); +const useProfile = require('stremio/common/useProfile'); const styles = require('./styles'); const NavMenu = ({ className }) => { diff --git a/src/common/index.js b/src/common/index.js index ef266e228..bec73015a 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -27,6 +27,7 @@ const useFullscreen = require('./useFullscreen'); const useInLibrary = require('./useInLibrary'); const useLiveRef = require('./useLiveRef'); const useModelState = require('./useModelState'); +const useProfile = require('./useProfile'); module.exports = { AddonDetailsModal, @@ -58,5 +59,6 @@ module.exports = { useFullscreen, useInLibrary, useLiveRef, - useModelState + useModelState, + useProfile }; diff --git a/src/common/NavBar/NavMenu/useProfile.js b/src/common/useProfile.js similarity index 100% rename from src/common/NavBar/NavMenu/useProfile.js rename to src/common/useProfile.js From 31da9e24acfce9655420041f3a06534071588f82 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Thu, 6 Feb 2020 16:43:32 +0200 Subject: [PATCH 10/56] facebook login implemented --- src/routes/Intro/Intro.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 223294e12..7f6b6836c 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -89,7 +89,7 @@ const Intro = ({ queryParams }) => { const loginWithFacebook = React.useCallback(() => { FB.login((response) => { if (response.status === 'connected') { - fetch('https://www.strem.io/fb-login-with-token/' + encodeURIComponent(response.authResponse.accessToken), { timeout: 10 * 1000 }) + fetch('https://www.strem.io/fb-login-with-token/' + encodeURIComponent(response.authResponse.accessToken), { timeout: 10 * 60 * 1000 }) .then((resp) => { if (resp.status < 200 || resp.status >= 300) { throw new Error('Login failed at getting token from Stremio with status ' + resp.status); @@ -97,22 +97,30 @@ const Intro = ({ queryParams }) => { return resp.json(); } }) - .then(() => { + .then(({ user }) => { + if (!user || typeof user.fbLoginToken !== 'string' || typeof user.email !== 'string') { + throw new Error('Login failed at getting token from Stremio'); + } core.dispatch({ - action: 'UserOp', + action: 'Ctx', args: { - userOp: 'Login', + action: 'Authenticate', args: { - email: state.email, - password: response.authResponse.accessToken + type: 'Login', + email: user.email, + password: user.fbLoginToken } } }); }) - .catch(() => { }); + .catch((err = {}) => { + dispatch({ type: 'error', error: err.message || JSON.stringify(err) }); + }); + } else { + dispatch({ type: 'error', error: 'Login failed at getting token from Facebook' }); } }); - }, [state.email, state.password]); + }, []); const loginWithEmail = React.useCallback(() => { if (typeof state.email !== 'string' || state.email.length === 0) { dispatch({ type: 'error', error: 'Invalid email' }); From e67d994679d74375364e5af423bb373f58e9f8c3 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Thu, 6 Feb 2020 18:13:13 +0200 Subject: [PATCH 11/56] useStreamingServer hook implemented --- src/common/index.js | 4 +++- src/common/useStreamingServer.js | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/common/useStreamingServer.js diff --git a/src/common/index.js b/src/common/index.js index bec73015a..c8923bbe0 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -28,6 +28,7 @@ const useInLibrary = require('./useInLibrary'); const useLiveRef = require('./useLiveRef'); const useModelState = require('./useModelState'); const useProfile = require('./useProfile'); +const useStreamingServer = require('./useStreamingServer'); module.exports = { AddonDetailsModal, @@ -60,5 +61,6 @@ module.exports = { useInLibrary, useLiveRef, useModelState, - useProfile + useProfile, + useStreamingServer }; diff --git a/src/common/useStreamingServer.js b/src/common/useStreamingServer.js new file mode 100644 index 000000000..7d3bd470f --- /dev/null +++ b/src/common/useStreamingServer.js @@ -0,0 +1,23 @@ +const React = require('react'); +const { useServices } = require('stremio/services'); +const useModelState = require('stremio/common/useModelState'); + +const mapStreamingServerState = (ctx) => { + return ctx.streaming_server; +}; + +const useStreamingServer = () => { + const { core } = useServices(); + const initStreamingServer = React.useCallback(() => { + const ctx = core.getState('ctx'); + return mapStreamingServerState(ctx); + }, []); + const streamingServer = useModelState({ + model: 'ctx', + init: initStreamingServer, + map: mapStreamingServerState + }); + return streamingServer; +}; + +module.exports = useStreamingServer; From 2bacd4ec6ad4d75aad160b07f2010d113c60aa5d Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Thu, 6 Feb 2020 18:15:17 +0200 Subject: [PATCH 12/56] render Transparent label instead of nothing for ColorInput --- src/common/ColorInput/ColorInput.js | 15 ++++++++++++++- src/common/ColorInput/styles.less | 27 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/common/ColorInput/styles.less diff --git a/src/common/ColorInput/ColorInput.js b/src/common/ColorInput/ColorInput.js index d64d8938e..312103225 100644 --- a/src/common/ColorInput/ColorInput.js +++ b/src/common/ColorInput/ColorInput.js @@ -1,10 +1,12 @@ const React = require('react'); const PropTypes = require('prop-types'); +const classnames = require('classnames'); const AColorPicker = require('a-color-picker'); const Button = require('stremio/common/Button'); const ModalDialog = require('stremio/common/ModalDialog'); const useBinaryState = require('stremio/common/useBinaryState'); const ColorPicker = require('./ColorPicker'); +const styles = require('./styles'); const parseColor = (value) => { return AColorPicker.parseColor(value, 'hexcss4'); @@ -18,6 +20,9 @@ const ColorInput = ({ className, value, dataset, onChange, ...props }) => { const labelButtonStyle = React.useMemo(() => ({ backgroundColor: value }), [value]); + const isTransparent = React.useMemo(() => { + return parseColor(value).endsWith('00'); + }, [value]); const labelButtonOnClick = React.useCallback((event) => { if (typeof props.onClick === 'function') { props.onClick(event); @@ -61,7 +66,15 @@ const ColorInput = ({ className, value, dataset, onChange, ...props }) => { setTempValue(parseColor(value)); }, [value, modalOpen]); return ( - + } { @@ -339,10 +348,78 @@ const Intro = ({ queryParams }) => {
{state.form === SIGNUP_FORM ? 'LOG IN' : 'SING UP WITH EMAIL'}
+ { + passwordRestModalOpen ? + + : + null + } ); }; +const PasswordResetModal = (props) => { + const routeFocused = useRouteFocused(); + const [error, setError] = React.useState(''); + const [email, setEmail] = React.useState(typeof props.email === 'string' ? props.email : ''); + const modalEmailRef = React.useRef(); + const passwordResetClicked = React.useCallback(() => { + email.length > 0 && modalEmailRef.current.validity.valid ? + window.open('https://www.strem.io/reset-password/' + email, '_blank') + : + setError('Invalid email'); + }, [email]); + const passwordResetModalButtons = React.useMemo(() => { + return [ + { + className: styles['cancel-button'], + label: 'Cancel', + props: { + onClick: props.onCloseRequest + } + }, + { + label: 'Send', + props: { + onClick: passwordResetClicked + } + } + ]; + }, [passwordResetClicked]); + const emailOnChange = React.useCallback((event) => { + setError(''); + setEmail(event.currentTarget.value); + }, [email]); + const emailOnSubmit = React.useCallback(() => { + passwordResetClicked(); + }, [passwordResetClicked]); + React.useEffect(() => { + if (routeFocused) { + modalEmailRef.current.focus(); + } + }, [routeFocused]); + return ( + +
Enter your email
+ + { + error.length > 0 ? +
{error}
+ : + null + } +
+ ); +}; + Intro.propTypes = { queryParams: PropTypes.instanceOf(URLSearchParams) }; diff --git a/src/routes/Intro/styles.less b/src/routes/Intro/styles.less index 872512687..857f0a4eb 100644 --- a/src/routes/Intro/styles.less +++ b/src/routes/Intro/styles.less @@ -110,12 +110,6 @@ margin: 1rem 0; } - .error-message { - margin: 1rem 0; - text-align: center; - color: var(--color-signal1); - } - .submit-button { min-height: 4rem; margin: 1rem 0; @@ -149,4 +143,34 @@ } } } +} + +.error-message { + margin: 1rem 0; + text-align: center; + color: var(--color-signal1); +} + +.password-reset-modal-container { + .message { + margin-bottom: 1rem; + } + + .text-input { + width: 20rem; + padding: 0.5rem; + background-color: var(--color-surface40); + + &:hover, &:focus { + background-color: var(--color-surface60); + } + } + + .error-message { + color: var(--color-signal2); + } + + .cancel-button { + background-color: var(--color-surfacedark); + } } \ No newline at end of file From 52c2e312645379181b78a50ccd635fa179bbf916 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Mon, 10 Feb 2020 17:07:05 +0200 Subject: [PATCH 15/56] PasswordRestModal moved to separate folder --- src/routes/Intro/Intro.js | 65 +--------------- .../PasswordResetModal/PasswordResetModal.js | 74 +++++++++++++++++++ src/routes/Intro/PasswordResetModal/index.js | 3 + .../Intro/PasswordResetModal/styles.less | 25 +++++++ src/routes/Intro/styles.less | 36 ++------- 5 files changed, 110 insertions(+), 93 deletions(-) create mode 100644 src/routes/Intro/PasswordResetModal/PasswordResetModal.js create mode 100644 src/routes/Intro/PasswordResetModal/index.js create mode 100644 src/routes/Intro/PasswordResetModal/styles.less diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 40d0f473c..2e54d5621 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -3,10 +3,11 @@ const PropTypes = require('prop-types'); const classnames = require('classnames'); const Icon = require('stremio-icons/dom'); const { useRouteFocused } = require('stremio-router'); -const { Button, ModalDialog, TextInput, useBinaryState, useCoreEvent } = require('stremio/common'); +const { Button, useBinaryState, useCoreEvent } = require('stremio/common'); const { useServices } = require('stremio/services'); const CredentialsTextInput = require('./CredentialsTextInput'); const ConsentCheckbox = require('./ConsentCheckbox'); +const PasswordResetModal = require('./PasswordResetModal'); const styles = require('./styles'); const SIGNUP_FORM = 'signup'; @@ -358,68 +359,6 @@ const Intro = ({ queryParams }) => { ); }; -const PasswordResetModal = (props) => { - const routeFocused = useRouteFocused(); - const [error, setError] = React.useState(''); - const [email, setEmail] = React.useState(typeof props.email === 'string' ? props.email : ''); - const modalEmailRef = React.useRef(); - const passwordResetClicked = React.useCallback(() => { - email.length > 0 && modalEmailRef.current.validity.valid ? - window.open('https://www.strem.io/reset-password/' + email, '_blank') - : - setError('Invalid email'); - }, [email]); - const passwordResetModalButtons = React.useMemo(() => { - return [ - { - className: styles['cancel-button'], - label: 'Cancel', - props: { - onClick: props.onCloseRequest - } - }, - { - label: 'Send', - props: { - onClick: passwordResetClicked - } - } - ]; - }, [passwordResetClicked]); - const emailOnChange = React.useCallback((event) => { - setError(''); - setEmail(event.currentTarget.value); - }, [email]); - const emailOnSubmit = React.useCallback(() => { - passwordResetClicked(); - }, [passwordResetClicked]); - React.useEffect(() => { - if (routeFocused) { - modalEmailRef.current.focus(); - } - }, [routeFocused]); - return ( - -
Enter your email
- - { - error.length > 0 ? -
{error}
- : - null - } -
- ); -}; - Intro.propTypes = { queryParams: PropTypes.instanceOf(URLSearchParams) }; diff --git a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js new file mode 100644 index 000000000..177fb5ee1 --- /dev/null +++ b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js @@ -0,0 +1,74 @@ +const React = require('react'); +const PropTypes = require('prop-types'); +const { useRouteFocused } = require('stremio-router'); +const { ModalDialog, TextInput } = require('stremio/common'); +const styles = require('./styles'); + +const PasswordResetModal = ({ email, onCloseRequest }) => { + const routeFocused = useRouteFocused(); + const [error, setError] = React.useState(''); + const [modalEmail, setModalEmail] = React.useState(typeof email === 'string' ? email : ''); + const modalEmailRef = React.useRef(); + const passwordResetClicked = React.useCallback(() => { + modalEmail.length > 0 && modalEmailRef.current.validity.valid ? + window.open('https://www.strem.io/reset-password/' + modalEmail, '_blank') + : + setError('Invalid email'); + }, [modalEmail]); + const passwordResetModalButtons = React.useMemo(() => { + return [ + { + className: styles['cancel-button'], + label: 'Cancel', + props: { + onClick: onCloseRequest + } + }, + { + label: 'Send', + props: { + onClick: passwordResetClicked + } + } + ]; + }, [passwordResetClicked]); + const emailOnChange = React.useCallback((event) => { + setError(''); + setModalEmail(event.currentTarget.value); + }, [modalEmail]); + const emailOnSubmit = React.useCallback(() => { + passwordResetClicked(); + }, [passwordResetClicked]); + React.useEffect(() => { + if (routeFocused) { + modalEmailRef.current.focus(); + } + }, [routeFocused]); + return ( + +
Enter your email
+ + { + error.length > 0 ? +
{error}
+ : + null + } +
+ ); +}; + +PasswordResetModal.propTypes = { + email: PropTypes.string, + onCloseRequest: PropTypes.func +}; + +module.exports = PasswordResetModal; diff --git a/src/routes/Intro/PasswordResetModal/index.js b/src/routes/Intro/PasswordResetModal/index.js new file mode 100644 index 000000000..128292036 --- /dev/null +++ b/src/routes/Intro/PasswordResetModal/index.js @@ -0,0 +1,3 @@ +const PasswordResetModal = require('./PasswordResetModal'); + +module.exports = PasswordResetModal; diff --git a/src/routes/Intro/PasswordResetModal/styles.less b/src/routes/Intro/PasswordResetModal/styles.less new file mode 100644 index 000000000..219a82b3f --- /dev/null +++ b/src/routes/Intro/PasswordResetModal/styles.less @@ -0,0 +1,25 @@ +.password-reset-modal-container { + .message { + margin-bottom: 1rem; + } + + .text-input { + width: 20rem; + padding: 0.5rem; + background-color: var(--color-surface40); + + &:hover, &:focus { + background-color: var(--color-surface60); + } + } + + .error-message { + margin: 1rem 0; + text-align: center; + color: var(--color-signal2); + } + + .cancel-button { + background-color: var(--color-surfacedark); + } +} \ No newline at end of file diff --git a/src/routes/Intro/styles.less b/src/routes/Intro/styles.less index 857f0a4eb..872512687 100644 --- a/src/routes/Intro/styles.less +++ b/src/routes/Intro/styles.less @@ -110,6 +110,12 @@ margin: 1rem 0; } + .error-message { + margin: 1rem 0; + text-align: center; + color: var(--color-signal1); + } + .submit-button { min-height: 4rem; margin: 1rem 0; @@ -143,34 +149,4 @@ } } } -} - -.error-message { - margin: 1rem 0; - text-align: center; - color: var(--color-signal1); -} - -.password-reset-modal-container { - .message { - margin-bottom: 1rem; - } - - .text-input { - width: 20rem; - padding: 0.5rem; - background-color: var(--color-surface40); - - &:hover, &:focus { - background-color: var(--color-surface60); - } - } - - .error-message { - color: var(--color-signal2); - } - - .cancel-button { - background-color: var(--color-surfacedark); - } } \ No newline at end of file From 0284ec301dd70ece8c9e45edfab53ba385497516 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Mon, 10 Feb 2020 17:10:03 +0200 Subject: [PATCH 16/56] unnecessary dependency removed --- src/routes/Intro/PasswordResetModal/PasswordResetModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js index 177fb5ee1..e5275f3a1 100644 --- a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js +++ b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js @@ -35,7 +35,7 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { const emailOnChange = React.useCallback((event) => { setError(''); setModalEmail(event.currentTarget.value); - }, [modalEmail]); + }, []); const emailOnSubmit = React.useCallback(() => { passwordResetClicked(); }, [passwordResetClicked]); From 515f362cc119512a4581c88acd61b45b9c096260 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Mon, 10 Feb 2020 17:13:01 +0200 Subject: [PATCH 17/56] onCloseRequest dependency added --- src/routes/Intro/PasswordResetModal/PasswordResetModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js index e5275f3a1..db85f286f 100644 --- a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js +++ b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js @@ -31,7 +31,7 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { } } ]; - }, [passwordResetClicked]); + }, [onCloseRequest, passwordResetClicked]); const emailOnChange = React.useCallback((event) => { setError(''); setModalEmail(event.currentTarget.value); From 2800eb088f13f0af4964a7dcbb93ba8e59333384 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Mon, 10 Feb 2020 17:15:53 +0200 Subject: [PATCH 18/56] passwordResetClicked renamed to passwordRestOnClick --- .../Intro/PasswordResetModal/PasswordResetModal.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js index db85f286f..7a6f7f938 100644 --- a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js +++ b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js @@ -9,7 +9,7 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { const [error, setError] = React.useState(''); const [modalEmail, setModalEmail] = React.useState(typeof email === 'string' ? email : ''); const modalEmailRef = React.useRef(); - const passwordResetClicked = React.useCallback(() => { + const passwordResetOnClick = React.useCallback(() => { modalEmail.length > 0 && modalEmailRef.current.validity.valid ? window.open('https://www.strem.io/reset-password/' + modalEmail, '_blank') : @@ -27,18 +27,18 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { { label: 'Send', props: { - onClick: passwordResetClicked + onClick: passwordResetOnClick } } ]; - }, [onCloseRequest, passwordResetClicked]); + }, [onCloseRequest, passwordResetOnClick]); const emailOnChange = React.useCallback((event) => { setError(''); setModalEmail(event.currentTarget.value); }, []); const emailOnSubmit = React.useCallback(() => { - passwordResetClicked(); - }, [passwordResetClicked]); + passwordResetOnClick(); + }, [passwordResetOnClick]); React.useEffect(() => { if (routeFocused) { modalEmailRef.current.focus(); From 2d609fd8bdf7efddc05509c345f2accbaee52ba7 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Mon, 10 Feb 2020 17:31:14 +0200 Subject: [PATCH 19/56] modalEmailRef initial state added --- src/routes/Intro/PasswordResetModal/PasswordResetModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js index 7a6f7f938..5c9b08dce 100644 --- a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js +++ b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js @@ -8,7 +8,7 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { const routeFocused = useRouteFocused(); const [error, setError] = React.useState(''); const [modalEmail, setModalEmail] = React.useState(typeof email === 'string' ? email : ''); - const modalEmailRef = React.useRef(); + const modalEmailRef = React.useRef(null); const passwordResetOnClick = React.useCallback(() => { modalEmail.length > 0 && modalEmailRef.current.validity.valid ? window.open('https://www.strem.io/reset-password/' + modalEmail, '_blank') From 9169f177de48f66a86efb91f44d8755b51e85abe Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 11:08:57 +0200 Subject: [PATCH 20/56] Settings ui reimplemented and adapted to changes in core --- .../Settings/SectionsList/SectionsList.js | 287 --------------- src/routes/Settings/SectionsList/index.js | 3 - src/routes/Settings/SectionsList/styles.less | 193 ---------- .../SectionsSelector/SectionsSelector.js | 28 -- src/routes/Settings/SectionsSelector/index.js | 3 - .../Settings/SectionsSelector/styles.less | 26 -- src/routes/Settings/Settings.js | 335 +++++++++++++++--- src/routes/Settings/constants.js | 28 -- src/routes/Settings/styles.less | 287 ++++++++++++++- .../Settings/useProfileSettingsInputs.js | 103 ++++++ src/routes/Settings/useSettings.js | 68 ---- .../useStreamingServerSettingsInputs.js | 108 ++++++ 12 files changed, 768 insertions(+), 701 deletions(-) delete mode 100644 src/routes/Settings/SectionsList/SectionsList.js delete mode 100644 src/routes/Settings/SectionsList/index.js delete mode 100644 src/routes/Settings/SectionsList/styles.less delete mode 100644 src/routes/Settings/SectionsSelector/SectionsSelector.js delete mode 100644 src/routes/Settings/SectionsSelector/index.js delete mode 100644 src/routes/Settings/SectionsSelector/styles.less delete mode 100644 src/routes/Settings/constants.js create mode 100644 src/routes/Settings/useProfileSettingsInputs.js delete mode 100644 src/routes/Settings/useSettings.js create mode 100644 src/routes/Settings/useStreamingServerSettingsInputs.js diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js deleted file mode 100644 index 8ff5fe581..000000000 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ /dev/null @@ -1,287 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); -const { Button, Multiselect, Checkbox, ColorInput } = require('stremio/common'); -const Icon = require('stremio-icons/dom'); -const classnames = require('classnames'); -const styles = require('./styles'); - -const SectionsList = React.forwardRef(({ className, sections, preferences, onPreferenceChanged, onScroll }, ref) => { - const toggleCheckbox = (id) => { - onPreferenceChanged(id, preferences[id] === 'true' ? 'false' : 'true'); - }; - - const colorChanged = React.useCallback((event) => { - const id = event.dataset.id; - const color = event.value; - onPreferenceChanged(id, color); - }, [onPreferenceChanged]); - - const updateDropdown = React.useCallback((event) => { - const name = event.dataset.name; - const value = event.reactEvent.currentTarget.dataset.value; - onPreferenceChanged(name, value); - }, [onPreferenceChanged]); - - const updateStreamingDropdown = React.useCallback((event) => { - const name = event.dataset.name; - const value = event.reactEvent.currentTarget.dataset.value; - const newPrefs = { ...preferences.streaming, [name]: value }; - onPreferenceChanged('streaming', newPrefs); - }, [onPreferenceChanged, preferences.streaming]); - - const checkUser = React.useCallback((event) => { - if (!preferences.user) { - // Here in Stremio 4 we show a toast with a message, asking the anonymous user to log in/register - console.log('No user found'); - event.preventDefault(); - } - }, []); - - // Determines whether the link should be opened in new window or in the current one. - const getTargetFor = url => ['//', 'http://', 'https://', 'file://', 'ftp://', 'mailto:', 'magnet:'] - .some(scheme => url.startsWith(scheme)) ? '_blank' : '_self' - - // TODO: If we get the user data after initialization, these should be wrapped in React.useState and set by React.useEffect - const changePasswordUrl = preferences.user && 'https://www.strem.io/reset-password/' + preferences.user.email; - const webCalUrl = preferences.user && 'webcal://www.strem.io/calendar/' + preferences.user._id + '.ics'; - - const formatBytes = inBytes => { - if (inBytes === '0') return 'no caching'; - if (inBytes === 'Infinity') return '∞'; - - const bytes = parseInt(inBytes, 10); - - const kilo = 1024; - const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - - const power = Math.floor(Math.log(bytes) / Math.log(kilo)); - - // More than 1024 yotta bytes - if (power >= sizes.length) { - power = sizes.length - 1; - } - return parseFloat((bytes / Math.pow(kilo, power)).toFixed(2)) + ' ' + sizes[power]; - } - const cacheSizes = ['0', '2147483648', '5368709120', '10737418240', 'Infinity']; - const mkCacheSizeOptions = sizes => sizes.map(size => ({ - label: formatBytes(size), // TODO: translation - value: size.toString(), - })) - const supportedProfiles = ['default', 'soft', 'fast']; - const mkProfiles = profiles => profiles.map(profile => ({ - label: profile[0].toUpperCase() + profile.slice(1).toLowerCase(), // TODO: translation - value: profile, - })) - const [cachingOptions, setCachingOptions] = React.useState(mkProfiles(supportedProfiles)); - const [streamingProfiles, setStreamingProfiles] = React.useState(mkProfiles(supportedProfiles)); - React.useEffect(() => { - if (!preferences.streaming || typeof preferences.streaming.cacheSize === 'undefined') return; - setCachingOptions(mkCacheSizeOptions([...new Set(cacheSizes.concat(preferences.streaming.cacheSize))])); - }, [preferences.streaming && preferences.streaming.cacheSize]); - React.useEffect(() => { - if (preferences.streaming && preferences.streaming.profile && !supportedProfiles.includes(preferences.streaming.profile)) { - setStreamingProfiles(mkProfiles(supportedProfiles.concat(preferences.streaming.profile))); - } - }, [preferences.streaming && preferences.streaming.profile]); - - const sectionsElements = sections.map((section) => -
-
{section.id}
- {(section.inputs || []) - .map((input) => { - if (input.type === 'user') { - return ( - -
- { - !preferences.user - ? -
- : -
- } -
{!preferences.user ? 'Anonymous user' : preferences.user.email}
-
-
- -
-
- -
-
-
-
{'Import options'}
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
{'Trakt Scrobbling'}
- -
- - ); - } else if (input.type === 'streaming') { - return ( - preferences.streaming_loaded - ? - - { - // The streaming server settings are shown only if server is available - preferences.streaming_error - ? - null - : - -
-
Caching
- -
-
-
Torrent Profile
- -
-
- } - {/* From here there is only presentation */} -
-
Streaming server URL: {preferences.server_url}
-
-
-
- -
{'Streaming server is ' + (preferences.streaming_error ? 'not ' : '') + 'available.'}{preferences.streaming_error && ' Reason: ' + preferences.streaming_error}
-
-
-
- : -
-
Loading streaming settgins...
-
- ); - } else if (input.type === 'select') { - return ( -
- {input.header ?
{input.header}
: null} - -
- ); - } else if (input.type === 'link') { - return ( -
- {input.header ?
{input.header}
: null} - -
- ); - } else if (input.type === 'button') { - return ( -
- {input.header ?
{input.header}
: null} - -
- ); - } else if (input.type === 'checkbox') { - return ( -
- {input.header ?
{input.header}
: null} - -
{input.label}
-
-
- ); - } else if (input.type === 'static-text') { - return ( -
- {input.header ?
{input.header}
: null} -
- {input.icon ? : null} -
{input.label}
-
-
- ); - } else if (input.type === 'color') { - return ( -
- {input.header ?
{input.header}
: null} - -
- ); - } else if (input.type === 'info') { - return ( -
-
{input.header} {preferences[input.id]}
-
- ); - } - })} -
- ); - - return ( -
- {sectionsElements} -
- ); -}); - -SectionsList.propTypes = { - className: PropTypes.string, - sections: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.string.isRequired, - ref: PropTypes.shape({ - current: PropTypes.object, - }).isRequired, - inputs: PropTypes.arrayOf(PropTypes.shape({ - type: PropTypes.string.isRequired, - id: PropTypes.string, - header: PropTypes.string, - label: PropTypes.string, - icon: PropTypes.string, - href: PropTypes.string, - options: PropTypes.arrayOf(PropTypes.shape({ - label: PropTypes.string.isRequired, - value: PropTypes.string.isRequired, - })), - })), - })), - preferences: PropTypes.object, - onPreferenceChanged: PropTypes.func.isRequired, - onScroll: PropTypes.func.isRequired, -}; - -module.exports = SectionsList; \ No newline at end of file diff --git a/src/routes/Settings/SectionsList/index.js b/src/routes/Settings/SectionsList/index.js deleted file mode 100644 index ff7a9b4a0..000000000 --- a/src/routes/Settings/SectionsList/index.js +++ /dev/null @@ -1,3 +0,0 @@ -const SectionsList = require('./SectionsList'); - -module.exports = SectionsList; \ No newline at end of file diff --git a/src/routes/Settings/SectionsList/styles.less b/src/routes/Settings/SectionsList/styles.less deleted file mode 100644 index 85e687d99..000000000 --- a/src/routes/Settings/SectionsList/styles.less +++ /dev/null @@ -1,193 +0,0 @@ -:import('~stremio/common/Checkbox/styles.less') { - checkbox-icon: icon; -} -:import('~stremio/common/Multiselect/styles.less') { - menu-container: menu-container; -} - -.menu-container { - max-height: 30rem; - overflow-y: auto; -} - -.section { - padding: 4rem 0; - margin: 0 2rem; - width: var(--input-width); - overflow: visible; - - .section-header { - margin: 1.5rem 0; - font-size: 2rem; - color: var(--color-surfacelighter); - } - - .input-container { - margin: 2rem 0; - display: flex; - flex-direction: column; - overflow: visible; - - .input-header { - margin-bottom: 0.5rem; - color: var(--color-surfacelighter); - } - - .checkbox-icon { - flex: none; - width: 1.2rem; - height: 1.2rem; - fill: var(--color-surfacelight); - } - - &.user-container { - flex-direction: row; - align-items: center; - - .avatar { - margin: 0 1rem; - width: 4.2rem; - height: 4.2rem; - border-radius: 50%; - border: var(--focusable-border-size) solid var(--color-primary); - background-size: cover; - background-position: center; - background-repeat: no-repeat; - } - - .email { - color: var(--color-surfacelighter); - } - } - - &.select-container { - .dropdown { - height: 3rem; - } - } - - &.link-container { - margin: 0; - .link { - padding: .75rem 0; - display: block; - color: var(--color-secondarylight); - - &:focus { - color: var(--color-surface); - } - - &:hover { - color: var(--color-surfacelighter); - } - } - } - - &.button-container { - .button { - padding: 0.7rem; - min-height: calc(var(--input-width) * 0.09); - display: flex; - align-items: center; - justify-content: center; - background-color: var(--color-primary); - cursor: pointer; - - .icon { - width: 1.4rem; - height: 100%; - margin-right: 0.5rem; - fill: var(--color-surfacelighter); - } - - .label { - color: var(--color-surfacelighter); - } - - &:focus { - border-color: var(--color-surfacelighter); - } - - &:hover { - border-color: transparent; - background-color: var(--color-primarylight); - } - } - } - - &.checkbox-container { - .checkbox { - display: flex; - flex-direction: row; - align-items: center; - cursor: pointer; - - .label { - width: 100%; - margin-left: 0.5rem; - color: var(--color-surfacelight); - } - - &:focus, - &:hover { - .checkbox-icon { - fill: var(--color-surfacelighter); - } - - .label { - color: var(--color-surfacelighter); - } - } - } - } - - &.text-container { - margin: 0; - .text { - display: flex; - flex-direction: row; - align-items: center; - padding: .75rem 0; - - .icon { - margin-right: 0.5rem; - width: 1rem; - height: 1rem; - fill: var(--color-signal5); - } - - .x-icon { - margin-right: 0.5rem; - width: 1rem; - height: 1rem; - fill: var(--color-signal2); - } - - .label { - width: 100%; - color: var(--color-surfacelighter); - } - } - } - - &.color-container { - .color-picker { - box-shadow: inset 0px 0px .2rem 0px var(--color-surfacelighter20); - height: calc(var(--input-width) * 0.08); - cursor: pointer; - - &:focus { - border-color: var(--color-surfacelighter); - } - - &:hover { - border-color: transparent; - } - } - } - } - - >:last-child { - margin-bottom: 0; - } -} \ No newline at end of file diff --git a/src/routes/Settings/SectionsSelector/SectionsSelector.js b/src/routes/Settings/SectionsSelector/SectionsSelector.js deleted file mode 100644 index a1f08dd12..000000000 --- a/src/routes/Settings/SectionsSelector/SectionsSelector.js +++ /dev/null @@ -1,28 +0,0 @@ -const React = require('react'); -const PropTypes = require('prop-types'); -const { Button } = require('stremio/common'); -const classnames = require('classnames'); -const styles = require('./styles'); - -const SectionsSelector = ({ className, sections, selectedSectionId, onSelectedSection }) => { - return ( -
- {sections.map((section) => - - )} -
- ); -}; - -SectionsSelector.propTypes = { - className: PropTypes.string, - sections: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.string.isRequired, - })), - selectedSectionId: PropTypes.string.isRequired, - onSelectedSection: PropTypes.func.isRequired, -}; - -module.exports = SectionsSelector; diff --git a/src/routes/Settings/SectionsSelector/index.js b/src/routes/Settings/SectionsSelector/index.js deleted file mode 100644 index d873092a7..000000000 --- a/src/routes/Settings/SectionsSelector/index.js +++ /dev/null @@ -1,3 +0,0 @@ -const SectionsSelector = require('./SectionsSelector'); - -module.exports = SectionsSelector; diff --git a/src/routes/Settings/SectionsSelector/styles.less b/src/routes/Settings/SectionsSelector/styles.less deleted file mode 100644 index f4a15c2a8..000000000 --- a/src/routes/Settings/SectionsSelector/styles.less +++ /dev/null @@ -1,26 +0,0 @@ -.section-label { - padding: 1rem; - font-size: 1.1rem; - border: calc(var(--focusable-border-size) * 0.5) solid transparent; - color: var(--color-surfacelight); - cursor: pointer; - - &.selected { - color: var(--color-surfacelighter); - background-color: var(--color-background); - - &:hover { - background-color: var(--color-background); - } - } - - &:focus { - border-color: var(--color-surfacelighter); - } - - &:hover { - color: var(--color-surfacelighter); - background-color: var(--color-surface20); - border-color: transparent; - } -} \ No newline at end of file diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index a8d216b20..149fc69c2 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -1,65 +1,300 @@ const React = require('react'); -const { NavBar } = require('stremio/common'); +const classnames = require('classnames'); +const Icon = require('stremio-icons/dom'); +const { useServices } = require('stremio/services'); +const { Button, Checkbox, NavBar, Multiselect, ColorInput, useProfile, useStreamingServer } = require('stremio/common'); +const useProfileSettingsInputs = require('./useProfileSettingsInputs'); +const useStreamingServerSettingsInputs = require('./useStreamingServerSettingsInputs'); const styles = require('./styles'); -const SectionsSelector = require('./SectionsSelector'); -const SectionsList = require('./SectionsList'); -const { settingsSections } = require('./constants'); -const useSettings = require('./useSettings'); + +const GENERAL_SECTION = 'general'; +const PLAYER_SECTION = 'player'; +const STREAMING_SECTION = 'streaming'; const Settings = () => { - const [preferences, setPreferences] = useSettings(); - const [dynamicSections, setDynamicSections] = React.useState(settingsSections); - // TODO: The Streaming section should be handled separately - const sections = React.useMemo(() => Object.keys(dynamicSections) - .map((section) => ({ - id: section, - inputs: dynamicSections[section], - ref: React.createRef() - })), [dynamicSections]); - - const [selectedSectionId, setSelectedSectionId] = React.useState(sections[0].id); - const scrollContainerRef = React.useRef(null); - - const updatePreference = (option, value) => { - setPreferences({ ...preferences, [option]: value }); - } - - const changeSection = React.useCallback((event) => { - const currentSectionId = event.currentTarget.dataset.section; - const section = sections.find((section) => section.id === currentSectionId); - //setSelectedSectionId(currentSectionId); - scrollContainerRef.current.scrollTo({ - top: section.ref.current.offsetTop, - behavior: 'smooth' - }); - }, [sections]); - - const sectionListOnScorll = React.useCallback((event) => { - const scrollContainer = event.currentTarget; - if (scrollContainer.scrollTop + scrollContainer.clientHeight === scrollContainer.scrollHeight) { - setSelectedSectionId(sections[sections.length - 1].id); - } else { - for (let i = sections.length - 1; i >= 0; i--) { - if (sections[i].ref.current.offsetTop <= scrollContainer.scrollTop) { - setSelectedSectionId(sections[i].id); - break; - } + const { core } = useServices(); + const profile = useProfile(); + const streaminServer = useStreamingServer(); + const { + interfaceLanguageSelect, + subtitlesLanguageSelect, + subtitlesSizeSelect, + subtitlesTextColorInput, + subtitlesBackgroundColorInput, + subtitlesOutlineColorInput, + bingeWatchingCheckbox, + playInBackgroundCheckbox, + playInExternalPlayerCheckbox + } = useProfileSettingsInputs(); + const { + cacheSizeSelect, + torrentProfileSelect + } = useStreamingServerSettingsInputs(); + const [section, setSection] = React.useState(GENERAL_SECTION); + const sectionsContainerRef = React.useRef(null); + const sectionsContainerOnScorll = React.useCallback((event) => { + }, []); + const sideMenuButtonOnClick = React.useCallback((event) => { + }, []); + const logoutButtonOnClick = React.useCallback(() => { + core.dispatch({ + action: 'Ctx', + args: { + action: 'Logout' } - } - }, [sections]); - + }); + }, []); return ( -
+
-
- - + navMenu={true} + /> +
+
+ + + +
+
+
+
General
+
+
+
+ {profile.auth === null ? 'Anonymous user' : profile.auth.user.email} +
+
+ { + profile.auth === null ? +
+ +
+ : +
+ +
+ } +
+ +
+
+
+
Interface language
+
+ +
+
+
+
Trakt Scrobbling
+
+ +
+
+
+
Facebook import
+
+ +
+
+
+
Calendar
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ { + profile.auth !== null ? +
+ +
+ : + null + } +
+
+
Player
+
+
+
Subtitles language
+
+ +
+
+
+
Subtitles size
+
+ +
+
+
+
Subtitles text color
+
+ +
+
+
+
Subtitles background color
+
+ +
+
+
+
Subtitles outline color
+
+ +
+
+
+
Auto-play next episode
+
+ +
+
+
+
Play in background
+
+ +
+
+
+
Hardware-accelerated decoding
+
+ +
+
+
+
Streaming Server
+
+
+
Status
+
+
+
Online
+
+
+
+
+
Base Url
+
+
+
http://
+
+
+ { + cacheSizeSelect !== null ? +
+
+
Cache size
+
+ +
+ : + null + } + { + torrentProfileSelect !== null ? +
+
+
Torrent profile
+
+ +
+ : + null + + } +
+
); diff --git a/src/routes/Settings/constants.js b/src/routes/Settings/constants.js deleted file mode 100644 index 9a3a95b9d..000000000 --- a/src/routes/Settings/constants.js +++ /dev/null @@ -1,28 +0,0 @@ -const languageOptions = [ { "label": "аҧсуа бызшәа", "value": "abk" }, { "label": "Afaraf", "value": "aar" }, { "label": "Afrikaans", "value": "afr" }, { "label": "Akan", "value": "aka" }, { "label": "gjuha shqipe", "value": "sqi" }, { "label": "አማርኛ", "value": "amh" }, { "label": "العربية", "value": "ara" }, { "label": "aragonés", "value": "arg" }, { "label": "Հայերեն", "value": "hye" }, { "label": "অসমীয়া", "value": "asm" }, { "label": "авар мацӀ", "value": "ava" }, { "label": "avesta", "value": "ave" }, { "label": "aymar aru", "value": "aym" }, { "label": "azərbaycan dili", "value": "aze" }, { "label": "bamanankan", "value": "bam" }, { "label": "башҡорт теле", "value": "bak" }, { "label": "euskara", "value": "eus" }, { "label": "беларуская мова", "value": "bel" }, { "label": "বাংলা", "value": "ben" }, { "label": "भोजपुरी", "value": "bih" }, { "label": "Bislama", "value": "bis" }, { "label": "bosanski jezik", "value": "bos" }, { "label": "brezhoneg", "value": "bre" }, { "label": "български език", "value": "bul" }, { "label": "ဗမာစာ", "value": "mya" }, { "label": "català", "value": "cat" }, { "label": "Chamoru", "value": "cha" }, { "label": "нохчийн мотт", "value": "che" }, { "label": "chiCheŵa", "value": "nya" }, { "label": "中文 (Zhōngwén)", "value": "zho" }, { "label": "чӑваш чӗлхи", "value": "chv" }, { "label": "Kernewek", "value": "cor" }, { "label": "corsu", "value": "cos" }, { "label": "ᓀᐦᐃᔭᐍᐏᐣ", "value": "cre" }, { "label": "hrvatski jezik", "value": "hrv" }, { "label": "čeština", "value": "ces" }, { "label": "dansk", "value": "dan" }, { "label": "ދިވެހި", "value": "div" }, { "label": "Nederlands", "value": "nld" }, { "label": "རྫོང་ཁ", "value": "dzo" }, { "label": "English", "value": "eng" }, { "label": "Esperanto", "value": "epo" }, { "label": "eesti", "value": "est" }, { "label": "Eʋegbe", "value": "ewe" }, { "label": "føroyskt", "value": "fao" }, { "label": "vosa Vakaviti", "value": "fij" }, { "label": "suomi", "value": "fin" }, { "label": "français", "value": "fre" }, { "label": "Fulfulde", "value": "ful" }, { "label": "galego", "value": "glg" }, { "label": "ქართული", "value": "kat" }, { "label": "Deutsch", "value": "ger" }, { "label": "ελληνικά", "value": "ell" }, { "label": "Avañe'ẽ", "value": "grn" }, { "label": "ગુજરાતી", "value": "guj" }, { "label": "Kreyòl ayisyen", "value": "hat" }, { "label": "Hausa", "value": "hau" }, { "label": "עברית", "value": "heb" }, { "label": "Otjiherero", "value": "her" }, { "label": "हिन्दी", "value": "hin" }, { "label": "Hiri Motu", "value": "hmo" }, { "label": "magyar", "value": "hun" }, { "label": "Interlingua", "value": "ina" }, { "label": "Bahasa Indonesia", "value": "ind" }, { "label": "Interlingue", "value": "ile" }, { "label": "Gaeilge", "value": "gle" }, { "label": "Asụsụ Igbo", "value": "ibo" }, { "label": "Iñupiaq", "value": "ipk" }, { "label": "Ido", "value": "ido" }, { "label": "Íslenska", "value": "isl" }, { "label": "italiano", "value": "ita" }, { "label": "ᐃᓄᒃᑎᑐᑦ", "value": "iku" }, { "label": "日本語 (にほんご)", "value": "jpn" }, { "label": "basa Jawa", "value": "jav" }, { "label": "kalaallisut", "value": "kal" }, { "label": "ಕನ್ನಡ", "value": "kan" }, { "label": "Kanuri", "value": "kau" }, { "label": "कश्मीरी", "value": "kas" }, { "label": "қазақ тілі", "value": "kaz" }, { "label": "ខ្មែរ", "value": "khm" }, { "label": "Gĩkũyũ", "value": "kik" }, { "label": "Ikinyarwanda", "value": "kin" }, { "label": "Кыргызча", "value": "kir" }, { "label": "коми кыв", "value": "kom" }, { "label": "KiKongo", "value": "kon" }, { "label": "한국어 (韓國語)", "value": "kor" }, { "label": "Kurdî", "value": "kur" }, { "label": "Kuanyama", "value": "kua" }, { "label": "latine", "value": "lat" }, { "label": "Lëtzebuergesch", "value": "ltz" }, { "label": "Luganda", "value": "lug" }, { "label": "Limburgs", "value": "lim" }, { "label": "Lingála", "value": "lin" }, { "label": "ພາສາລາວ", "value": "lao" }, { "label": "lietuvių kalba", "value": "lit" }, { "label": "Tshiluba", "value": "lub" }, { "label": "latviešu valoda", "value": "lav" }, { "label": "Gaelg", "value": "glv" }, { "label": "македонски јазик", "value": "mkd" }, { "label": "fiteny malagasy", "value": "mlg" }, { "label": "bahasa Melayu", "value": "msa" }, { "label": "മലയാളം", "value": "mal" }, { "label": "Malti", "value": "mlt" }, { "label": "te reo Māori", "value": "mri" }, { "label": "मराठी", "value": "mar" }, { "label": "Kajin M̧ajeļ", "value": "mah" }, { "label": "монгол", "value": "mon" }, { "label": "Ekakairũ Naoero", "value": "nau" }, { "label": "Diné bizaad", "value": "nav" }, { "label": "Norsk bokmål", "value": "nob" }, { "label": "isiNdebele", "value": "nde" }, { "label": "नेपाली", "value": "nep" }, { "label": "Owambo", "value": "ndo" }, { "label": "Norsk nynorsk", "value": "nno" }, { "label": "Norsk", "value": "nor" }, { "label": "ꆈꌠ꒿ Nuosuhxop", "value": "iii" }, { "label": "isiNdebele", "value": "nbl" }, { "label": "occitan", "value": "oci" }, { "label": "ᐊᓂᔑᓈᐯᒧᐎᓐ", "value": "oji" }, { "label": "ѩзыкъ словѣньскъ", "value": "chu" }, { "label": "Afaan Oromoo", "value": "orm" }, { "label": "ଓଡ଼ିଆ", "value": "ori" }, { "label": "ирон æвзаг", "value": "oss" }, { "label": "ਪੰਜਾਬੀ", "value": "pan" }, { "label": "पाऴि", "value": "pli" }, { "label": "فارسی", "value": "fas" }, { "label": "język polski", "value": "pol" }, { "label": "پښتو", "value": "pus" }, { "label": "português", "value": "por" }, { "label": "português Brazil", "value": "pob" }, { "label": "Runa Simi", "value": "que" }, { "label": "rumantsch grischun", "value": "roh" }, { "label": "Ikirundi", "value": "run" }, { "label": "limba română", "value": "ron" }, { "label": "русский язык", "value": "rus" }, { "label": "संस्कृतम्", "value": "san" }, { "label": "sardu", "value": "srd" }, { "label": "सिन्धी", "value": "snd" }, { "label": "Davvisámegiella", "value": "sme" }, { "label": "gagana fa'a Samoa", "value": "smo" }, { "label": "yângâ tî sängö", "value": "sag" }, { "label": "српски језик", "value": "srp" }, { "label": "Gàidhlig", "value": "gla" }, { "label": "chiShona", "value": "sna" }, { "label": "සිංහල", "value": "sin" }, { "label": "slovenčina", "value": "slk" }, { "label": "slovenski jezik", "value": "slv" }, { "label": "Soomaaliga", "value": "som" }, { "label": "Sesotho", "value": "sot" }, { "label": "español", "value": "spa" }, { "label": "Basa Sunda", "value": "sun" }, { "label": "Kiswahili", "value": "swa" }, { "label": "SiSwati", "value": "ssw" }, { "label": "Svenska", "value": "swe" }, { "label": "தமிழ்", "value": "tam" }, { "label": "తెలుగు", "value": "tel" }, { "label": "тоҷикӣ", "value": "tgk" }, { "label": "ไทย", "value": "tha" }, { "label": "ትግርኛ", "value": "tir" }, { "label": "བོད་ཡིག", "value": "bod" }, { "label": "Türkmen", "value": "tuk" }, { "label": "Wikang Tagalog", "value": "tgl" }, { "label": "Setswana", "value": "tsn" }, { "label": "faka Tonga", "value": "ton" }, { "label": "Türkçe", "value": "tur" }, { "label": "Xitsonga", "value": "tso" }, { "label": "татар теле", "value": "tat" }, { "label": "Twi", "value": "twi" }, { "label": "Reo Tahiti", "value": "tah" }, { "label": "Uyƣurqə", "value": "uig" }, { "label": "українська мова", "value": "ukr" }, { "label": "اردو", "value": "urd" }, { "label": "O'zbek", "value": "uzb" }, { "label": "Tshivenḓa", "value": "ven" }, { "label": "Tiếng Việt", "value": "vie" }, { "label": "Volapük", "value": "vol" }, { "label": "walon", "value": "wln" }, { "label": "Cymraeg", "value": "cym" }, { "label": "Wollof", "value": "wol" }, { "label": "Frysk", "value": "fry" }, { "label": "isiXhosa", "value": "xho" }, { "label": "ייִדיש", "value": "yid" }, { "label": "Yorùbá", "value": "yor" }, { "label": "Saɯ cueŋƅ", "value": "zha" }, { "label": "isiZulu", "value": "zul" } ]; - -const settingsSections = { - 'General': [ - { 'id': 'user', 'type': 'user' }, - { 'id': 'language', 'header': 'UI Language', 'label': 'UI Language', 'type': 'select', 'options': [{ 'label': 'Български език', 'value': 'bul' }, { 'label': 'English', 'value': 'eng' }, { 'label': 'Deutsch', 'value': 'ger' }, { 'label': 'Español', 'value': 'esp' }, { 'label': 'Italiano', 'value': 'ita' }] }, - { 'id': 'show_vid_overview', 'label': 'Show videos overview', 'type': 'checkbox' }, - ], - 'Player': [ - { 'id': 'add-ons', 'label': 'ADD-ONS', 'type': 'button', 'icon': 'ic_addons', 'href': '#/addons' }, - { 'id': 'subtitles_language', 'header': 'Default Subtitles Language', 'label': 'Default Subtitles Language', 'type': 'select', 'options': languageOptions }, - { 'id': 'subtitles_size', 'header': 'Default Subtitles Size', 'label': 'Default Subtitles Size', 'type': 'select', 'options': [{ 'label': '72%', 'value': '72%' }, { 'label': '80%', 'value': '80%' }, { 'label': '100%', 'value': '100%' }, { 'label': '120%', 'value': '120%' }, { 'label': '140%', 'value': '140%' }, { 'label': '160%', 'value': '160%' }, { 'label': '180%', 'value': '180%' }] }, - { 'id': 'subtitles_background', 'header': 'Subtitles Background', 'label': 'Subtitles background', 'type': 'select', 'options': [{ 'label': 'None', 'value': '' }, { 'label': 'Solid', 'value': 'solid' }, { 'label': 'Transparent', 'value': 'transparent' }] }, - { 'id': 'subtitles_color', 'header': 'Subtitles color', 'label': 'Subtitles color', 'type': 'color' }, - { 'id': 'subtitles_outline_color', 'header': 'Subtitles outline color', 'label': 'Subtitles outline color', 'type': 'color' }, - { 'id': 'autoplay_next_vid', 'label': 'Auto-play next episode', 'type': 'checkbox' }, - { 'id': 'pause_on_lost_focus', 'label': 'Pause playback when not in focus', 'type': 'checkbox' }, - { 'id': 'use_external_player', 'label': 'Launch player in a separate window (advanced)', 'type': 'checkbox' }, - ], - 'Streaming': [ - { 'id': 'streaming', 'type': 'streaming' }, - ] -}; - - -module.exports = { - settingsSections, -}; diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less index df5281628..a5edc0be4 100644 --- a/src/routes/Settings/styles.less +++ b/src/routes/Settings/styles.less @@ -1,4 +1,13 @@ -.settings-parent-container { +:import('~stremio/common/Checkbox/styles.less') { + checkbox-icon: icon; +} + +:import('~stremio/common/Multiselect/styles.less') { + multiselect-menu-container: menu-container; + multiselect-label: label; +} + +.settings-container { display: flex; flex-direction: column; width: 100%; @@ -10,32 +19,280 @@ align-self: stretch; } - .settings-container { - width: 100%; - height: 100%; + .settings-content { + flex: 1; + align-self: stretch; display: flex; flex-direction: row; - background-color: var(--color-backgroundlight); - --input-width: 35rem; - - .side-menu { - padding: 1rem; - width: 17rem; + + .side-menu-container { + flex: none; + align-self: stretch; display: flex; flex-direction: column; + width: 17rem; + padding: 1rem; background-color: var(--color-backgroundlighter); + .side-menu-button { + flex: none; + align-self: stretch; + padding: 1rem; + font-size: 1.1rem; + color: var(--color-surfacelighter); + &.selected { + background-color: var(--color-background); + } + + &:hover { + background-color: var(--color-surface20); + } + } } - .scroll-container { - padding: 0 2rem; + .sections-container { flex: 1; + align-self: stretch; + padding: 0 2rem; overflow-y: auto; - >:not(:last-child) { - border-bottom: calc(var(--focusable-border-size) * 0.5) solid var(--color-primary); + .section-container { + display: flex; + flex-direction: column; + margin-bottom: 2rem; + overflow: visible; + + &:first-child { + margin-top: 1rem; + } + + &:not(:last-child) { + padding-bottom: 2rem; + border-bottom: thin solid var(--color-primary40); + } + + .section-title { + flex: none; + align-self: stretch; + font-size: 1.8rem; + line-height: 3.4rem; + margin-bottom: 1rem; + color: var(--color-surfacelighter); + } + + .option-container { + flex: none; + align-self: stretch; + display: flex; + flex-direction: row; + align-items: center; + max-width: 35rem; + margin-bottom: 2rem; + overflow: visible; + + &:last-child { + margin-bottom: 0; + } + + .option-name-container, .option-input-container { + flex: 1 1 50%; + display: flex; + flex-direction: row; + align-items: center; + + .icon { + flex: none; + width: 1.5rem; + height: 1.5rem; + margin-right: 0.5rem; + fill: var(--color-surfacelighter); + } + + .label { + flex-grow: 0; + flex-shrink: 1; + flex-basis: auto; + line-height: 1.5rem; + color: var(--color-surfacelighter); + } + } + + .option-name-container { + justify-content: flex-start; + padding: 1rem 1rem 1rem 0; + margin-right: 2rem; + } + + .option-input-container { + padding: 1rem; + + &.button-container { + justify-content: center; + background-color: var(--color-primary); + + &:hover { + background-color: var(--color-primarylight); + } + } + + &.multiselect-container { + >.multiselect-label { + line-height: 1.5rem; + max-height: 1.5rem; + } + + .multiselect-menu-container { + max-height: calc(3.2rem * 7); + overflow: auto; + } + } + + &.link-container { + flex: 0 1 auto; + padding: 1rem 0; + + &:hover { + .label { + text-decoration: underline; + } + } + } + + &.checkbox-container { + justify-content: center; + + .checkbox-icon { + width: 1.5rem; + height: 1.5rem; + } + } + + &.color-input-container { + padding: 1.75rem 1rem; + } + + &.info-container { + justify-content: center; + } + } + } + + .user-info-option-container { + .avatar-container { + flex: none; + width: 6rem; + height: 6rem; + border-radius: 50%; + background-size: cover; + background-repeat: no-repeat; + background-position: center; + background-origin: content-box; + background-clip: content-box; + } + + .email-label { + flex: 1; + font-size: 1.4rem; + max-height: 3.6em; + padding: 0 2rem; + color: var(--color-surfacelighter); + } + } + + //////////////////////////////// + + .subscription-container, + .language-picker-container, + .size-picker-container, + .color-picker-container { + flex: none; + align-self: stretch; + display: flex; + flex-direction: row; + align-items: center; + max-width: 35rem; + margin-bottom: 2rem; + + .label-container { + flex: 1; + display: flex; + flex-direction: row; + align-items: center; + margin-right: 1rem; + + .icon { + flex: none; + width: 2.5rem; + height: 2.5rem; + margin-right: 0.5rem; + fill: var(--color-surfacelighter); + } + + .label { + flex: 1; + color: var(--color-surfacelighter); + } + } + + .subscribe-button-container, .multiselect-container, .color-input { + flex: 1; + padding: 1.15rem; + } + } + + .subscription-container { + .subscribe-button-container { + color: var(--color-surfacelighter); + text-align: center; + background-color: var(--color-primary); + + &:hover { + background-color: var(--color-primarylight); + } + } + } + + .language-picker-container, .size-picker-container { + overflow: visible; + + .multiselect-container { + .multiselect-menu-container { + max-height: calc(3.2rem * 7); + overflow: auto; + } + } + } + + .color-picker-container { + .color-input { + height: 3.5rem; + } + } + + .checkbox-option-container { + display: flex; + flex-direction: row-reverse; + align-items: center; + justify-content: flex-end; + max-width: 35rem; + margin-bottom: 2rem; + + .label { + flex: none; + width: calc(50% - 0.5rem); + color: var(--color-surfacelighter); + } + + .checkbox-icon { + flex: none; + width: 2rem; + height: 2rem; + margin-left: calc(25% - 1rem); + fill: var(--color-surfacelighter); + } + } } } } -} +} \ No newline at end of file diff --git a/src/routes/Settings/useProfileSettingsInputs.js b/src/routes/Settings/useProfileSettingsInputs.js new file mode 100644 index 000000000..514354d74 --- /dev/null +++ b/src/routes/Settings/useProfileSettingsInputs.js @@ -0,0 +1,103 @@ +const React = require('react'); +const { useProfile } = require('stremio/common'); +const languages = require('./languages'); + +const SUBTITLES_SIZES = [75, 100, 125, 150, 175, 200, 250]; + +const useProfileSettingsInputs = () => { + const profile = useProfile(); + const interfaceLanguageSelect = React.useMemo(() => ({ + options: Object.keys(languages).map((code) => ({ + value: code, + label: languages[code] + })), + selected: [profile.settings.interface_language], + renderLabelText: () => { + return typeof languages[profile.settings.interface_language] === 'string' ? + languages[profile.settings.interface_language] + : + profile.settings.interface_language; + }, + onSelect: (event) => { + console.log(event); + } + }), [profile.settings.interface_language]); + const subtitlesLanguageSelect = React.useMemo(() => ({ + options: Object.keys(languages).map((code) => ({ + value: code, + label: languages[code] + })), + selected: [profile.settings.subtitles_language], + renderLabelText: () => { + return typeof languages[profile.settings.subtitles_language] === 'string' ? + languages[profile.settings.subtitles_language] + : + profile.settings.subtitles_language; + }, + onSelect: (event) => { + console.log(event); + } + }), [profile.settings.subtitles_language]); + const subtitlesSizeSelect = React.useMemo(() => ({ + options: SUBTITLES_SIZES.map((size) => ({ + value: `${size}`, + label: `${size}%` + })), + selected: [`${profile.settings.subtitles_size}`], + renderLabelText: () => { + return `${profile.settings.subtitles_size}%`; + }, + onSelect: (event) => { + console.log(event); + } + }), [profile.settings.subtitles_size]); + const subtitlesTextColorInput = React.useMemo(() => ({ + value: profile.settings.subtitles_text_color, + onChange: (event) => { + console.log(event); + } + }), [profile.settings.subtitles_text_color]); + const subtitlesBackgroundColorInput = React.useMemo(() => ({ + value: profile.settings.subtitles_background_color, + onChange: (event) => { + console.log(event); + } + }), [profile.settings.subtitles_background_color]); + const subtitlesOutlineColorInput = React.useMemo(() => ({ + value: profile.settings.subtitles_outline_color, + onChange: (event) => { + console.log(event); + } + }), [profile.settings.subtitles_outline_color]); + const bingeWatchingCheckbox = React.useMemo(() => ({ + checked: profile.settings.binge_watching, + onClick: (event) => { + console.log(event); + } + }), [profile.settings.binge_watching]); + const playInBackgroundCheckbox = React.useMemo(() => ({ + checked: profile.settings.play_in_background, + onClick: (event) => { + console.log(event); + } + }), [profile.settings.play_in_background]); + const playInExternalPlayerCheckbox = React.useMemo(() => ({ + checked: profile.settings.play_in_external_player, + onClick: (event) => { + console.log(event); + } + }), [profile.settings.play_in_external_player]); + return { + interfaceLanguageSelect, + subtitlesLanguageSelect, + subtitlesSizeSelect, + subtitlesTextColorInput, + subtitlesBackgroundColorInput, + subtitlesOutlineColorInput, + bingeWatchingCheckbox, + playInBackgroundCheckbox, + playInExternalPlayerCheckbox + }; +}; + +module.exports = useProfileSettingsInputs; diff --git a/src/routes/Settings/useSettings.js b/src/routes/Settings/useSettings.js deleted file mode 100644 index 4f71eba0e..000000000 --- a/src/routes/Settings/useSettings.js +++ /dev/null @@ -1,68 +0,0 @@ -const React = require('react'); -const { useServices } = require('stremio/services'); - -const IGNORED_SETTINGS = Object.freeze(['user', 'streaming']); - -module.exports = () => { - const { core } = useServices(); - - const [settings, setSettings] = React.useState({ - user: null, - streaming: {}, - streaming_loaded: false, - streaming_error: "" - }); - - React.useEffect(() => { - const onNewState = () => { - const { ctx, streaming_server_settings } = core.getState() - try { - const newSettings = { - ...settings, - ...ctx.content.settings, - user: ctx.content.auth ? ctx.content.auth.user : null, - streaming: streaming_server_settings && streaming_server_settings.ready || {}, - streaming_loaded: streaming_server_settings && !!(streaming_server_settings.error || streaming_server_settings.ready), - streaming_error: streaming_server_settings && streaming_server_settings.error || "", - }; - setSettings(newSettings); - } catch (e) { - console.log('Cannot update settings state', e); - } - }; - const onStoreError = ({ event, args }) => { - if (event !== "SettingsStoreError") return; - // TODO: Notify with maybe a toast? - console.log(args) - } - - core.on('NewModel', onNewState); - core.on('Event', onStoreError); - - onNewState(); - - return () => { - // Destructor function - core.off('NewModel', onNewState); - core.off('Event', onStoreError); - }; - }, []); - - const setTheSettings = React.useCallback(newSettings => { - const event = { action: 'Settings', args: { args: {} } }; - // This can be done with React.useEffect and newSettings.streaming as dependency - const streamingServerSettingChanged = settings.streaming && Object.keys(newSettings.streaming) - .some(prop => settings.streaming[prop] !== newSettings.streaming[prop]); - if (streamingServerSettingChanged) { - event.args = { settings: 'StoreStreamingServer', args: newSettings.streaming }; - } else { - event.args.settings = 'Store'; - Object.keys(newSettings) - .filter(prop => !IGNORED_SETTINGS.includes(prop)) - .forEach(key => event.args.args[key] = newSettings[key].toString()); - } - core.dispatch(event); - }, [settings]) - - return [settings, setTheSettings]; -}; diff --git a/src/routes/Settings/useStreamingServerSettingsInputs.js b/src/routes/Settings/useStreamingServerSettingsInputs.js new file mode 100644 index 000000000..f3abd5321 --- /dev/null +++ b/src/routes/Settings/useStreamingServerSettingsInputs.js @@ -0,0 +1,108 @@ +const React = require('react'); +const isEqual = require('lodash.isequal'); +const { useStreamingServer } = require('stremio/common'); + +const CACHE_SIZES = [0, 2147483648, 5368709120, 10737418240, null]; + +const cacheSizeToString = (size) => { + return size === null ? + 'Infinite' + : + size === 0 ? + 'No caching' + : + `${size / 1024 / 1024 / 1024}GiB`; +}; + +const TORRENT_PROFILES = { + default: { + btDownloadSpeedHardLimit: 2621440, + btDownloadSpeedSoftLimit: 1677721.6, + btHandshakeTimeout: 20000, + btMaxConnections: 35, + btMinPeersForStable: 5, + btRequestTimeout: 4000 + }, + soft: { + btDownloadSpeedHardLimit: 1677721.6, + btDownloadSpeedSoftLimit: 1677721.6, + btHandshakeTimeout: 20000, + btMaxConnections: 35, + btMinPeersForStable: 5, + btRequestTimeout: 4000 + }, + fast: { + btDownloadSpeedHardLimit: 39321600, + btDownloadSpeedSoftLimit: 4194304, + btHandshakeTimeout: 20000, + btMaxConnections: 200, + btMinPeersForStable: 10, + btRequestTimeout: 4000 + } +}; + +const useStreaminServerSettingsInputs = () => { + const streaminServer = useStreamingServer(); + const cacheSizeSelect = React.useMemo(() => { + if (streaminServer.type !== 'Ready') { + return null; + } + + return { + options: CACHE_SIZES.map((size) => ({ + label: cacheSizeToString(size), + value: JSON.stringify(size) + })), + selected: [JSON.stringify(streaminServer.settings.cacheSize)], + renderLabelText: () => { + return cacheSizeToString(streaminServer.settings.cacheSize); + } + }; + }, [streaminServer.type, streaminServer.settings && streaminServer.settings.cacheSize]); + const torrentProfileSelect = React.useMemo(() => { + if (streaminServer.type !== 'Ready') { + return null; + } + + const selectedTorrentProfile = { + btDownloadSpeedHardLimit: streaminServer.settings.btDownloadSpeedHardLimit, + btDownloadSpeedSoftLimit: streaminServer.settings.btDownloadSpeedSoftLimit, + btHandshakeTimeout: streaminServer.settings.btHandshakeTimeout, + btMaxConnections: streaminServer.settings.btMaxConnections, + btMinPeersForStable: streaminServer.settings.btMinPeersForStable, + btRequestTimeout: streaminServer.settings.btRequestTimeout + }; + const isCustomTorrentProfileSelected = Object.values(TORRENT_PROFILES).every((torrentProfile) => { + return !isEqual(torrentProfile, selectedTorrentProfile); + }); + return { + options: Object.keys(TORRENT_PROFILES) + .map((profileName) => ({ + label: profileName, + value: JSON.stringify(TORRENT_PROFILES[profileName]) + })) + .concat( + isCustomTorrentProfileSelected ? + [{ + label: 'custom', + value: JSON.stringify(selectedTorrentProfile) + }] + : + [] + ), + selected: [JSON.stringify(selectedTorrentProfile)], + renderLabelText: () => { + return Object.keys(TORRENT_PROFILES).reduce((result, profileName) => { + if (isEqual(TORRENT_PROFILES[profileName], selectedTorrentProfile)) { + return profileName; + } + + return result; + }, 'custom'); + } + }; + }, [streaminServer.type, streaminServer.settings]); + return { cacheSizeSelect, torrentProfileSelect }; +}; + +module.exports = useStreaminServerSettingsInputs; From 45c7c4ab330e61dcfce3425307e8e621faef464e Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 11:12:10 +0200 Subject: [PATCH 21/56] change cache size implemented --- .../Settings/useStreamingServerSettingsInputs.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/routes/Settings/useStreamingServerSettingsInputs.js b/src/routes/Settings/useStreamingServerSettingsInputs.js index f3abd5321..a3fbac42b 100644 --- a/src/routes/Settings/useStreamingServerSettingsInputs.js +++ b/src/routes/Settings/useStreamingServerSettingsInputs.js @@ -1,5 +1,6 @@ const React = require('react'); const isEqual = require('lodash.isequal'); +const { useServices } = require('stremio/services'); const { useStreamingServer } = require('stremio/common'); const CACHE_SIZES = [0, 2147483648, 5368709120, 10737418240, null]; @@ -42,6 +43,7 @@ const TORRENT_PROFILES = { }; const useStreaminServerSettingsInputs = () => { + const { core } = useServices(); const streaminServer = useStreamingServer(); const cacheSizeSelect = React.useMemo(() => { if (streaminServer.type !== 'Ready') { @@ -56,9 +58,21 @@ const useStreaminServerSettingsInputs = () => { selected: [JSON.stringify(streaminServer.settings.cacheSize)], renderLabelText: () => { return cacheSizeToString(streaminServer.settings.cacheSize); + }, + onSelect: (event) => { + core.dispatch({ + action: 'Ctx', + args: { + action: 'UpdateSettings', + args: { + ...streaminServer.settings, + cacheSize: JSON.parse(event.value) + } + } + }); } }; - }, [streaminServer.type, streaminServer.settings && streaminServer.settings.cacheSize]); + }, [streaminServer.type, streaminServer.settings]); const torrentProfileSelect = React.useMemo(() => { if (streaminServer.type !== 'Ready') { return null; From 7ab2dab78a378331a63380638b5ffdd73955d455 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Tue, 11 Feb 2020 11:35:18 +0200 Subject: [PATCH 22/56] CredentialsTextInput with defaultValue used instead of TextInput --- .../PasswordResetModal/PasswordResetModal.js | 17 ++++++++--------- src/routes/Intro/PasswordResetModal/styles.less | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js index 5c9b08dce..15d795a11 100644 --- a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js +++ b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js @@ -1,20 +1,20 @@ const React = require('react'); const PropTypes = require('prop-types'); const { useRouteFocused } = require('stremio-router'); -const { ModalDialog, TextInput } = require('stremio/common'); +const { ModalDialog } = require('stremio/common'); +const CredentialsTextInput = require('stremio/routes/Intro/CredentialsTextInput'); const styles = require('./styles'); const PasswordResetModal = ({ email, onCloseRequest }) => { const routeFocused = useRouteFocused(); const [error, setError] = React.useState(''); - const [modalEmail, setModalEmail] = React.useState(typeof email === 'string' ? email : ''); const modalEmailRef = React.useRef(null); const passwordResetOnClick = React.useCallback(() => { - modalEmail.length > 0 && modalEmailRef.current.validity.valid ? - window.open('https://www.strem.io/reset-password/' + modalEmail, '_blank') + modalEmailRef.current.value.length > 0 && modalEmailRef.current.validity.valid ? + window.open('https://www.strem.io/reset-password/' + modalEmailRef.current.value, '_blank') : setError('Invalid email'); - }, [modalEmail]); + }, [modalEmailRef.current]); const passwordResetModalButtons = React.useMemo(() => { return [ { @@ -34,7 +34,6 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { }, [onCloseRequest, passwordResetOnClick]); const emailOnChange = React.useCallback((event) => { setError(''); - setModalEmail(event.currentTarget.value); }, []); const emailOnSubmit = React.useCallback(() => { passwordResetOnClick(); @@ -47,12 +46,12 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { return (
Enter your email
- diff --git a/src/routes/Intro/PasswordResetModal/styles.less b/src/routes/Intro/PasswordResetModal/styles.less index 219a82b3f..c61452534 100644 --- a/src/routes/Intro/PasswordResetModal/styles.less +++ b/src/routes/Intro/PasswordResetModal/styles.less @@ -3,7 +3,7 @@ margin-bottom: 1rem; } - .text-input { + .credentials-text-input { width: 20rem; padding: 0.5rem; background-color: var(--color-surface40); From 553340f329d91f79cb26a0ed9ab1a3b6b1c198c3 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Tue, 11 Feb 2020 12:33:27 +0200 Subject: [PATCH 23/56] unnecessary arg removed --- src/routes/Intro/PasswordResetModal/PasswordResetModal.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js index 15d795a11..b8dd6f861 100644 --- a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js +++ b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js @@ -14,7 +14,7 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { window.open('https://www.strem.io/reset-password/' + modalEmailRef.current.value, '_blank') : setError('Invalid email'); - }, [modalEmailRef.current]); + }, [modalEmailRef.current]); const passwordResetModalButtons = React.useMemo(() => { return [ { @@ -32,7 +32,7 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { } ]; }, [onCloseRequest, passwordResetOnClick]); - const emailOnChange = React.useCallback((event) => { + const emailOnChange = React.useCallback(() => { setError(''); }, []); const emailOnSubmit = React.useCallback(() => { From b0ef3f89d65dcccb620b1d446d080f5ec5512ea6 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 13:05:25 +0200 Subject: [PATCH 24/56] change torrent profile implemented --- .../Settings/useStreamingServerSettingsInputs.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/routes/Settings/useStreamingServerSettingsInputs.js b/src/routes/Settings/useStreamingServerSettingsInputs.js index a3fbac42b..fc247c902 100644 --- a/src/routes/Settings/useStreamingServerSettingsInputs.js +++ b/src/routes/Settings/useStreamingServerSettingsInputs.js @@ -113,6 +113,18 @@ const useStreaminServerSettingsInputs = () => { return result; }, 'custom'); + }, + onSelect: (event) => { + core.dispatch({ + action: 'Ctx', + args: { + action: 'UpdateSettings', + args: { + ...streaminServer.settings, + ...JSON.parse(event.value) + } + } + }); } }; }, [streaminServer.type, streaminServer.settings]); From 84a1785a0e8d7d73f3eaf86037ef73b37acfc3dc Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Tue, 11 Feb 2020 13:59:54 +0200 Subject: [PATCH 25/56] not needed target _blank removed --- src/routes/Intro/Intro.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Intro/Intro.js b/src/routes/Intro/Intro.js index 2e54d5621..97e19ef3a 100644 --- a/src/routes/Intro/Intro.js +++ b/src/routes/Intro/Intro.js @@ -325,7 +325,7 @@ const Intro = ({ queryParams }) => { :
- +
} { From c0b149e9ab34144461fd2ee8d996c8e91101f212 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Tue, 11 Feb 2020 14:03:02 +0200 Subject: [PATCH 26/56] ../ usage for CredentialsTextInput --- src/routes/Intro/PasswordResetModal/PasswordResetModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js index b8dd6f861..35b71ecce 100644 --- a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js +++ b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js @@ -2,7 +2,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const { useRouteFocused } = require('stremio-router'); const { ModalDialog } = require('stremio/common'); -const CredentialsTextInput = require('stremio/routes/Intro/CredentialsTextInput'); +const CredentialsTextInput = require('../CredentialsTextInput'); const styles = require('./styles'); const PasswordResetModal = ({ email, onCloseRequest }) => { From eea05e46cb9e3801561f2c73a4f72e2609468265 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Tue, 11 Feb 2020 14:03:48 +0200 Subject: [PATCH 27/56] passwordResetOnClick dependency removed --- src/routes/Intro/PasswordResetModal/PasswordResetModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js index 35b71ecce..f12c63676 100644 --- a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js +++ b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js @@ -14,7 +14,7 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { window.open('https://www.strem.io/reset-password/' + modalEmailRef.current.value, '_blank') : setError('Invalid email'); - }, [modalEmailRef.current]); + }, []); const passwordResetModalButtons = React.useMemo(() => { return [ { From 8f64cb61e9dd4fe780633110078583afd466da4c Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Tue, 11 Feb 2020 14:24:18 +0200 Subject: [PATCH 28/56] goToPasswordReset func added --- .../Intro/PasswordResetModal/PasswordResetModal.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js index f12c63676..22a50448c 100644 --- a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js +++ b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js @@ -9,12 +9,15 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { const routeFocused = useRouteFocused(); const [error, setError] = React.useState(''); const modalEmailRef = React.useRef(null); - const passwordResetOnClick = React.useCallback(() => { + const goToPasswordReset = React.useCallback(() => { modalEmailRef.current.value.length > 0 && modalEmailRef.current.validity.valid ? window.open('https://www.strem.io/reset-password/' + modalEmailRef.current.value, '_blank') : setError('Invalid email'); }, []); + const passwordResetOnClick = React.useCallback(() => { + goToPasswordReset(); + }, [goToPasswordReset]); const passwordResetModalButtons = React.useMemo(() => { return [ { @@ -36,8 +39,8 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { setError(''); }, []); const emailOnSubmit = React.useCallback(() => { - passwordResetOnClick(); - }, [passwordResetOnClick]); + goToPasswordReset(); + }, [goToPasswordReset]); React.useEffect(() => { if (routeFocused) { modalEmailRef.current.focus(); From 7f73dab543a0e5c91fac8d1ac34239f530038a59 Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Tue, 11 Feb 2020 14:44:59 +0200 Subject: [PATCH 29/56] modalEmailRef renamed to emailRef --- .../Intro/PasswordResetModal/PasswordResetModal.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js index 22a50448c..6884cc6ec 100644 --- a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js +++ b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js @@ -8,10 +8,10 @@ const styles = require('./styles'); const PasswordResetModal = ({ email, onCloseRequest }) => { const routeFocused = useRouteFocused(); const [error, setError] = React.useState(''); - const modalEmailRef = React.useRef(null); + const еmailRef = React.useRef(null); const goToPasswordReset = React.useCallback(() => { - modalEmailRef.current.value.length > 0 && modalEmailRef.current.validity.valid ? - window.open('https://www.strem.io/reset-password/' + modalEmailRef.current.value, '_blank') + еmailRef.current.value.length > 0 && еmailRef.current.validity.valid ? + window.open('https://www.strem.io/reset-password/' + еmailRef.current.value, '_blank') : setError('Invalid email'); }, []); @@ -43,14 +43,14 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { }, [goToPasswordReset]); React.useEffect(() => { if (routeFocused) { - modalEmailRef.current.focus(); + еmailRef.current.focus(); } }, [routeFocused]); return (
Enter your email
Date: Tue, 11 Feb 2020 14:58:25 +0200 Subject: [PATCH 30/56] goToPasswordReset removed from deps --- src/routes/Intro/PasswordResetModal/PasswordResetModal.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js index 6884cc6ec..6782eac46 100644 --- a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js +++ b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js @@ -17,7 +17,7 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { }, []); const passwordResetOnClick = React.useCallback(() => { goToPasswordReset(); - }, [goToPasswordReset]); + }, []); const passwordResetModalButtons = React.useMemo(() => { return [ { @@ -40,7 +40,7 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { }, []); const emailOnSubmit = React.useCallback(() => { goToPasswordReset(); - }, [goToPasswordReset]); + }, []); React.useEffect(() => { if (routeFocused) { еmailRef.current.focus(); From 2454f18fa899f19a1924bc73b5326b7e1fe47d99 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 15:14:34 +0200 Subject: [PATCH 31/56] streaming server finalized --- src/routes/Settings/Settings.js | 51 ++++++++++++++++++++++++++------- src/routes/Settings/styles.less | 8 ++++++ 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 149fc69c2..9e2cead9a 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -2,7 +2,7 @@ const React = require('react'); const classnames = require('classnames'); const Icon = require('stremio-icons/dom'); const { useServices } = require('stremio/services'); -const { Button, Checkbox, NavBar, Multiselect, ColorInput, useProfile, useStreamingServer } = require('stremio/common'); +const { Button, Checkbox, NavBar, Multiselect, TextInput, ColorInput, useProfile, useStreamingServer } = require('stremio/common'); const useProfileSettingsInputs = require('./useProfileSettingsInputs'); const useStreamingServerSettingsInputs = require('./useStreamingServerSettingsInputs'); const styles = require('./styles'); @@ -44,6 +44,14 @@ const Settings = () => { } }); }, []); + const reloadStreamingServer = React.useCallback(() => { + core.dispatch({ + action: 'Ctx', + args: { + action: 'ReloadStreamingServer' + } + }); + }, []); return (
{
Streaming Server
+
+ +
Status
-
Online
-
-
-
-
-
Base Url
-
-
-
http://
+
+ { + streaminServer.type === 'Ready' ? + 'Online' + : + streaminServer.type === 'Error' ? + `Error: (${streaminServer.error})` + : + streaminServer.type + } +
+ { + streaminServer.type === 'Ready' ? +
+
+
Base Url
+
+
+
+ {streaminServer.base_url} +
+
+
+ : + null + } { cacheSizeSelect !== null ?
@@ -291,7 +321,6 @@ const Settings = () => {
: null - }
diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less index a5edc0be4..770834529 100644 --- a/src/routes/Settings/styles.less +++ b/src/routes/Settings/styles.less @@ -174,6 +174,14 @@ &.info-container { justify-content: center; + + &.selectable { + user-select: text; + + .label { + user-select: text; + } + } } } } From 23a57c27147a5ef52a8226195144117a8f90f79c Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 15:34:15 +0200 Subject: [PATCH 32/56] not needed deps removed --- src/routes/Settings/Settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 9e2cead9a..0176f048c 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -2,7 +2,7 @@ const React = require('react'); const classnames = require('classnames'); const Icon = require('stremio-icons/dom'); const { useServices } = require('stremio/services'); -const { Button, Checkbox, NavBar, Multiselect, TextInput, ColorInput, useProfile, useStreamingServer } = require('stremio/common'); +const { Button, Checkbox, NavBar, Multiselect, ColorInput, useProfile, useStreamingServer } = require('stremio/common'); const useProfileSettingsInputs = require('./useProfileSettingsInputs'); const useStreamingServerSettingsInputs = require('./useStreamingServerSettingsInputs'); const styles = require('./styles'); From 396076e475229513835a64bc4f777cee253fc6ab Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 15:34:28 +0200 Subject: [PATCH 33/56] change interface language in core --- src/routes/Settings/useProfileSettingsInputs.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/routes/Settings/useProfileSettingsInputs.js b/src/routes/Settings/useProfileSettingsInputs.js index 514354d74..257a54aeb 100644 --- a/src/routes/Settings/useProfileSettingsInputs.js +++ b/src/routes/Settings/useProfileSettingsInputs.js @@ -1,10 +1,12 @@ const React = require('react'); +const { useServices } = require('stremio/services'); const { useProfile } = require('stremio/common'); const languages = require('./languages'); const SUBTITLES_SIZES = [75, 100, 125, 150, 175, 200, 250]; const useProfileSettingsInputs = () => { + const { core } = useServices(); const profile = useProfile(); const interfaceLanguageSelect = React.useMemo(() => ({ options: Object.keys(languages).map((code) => ({ @@ -19,9 +21,18 @@ const useProfileSettingsInputs = () => { profile.settings.interface_language; }, onSelect: (event) => { - console.log(event); + core.dispatch({ + action: 'Ctx', + args: { + action: 'UpdateSettings', + args: { + ...profile.settings, + interface_language: event.value + } + } + }); } - }), [profile.settings.interface_language]); + }), [profile.settings]); const subtitlesLanguageSelect = React.useMemo(() => ({ options: Object.keys(languages).map((code) => ({ value: code, From 4a7d30ec95f75f4ccad17bf0cd0bf1b9d6e20fb4 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 15:46:22 +0200 Subject: [PATCH 34/56] subtitles language changed in core --- src/routes/Settings/useProfileSettingsInputs.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/routes/Settings/useProfileSettingsInputs.js b/src/routes/Settings/useProfileSettingsInputs.js index 257a54aeb..ce4e308a3 100644 --- a/src/routes/Settings/useProfileSettingsInputs.js +++ b/src/routes/Settings/useProfileSettingsInputs.js @@ -46,9 +46,18 @@ const useProfileSettingsInputs = () => { profile.settings.subtitles_language; }, onSelect: (event) => { - console.log(event); + core.dispatch({ + action: 'Ctx', + args: { + action: 'UpdateSettings', + args: { + ...profile.settings, + subtitles_language: event.value + } + } + }); } - }), [profile.settings.subtitles_language]); + }), [profile.settings]); const subtitlesSizeSelect = React.useMemo(() => ({ options: SUBTITLES_SIZES.map((size) => ({ value: `${size}`, From c1f8f6fe6726f9d925c4a6849414f5de4e664ecd Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 15:47:57 +0200 Subject: [PATCH 35/56] subtitles size changed in core --- src/routes/Settings/useProfileSettingsInputs.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/routes/Settings/useProfileSettingsInputs.js b/src/routes/Settings/useProfileSettingsInputs.js index ce4e308a3..f007eb0c9 100644 --- a/src/routes/Settings/useProfileSettingsInputs.js +++ b/src/routes/Settings/useProfileSettingsInputs.js @@ -68,9 +68,18 @@ const useProfileSettingsInputs = () => { return `${profile.settings.subtitles_size}%`; }, onSelect: (event) => { - console.log(event); + core.dispatch({ + action: 'Ctx', + args: { + action: 'UpdateSettings', + args: { + ...profile.settings, + subtitles_size: parseInt(event.value) + } + } + }); } - }), [profile.settings.subtitles_size]); + }), [profile.settings]); const subtitlesTextColorInput = React.useMemo(() => ({ value: profile.settings.subtitles_text_color, onChange: (event) => { From 61688c9b86d6d838d639bd8bebcebd94d001013b Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 15:57:16 +0200 Subject: [PATCH 36/56] ColorInput thumb clip fixed --- src/common/ColorInput/ColorInput.js | 2 +- src/common/ColorInput/styles.less | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/common/ColorInput/ColorInput.js b/src/common/ColorInput/ColorInput.js index 312103225..e03aa3266 100644 --- a/src/common/ColorInput/ColorInput.js +++ b/src/common/ColorInput/ColorInput.js @@ -78,7 +78,7 @@ const ColorInput = ({ className, value, dataset, onChange, ...props }) => { { modalOpen ? - + : null diff --git a/src/common/ColorInput/styles.less b/src/common/ColorInput/styles.less index eed2b060f..9e85d9d8b 100644 --- a/src/common/ColorInput/styles.less +++ b/src/common/ColorInput/styles.less @@ -24,4 +24,8 @@ text-overflow: ellipsis; } } -} \ No newline at end of file +} + +.color-picker-container { + margin: 1rem 0; +} From 09a85bc3a036e3e8ca3f931e30959a2d26fd3911 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 16:04:19 +0200 Subject: [PATCH 37/56] subtitles colors changed in core --- .../Settings/useProfileSettingsInputs.js | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/routes/Settings/useProfileSettingsInputs.js b/src/routes/Settings/useProfileSettingsInputs.js index f007eb0c9..77a94d61b 100644 --- a/src/routes/Settings/useProfileSettingsInputs.js +++ b/src/routes/Settings/useProfileSettingsInputs.js @@ -83,21 +83,48 @@ const useProfileSettingsInputs = () => { const subtitlesTextColorInput = React.useMemo(() => ({ value: profile.settings.subtitles_text_color, onChange: (event) => { - console.log(event); + core.dispatch({ + action: 'Ctx', + args: { + action: 'UpdateSettings', + args: { + ...profile.settings, + subtitles_text_color: event.value + } + } + }); } - }), [profile.settings.subtitles_text_color]); + }), [profile.settings]); const subtitlesBackgroundColorInput = React.useMemo(() => ({ value: profile.settings.subtitles_background_color, onChange: (event) => { - console.log(event); + core.dispatch({ + action: 'Ctx', + args: { + action: 'UpdateSettings', + args: { + ...profile.settings, + subtitles_background_color: event.value + } + } + }); } - }), [profile.settings.subtitles_background_color]); + }), [profile.settings]); const subtitlesOutlineColorInput = React.useMemo(() => ({ value: profile.settings.subtitles_outline_color, onChange: (event) => { - console.log(event); + core.dispatch({ + action: 'Ctx', + args: { + action: 'UpdateSettings', + args: { + ...profile.settings, + subtitles_outline_color: event.value + } + } + }); } - }), [profile.settings.subtitles_outline_color]); + }), [profile.settings]); const bingeWatchingCheckbox = React.useMemo(() => ({ checked: profile.settings.binge_watching, onClick: (event) => { From e48606db7e53952c7dab397494dc5860583681a6 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 16:38:19 +0200 Subject: [PATCH 38/56] checkbox settings changed in core --- src/routes/Settings/Settings.js | 14 ++++- .../Settings/useProfileSettingsInputs.js | 63 ++++++++++++++++--- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 0176f048c..0fb372e8d 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -24,7 +24,8 @@ const Settings = () => { subtitlesOutlineColorInput, bingeWatchingCheckbox, playInBackgroundCheckbox, - playInExternalPlayerCheckbox + playInExternalPlayerCheckbox, + hardwareDecodingCheckbox } = useProfileSettingsInputs(); const { cacheSizeSelect, @@ -244,13 +245,22 @@ const Settings = () => { {...playInBackgroundCheckbox} />
+
+
+
Play in external player
+
+ +
Hardware-accelerated decoding
diff --git a/src/routes/Settings/useProfileSettingsInputs.js b/src/routes/Settings/useProfileSettingsInputs.js index 77a94d61b..42e06cd50 100644 --- a/src/routes/Settings/useProfileSettingsInputs.js +++ b/src/routes/Settings/useProfileSettingsInputs.js @@ -127,22 +127,64 @@ const useProfileSettingsInputs = () => { }), [profile.settings]); const bingeWatchingCheckbox = React.useMemo(() => ({ checked: profile.settings.binge_watching, - onClick: (event) => { - console.log(event); + onClick: () => { + core.dispatch({ + action: 'Ctx', + args: { + action: 'UpdateSettings', + args: { + ...profile.settings, + binge_watching: !profile.settings.binge_watching + } + } + }); } - }), [profile.settings.binge_watching]); + }), [profile.settings]); const playInBackgroundCheckbox = React.useMemo(() => ({ checked: profile.settings.play_in_background, - onClick: (event) => { - console.log(event); + onClick: () => { + core.dispatch({ + action: 'Ctx', + args: { + action: 'UpdateSettings', + args: { + ...profile.settings, + play_in_background: !profile.settings.play_in_background + } + } + }); } - }), [profile.settings.play_in_background]); + }), [profile.settings]); const playInExternalPlayerCheckbox = React.useMemo(() => ({ checked: profile.settings.play_in_external_player, - onClick: (event) => { - console.log(event); + onClick: () => { + core.dispatch({ + action: 'Ctx', + args: { + action: 'UpdateSettings', + args: { + ...profile.settings, + play_in_external_player: !profile.settings.play_in_external_player + } + } + }); } - }), [profile.settings.play_in_external_player]); + }), [profile.settings]); + const hardwareDecodingCheckbox = React.useMemo(() => ({ + checked: profile.settings.hardware_decoding, + onClick: () => { + core.dispatch({ + action: 'Ctx', + args: { + action: 'UpdateSettings', + args: { + ...profile.settings, + hardware_decoding: !profile.settings.hardware_decoding + } + } + }); + } + }), [profile.settings]); return { interfaceLanguageSelect, subtitlesLanguageSelect, @@ -152,7 +194,8 @@ const useProfileSettingsInputs = () => { subtitlesOutlineColorInput, bingeWatchingCheckbox, playInBackgroundCheckbox, - playInExternalPlayerCheckbox + playInExternalPlayerCheckbox, + hardwareDecodingCheckbox }; }; From 8b63aaf0f4180d2a34f0943769a592b236d0db0a Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 16:43:34 +0200 Subject: [PATCH 39/56] not used styles removed --- src/routes/Settings/styles.less | 93 --------------------------------- 1 file changed, 93 deletions(-) diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less index 770834529..e803d4aa1 100644 --- a/src/routes/Settings/styles.less +++ b/src/routes/Settings/styles.less @@ -207,99 +207,6 @@ color: var(--color-surfacelighter); } } - - //////////////////////////////// - - .subscription-container, - .language-picker-container, - .size-picker-container, - .color-picker-container { - flex: none; - align-self: stretch; - display: flex; - flex-direction: row; - align-items: center; - max-width: 35rem; - margin-bottom: 2rem; - - .label-container { - flex: 1; - display: flex; - flex-direction: row; - align-items: center; - margin-right: 1rem; - - .icon { - flex: none; - width: 2.5rem; - height: 2.5rem; - margin-right: 0.5rem; - fill: var(--color-surfacelighter); - } - - .label { - flex: 1; - color: var(--color-surfacelighter); - } - } - - .subscribe-button-container, .multiselect-container, .color-input { - flex: 1; - padding: 1.15rem; - } - } - - .subscription-container { - .subscribe-button-container { - color: var(--color-surfacelighter); - text-align: center; - background-color: var(--color-primary); - - &:hover { - background-color: var(--color-primarylight); - } - } - } - - .language-picker-container, .size-picker-container { - overflow: visible; - - .multiselect-container { - .multiselect-menu-container { - max-height: calc(3.2rem * 7); - overflow: auto; - } - } - } - - .color-picker-container { - .color-input { - height: 3.5rem; - } - } - - .checkbox-option-container { - display: flex; - flex-direction: row-reverse; - align-items: center; - justify-content: flex-end; - max-width: 35rem; - margin-bottom: 2rem; - - .label { - flex: none; - width: calc(50% - 0.5rem); - color: var(--color-surfacelighter); - } - - .checkbox-icon { - flex: none; - width: 2rem; - height: 2rem; - margin-left: calc(25% - 1rem); - fill: var(--color-surfacelighter); - } - } } } } From 539c90c6b89eea0903d3e96566856d49629047ee Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 16:57:01 +0200 Subject: [PATCH 40/56] logout button padding fixed --- src/common/NavBar/NavMenu/styles.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/NavBar/NavMenu/styles.less b/src/common/NavBar/NavMenu/styles.less index c6d5c6dc9..fe3c84d7c 100644 --- a/src/common/NavBar/NavMenu/styles.less +++ b/src/common/NavBar/NavMenu/styles.less @@ -62,7 +62,7 @@ display: flex; flex-direction: row; align-items: center; - padding: 0 1em 1rem 0; + padding: 0 1rem 1rem 0; &:hover, &:focus { outline: none; From 5a7b3222853e080b49c980f8f3f27d8fa2d9a5d2 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 18:02:06 +0200 Subject: [PATCH 41/56] logout button moved bellow the email label --- src/routes/Settings/Settings.js | 30 ++++++------ src/routes/Settings/styles.less | 83 ++++++++++++++++++++++++--------- 2 files changed, 77 insertions(+), 36 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 0fb372e8d..feb6bb180 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -88,22 +88,34 @@ const Settings = () => { `url('${profile.auth.user.avatar}'), url('/images/default_avatar.png')` }} /> -
- {profile.auth === null ? 'Anonymous user' : profile.auth.user.email} +
+
+
+ {profile.auth === null ? 'Anonymous user' : profile.auth.user.email} +
+
+ { + profile.auth !== null ? + + : + null + }
{ profile.auth === null ?
:
} @@ -169,16 +181,6 @@ const Settings = () => {
Privacy Policy
- { - profile.auth !== null ? -
- -
- : - null - }
Player
diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less index e803d4aa1..327799265 100644 --- a/src/routes/Settings/styles.less +++ b/src/routes/Settings/styles.less @@ -95,6 +95,67 @@ margin-bottom: 0; } + &.user-info-option-container { + height: 7rem; + + .avatar-container { + flex: none; + align-self: stretch; + width: 7rem; + margin-right: 1rem; + border-radius: 50%; + background-size: cover; + background-repeat: no-repeat; + background-position: center; + background-origin: content-box; + background-clip: content-box; + } + + .email-logout-container { + flex: 1; + align-self: stretch; + display: flex; + flex-direction: column; + padding: 1rem 0; + + .email-label-container, .logout-button-container { + display: flex; + flex-direction: row; + align-items: center; + } + + .email-label-container { + flex: 1 0 auto; + + .email-label { + flex: 1; + font-size: 1.4rem; + max-height: 2.4em; + color: var(--color-surfacelighter); + } + } + + .logout-button-container { + flex: 0 1 50%; + + &:hover, &:focus { + outline: none; + + .logout-label { + color: var(--color-surfacelighter); + text-decoration: underline; + } + } + + .logout-label { + flex: 1; + max-height: 1.2em; + color: var(--color-surface); + } + } + } + } + .option-name-container, .option-input-container { flex: 1 1 50%; display: flex; @@ -185,28 +246,6 @@ } } } - - .user-info-option-container { - .avatar-container { - flex: none; - width: 6rem; - height: 6rem; - border-radius: 50%; - background-size: cover; - background-repeat: no-repeat; - background-position: center; - background-origin: content-box; - background-clip: content-box; - } - - .email-label { - flex: 1; - font-size: 1.4rem; - max-height: 3.6em; - padding: 0 2rem; - color: var(--color-surfacelighter); - } - } } } } From d4ab63ef56531914a732816ee815f2a368933835 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 18:11:33 +0200 Subject: [PATCH 42/56] general links redirect to where they should --- src/routes/Settings/Settings.js | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index feb6bb180..a50df5ba6 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -45,6 +45,18 @@ const Settings = () => { } }); }, []); + const authenticateTraktOnClick = React.useCallback(() => { + // TODO + }, []); + const importFacebookOnClick = React.useCallback(() => { + // TODO + }, []); + const subscribeCalendarOnClick = React.useCallback(() => { + // TODO + }, []); + const exportDataOnClick = React.useCallback(() => { + // TODO + }, []); const reloadStreamingServer = React.useCallback(() => { core.dispatch({ action: 'Ctx', @@ -120,7 +132,7 @@ const Settings = () => {
}
- @@ -138,46 +150,46 @@ const Settings = () => {
Trakt Scrobbling
-
-
Facebook import
+
Facebook
-
Calendar
-
-
-
-
-
From 840dbd925b58bc7828b98de6cafc1955f3097586 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 22:38:59 +0200 Subject: [PATCH 43/56] scrollspy optimized --- src/routes/Settings/Settings.js | 56 ++++++++++++++++++++++++++------- src/routes/Settings/styles.less | 5 ++- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index a50df5ba6..1051c5156 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -1,5 +1,6 @@ const React = require('react'); const classnames = require('classnames'); +const throttle = require('lodash.throttle'); const Icon = require('stremio-icons/dom'); const { useServices } = require('stremio/services'); const { Button, Checkbox, NavBar, Multiselect, ColorInput, useProfile, useStreamingServer } = require('stremio/common'); @@ -31,12 +32,6 @@ const Settings = () => { cacheSizeSelect, torrentProfileSelect } = useStreamingServerSettingsInputs(); - const [section, setSection] = React.useState(GENERAL_SECTION); - const sectionsContainerRef = React.useRef(null); - const sectionsContainerOnScorll = React.useCallback((event) => { - }, []); - const sideMenuButtonOnClick = React.useCallback((event) => { - }, []); const logoutButtonOnClick = React.useCallback(() => { core.dispatch({ action: 'Ctx', @@ -65,6 +60,43 @@ const Settings = () => { } }); }, []); + const sectionsContainerRef = React.useRef(null); + const generalSectionRef = React.useRef(null); + const playerSectionRef = React.useRef(null); + const streamingServerSectionRef = React.useRef(null); + const sections = React.useMemo(() => ([ + { ref: generalSectionRef, id: GENERAL_SECTION }, + { ref: playerSectionRef, id: PLAYER_SECTION }, + { ref: streamingServerSectionRef, id: STREAMING_SECTION }, + ]), []); + const [selectedSectionId, setSelectedSectionId] = React.useState(sections[0].id); + const updateSelectedSectionId = React.useCallback(() => { + if (sectionsContainerRef.current.scrollTop + sectionsContainerRef.current.clientHeight === sectionsContainerRef.current.scrollHeight) { + setSelectedSectionId(sections[sections.length - 1].id); + } else { + for (let i = sections.length - 1; i >= 0; i--) { + if (sections[i].ref.current.offsetTop - sectionsContainerRef.current.offsetTop <= sectionsContainerRef.current.scrollTop) { + setSelectedSectionId(sections[i].id); + break; + } + } + } + }, []); + const sideMenuButtonOnClick = React.useCallback((event) => { + const section = sections.find((section) => { + return section.id === event.currentTarget.dataset.section; + }); + sectionsContainerRef.current.scrollTo({ + top: section.ref.current.offsetTop - sectionsContainerRef.current.offsetTop, + behavior: 'smooth' + }); + }, []); + const sectionsContainerOnScorll = React.useCallback(throttle(() => { + updateSelectedSectionId(); + }, 50), []); + React.useEffect(() => { + updateSelectedSectionId(); + }, []); return (
{ />
- - -
-
+
General
{
-
+
Player
@@ -278,7 +310,7 @@ const Settings = () => { />
-
+
Streaming Server
+ {process.env.version}
diff --git a/webpack.config.js b/webpack.config.js index cf679670e..6c5e81f17 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,6 +5,7 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin'); +const pachageJson = require('./package.json'); module.exports = (env, argv) => ({ entry: './src/index.js', @@ -130,6 +131,7 @@ module.exports = (env, argv) => ({ plugins: [ new webpack.EnvironmentPlugin({ DEBUG: argv.mode !== 'production', + version: pachageJson.version, ...env }), new webpack.ProgressPlugin(), From a49b8b3211dab762e8a097c02513623fc67ed319 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Tue, 11 Feb 2020 23:15:57 +0200 Subject: [PATCH 45/56] move interface language select bellow subscription buttons --- src/routes/Settings/Settings.js | 27 +++++++++++++++++---------- src/routes/Settings/styles.less | 9 +++++++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 0be6eb712..b542fc072 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -118,7 +118,14 @@ const Settings = () => { - {process.env.version} +
+
App Version: {process.env.version}
+ { + streaminServer.type === 'Ready' ? +
Server Version: {streaminServer.settings.serverVersion}
+ : + null + }
@@ -170,15 +177,6 @@ const Settings = () => {
Addons
-
-
-
Interface language
-
- -
Trakt Scrobbling
@@ -206,6 +204,15 @@ const Settings = () => {
Subscribe
+
+
+
Interface language
+
+ +
+
+ +
-
App Version: {process.env.version}
+
App Version: {process.env.VERSION}
{ streaminServer.type === 'Ready' ?
Server Version: {streaminServer.settings.serverVersion}
diff --git a/webpack.config.js b/webpack.config.js index 6c5e81f17..ae2f533da 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -131,7 +131,7 @@ module.exports = (env, argv) => ({ plugins: [ new webpack.EnvironmentPlugin({ DEBUG: argv.mode !== 'production', - version: pachageJson.version, + VERSION: pachageJson.version, ...env }), new webpack.ProgressPlugin(), From 3d840507a9a0cf840f05867a6cf05cf123efd05b Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 12 Feb 2020 09:11:57 +0200 Subject: [PATCH 48/56] commit hash added to sourcecode link --- src/routes/Settings/Settings.js | 2 +- webpack.config.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index bffc0a6c1..e00b92a99 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -224,7 +224,7 @@ const Settings = () => {
-
diff --git a/webpack.config.js b/webpack.config.js index ae2f533da..9676769b4 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,4 +1,5 @@ const path = require('path'); +const child_process = require('child_process'); const webpack = require('webpack'); const HtmlWebPackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); @@ -132,6 +133,7 @@ module.exports = (env, argv) => ({ new webpack.EnvironmentPlugin({ DEBUG: argv.mode !== 'production', VERSION: pachageJson.version, + COMMIT_HASH: child_process.execSync('git rev-parse HEAD').toString(), ...env }), new webpack.ProgressPlugin(), From 292f4d59c680e7e2c2e5f77da1fa1171a932e023 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 12 Feb 2020 10:36:03 +0200 Subject: [PATCH 49/56] useSearch adapted to the new core api --- src/routes/Search/useSearch.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/Search/useSearch.js b/src/routes/Search/useSearch.js index 078d53840..a4bb60949 100644 --- a/src/routes/Search/useSearch.js +++ b/src/routes/Search/useSearch.js @@ -13,7 +13,7 @@ const mapSearchStateWithCtx = (search, ctx) => { ''; const selected = search.selected; const catalog_resources = search.catalog_resources.map((catalog_resource) => { - catalog_resource.addon_name = ctx.content.addons.reduce((addon_name, addon) => { + catalog_resource.addon_name = ctx.profile.addons.reduce((addon_name, addon) => { if (addon.transportUrl === catalog_resource.request.base) { return addon.manifest.name; } @@ -41,7 +41,7 @@ const useSearch = (queryParams) => { return { action: 'Load', args: { - load: 'CatalogsWithExtra', + model: 'CatalogsWithExtra', args: { extra: [ ['search', queryParams.get('search')] From 9871e438e8b91eab06206adcea09f7b763460399 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 12 Feb 2020 10:59:55 +0200 Subject: [PATCH 50/56] bind backspace key to back button --- src/services/KeyboardNavigation/KeyboardNavigation.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/services/KeyboardNavigation/KeyboardNavigation.js b/src/services/KeyboardNavigation/KeyboardNavigation.js index bd46169d2..cfcfe58f3 100644 --- a/src/services/KeyboardNavigation/KeyboardNavigation.js +++ b/src/services/KeyboardNavigation/KeyboardNavigation.js @@ -12,6 +12,14 @@ function KeyboardNavigation() { if (tab) { event.preventDefault(); window.location = tab.href; + return; + } + + if (event.target.tag !== 'INPUT') { + if (event.key === 'Backspace') { + window.history.back(); + return; + } } } function start() { From 6a943415f172a430cc23178efc3a71c3d2bb925d Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 12 Feb 2020 12:15:48 +0200 Subject: [PATCH 51/56] back key binding fixed --- src/services/KeyboardNavigation/KeyboardNavigation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/KeyboardNavigation/KeyboardNavigation.js b/src/services/KeyboardNavigation/KeyboardNavigation.js index cfcfe58f3..c3ad4a65b 100644 --- a/src/services/KeyboardNavigation/KeyboardNavigation.js +++ b/src/services/KeyboardNavigation/KeyboardNavigation.js @@ -15,7 +15,7 @@ function KeyboardNavigation() { return; } - if (event.target.tag !== 'INPUT') { + if (event.target.tagName !== 'INPUT') { if (event.key === 'Backspace') { window.history.back(); return; From e78c3f225a142a9eec655d5418c38c73290285fe Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 12 Feb 2020 12:19:28 +0200 Subject: [PATCH 52/56] white space removed from error message --- src/routes/Search/Search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Search/Search.js b/src/routes/Search/Search.js index b6669f298..7abcdbe24 100644 --- a/src/routes/Search/Search.js +++ b/src/routes/Search/Search.js @@ -42,7 +42,7 @@ const Search = ({ queryParams }) => { search.catalog_resources.length === 0 ?
-
No addons were requested for catalogs
+
No addons were requested for catalogs
: From 521678884f30c2090a6c049fd4f518d683d60659 Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Wed, 12 Feb 2020 12:19:52 +0200 Subject: [PATCH 53/56] height limit removed from message container --- src/routes/Search/styles.less | 1 - 1 file changed, 1 deletion(-) diff --git a/src/routes/Search/styles.less b/src/routes/Search/styles.less index 565cc3a94..bda1befdd 100644 --- a/src/routes/Search/styles.less +++ b/src/routes/Search/styles.less @@ -48,7 +48,6 @@ flex-direction: row; align-items: flex-start; justify-content: center; - max-height: calc(100% - 10rem); margin: 5rem; .message-content { From a3abce44c98c0ba910c49a2006594cbd4ba6493f Mon Sep 17 00:00:00 2001 From: svetlagasheva Date: Wed, 12 Feb 2020 12:44:26 +0200 Subject: [PATCH 54/56] message removed; text-input styles changed --- .../Intro/PasswordResetModal/PasswordResetModal.js | 1 - src/routes/Intro/PasswordResetModal/styles.less | 10 +--------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js index 6782eac46..7877492bb 100644 --- a/src/routes/Intro/PasswordResetModal/PasswordResetModal.js +++ b/src/routes/Intro/PasswordResetModal/PasswordResetModal.js @@ -48,7 +48,6 @@ const PasswordResetModal = ({ email, onCloseRequest }) => { }, [routeFocused]); return ( -
Enter your email
Date: Thu, 13 Feb 2020 13:58:31 +0200 Subject: [PATCH 55/56] Board dapted to changes in core --- src/routes/Board/useBoard.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/Board/useBoard.js b/src/routes/Board/useBoard.js index d5cc92d17..a16468292 100644 --- a/src/routes/Board/useBoard.js +++ b/src/routes/Board/useBoard.js @@ -9,7 +9,7 @@ const initBoardState = () => ({ const mapBoardStateWithCtx = (board, ctx) => { const selected = board.selected; const catalog_resources = board.catalog_resources.map((catalog_resource) => { - catalog_resource.addon_name = ctx.content.addons.reduce((addon_name, addon) => { + catalog_resource.addon_name = ctx.profile.addons.reduce((addon_name, addon) => { if (addon.transportUrl === catalog_resource.request.base) { return addon.manifest.name; } @@ -35,7 +35,7 @@ const useBoard = () => { const loadBoardAction = React.useMemo(() => ({ action: 'Load', args: { - load: 'CatalogsWithExtra', + model: 'CatalogsWithExtra', args: { extra: [] } } }), []); From 42846d7392010fecfcd8252ddf59eacdccd2fcbe Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Thu, 13 Feb 2020 14:30:47 +0200 Subject: [PATCH 56/56] Discover adapted to changes in core --- src/routes/Discover/Discover.js | 2 +- src/routes/Discover/useDiscover.js | 32 ++++++++++-------- src/routes/Discover/useSelectableInputs.js | 38 +++++++++------------- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/routes/Discover/Discover.js b/src/routes/Discover/Discover.js index d314c2d68..78116cd7e 100644 --- a/src/routes/Discover/Discover.js +++ b/src/routes/Discover/Discover.js @@ -74,7 +74,7 @@ const Discover = ({ urlParams, queryParams }) => { }
{ - discover.catalog_resource != null && !state.ctx.content.addons.some((addon) => addon.transportUrl === discover.catalog_resource.request.base) ? + discover.catalog_resource !== null && !state.ctx.profile.addons.some((addon) => addon.transportUrl === discover.catalog_resource.request.base) ?
This addon is not installed. Install now?