From 7d04ce41ce257f98055b46e81a1afda468eec22b Mon Sep 17 00:00:00 2001 From: nklhrstv Date: Mon, 26 Oct 2020 14:13:59 +0200 Subject: [PATCH] addons route adapted to changes in core --- src/routes/Addons/Addons.js | 75 +++++---------- .../Addons/useAddonDetailsTransportUrl.js | 20 ++++ src/routes/Addons/useInstalledAddons.js | 36 ++----- src/routes/Addons/useRemoteAddons.js | 46 ++------- src/routes/Addons/useSelectableInputs.js | 96 ++++++++----------- 5 files changed, 99 insertions(+), 174 deletions(-) create mode 100644 src/routes/Addons/useAddonDetailsTransportUrl.js diff --git a/src/routes/Addons/Addons.js b/src/routes/Addons/Addons.js index 3615dd61b..27f80bec6 100644 --- a/src/routes/Addons/Addons.js +++ b/src/routes/Addons/Addons.js @@ -2,57 +2,27 @@ const React = require('react'); const PropTypes = require('prop-types'); -const { useRouteFocused } = require('stremio-router'); const Icon = require('@stremio/stremio-icons/dom'); const { AddonDetailsModal, Button, Image, Multiselect, MainNavBars, TextInput, SearchBar, SharePrompt, ModalDialog, useBinaryState } = require('stremio/common'); const Addon = require('./Addon'); const useInstalledAddons = require('./useInstalledAddons'); const useRemoteAddons = require('./useRemoteAddons'); +const useAddonDetailsTransportUrl = require('./useAddonDetailsTransportUrl'); const useSelectableInputs = require('./useSelectableInputs'); const styles = require('./styles'); const Addons = ({ urlParams, queryParams }) => { - const routeFocused = useRouteFocused(); - const navigate = React.useCallback((args) => { - if (!routeFocused) { - return; - } - - const nextPath = args.hasOwnProperty('request') ? - `/${encodeURIComponent(args.request.path.type_name)}/${encodeURIComponent(args.request.base)}/${encodeURIComponent(args.request.path.id)}` - : - args.hasOwnProperty('type_name') ? - typeof args.type_name === 'string' ? - `/${encodeURIComponent(args.type_name)}` - : - '' - : - typeof urlParams.type === 'string' && typeof urlParams.transportUrl === 'string' && typeof urlParams.catalogId === 'string' ? - `/${encodeURIComponent(urlParams.type)}/${encodeURIComponent(urlParams.transportUrl)}/${encodeURIComponent(urlParams.catalogId)}` - : - ''; - const nextQueryParams = new URLSearchParams(queryParams); - if (args.hasOwnProperty('detailsTransportUrl')) { - if (typeof args.detailsTransportUrl === 'string') { - nextQueryParams.set('addon', args.detailsTransportUrl); - } else { - nextQueryParams.delete('addon'); - } - } - - window.location = `#/addons${nextPath}?${nextQueryParams}`; - }, [routeFocused, urlParams, queryParams]); const installedAddons = useInstalledAddons(urlParams); const remoteAddons = useRemoteAddons(urlParams); - const detailsTransportUrl = queryParams.get('addon'); - const selectInputs = useSelectableInputs(installedAddons, remoteAddons, navigate); + const [addonDetailsTransportUrl, setAddonDetailsTransportUrl] = useAddonDetailsTransportUrl(urlParams, queryParams); + const selectInputs = useSelectableInputs(installedAddons, remoteAddons); const [addAddonModalOpen, openAddAddonModal, closeAddAddonModal] = useBinaryState(false); const addAddonUrlInputRef = React.useRef(null); const addAddonOnSubmit = React.useCallback(() => { if (addAddonUrlInputRef.current !== null) { - navigate({ detailsTransportUrl: addAddonUrlInputRef.current.value }); + setAddonDetailsTransportUrl(addAddonUrlInputRef.current.value); } - }, [navigate]); + }, [setAddonDetailsTransportUrl]); const addAddonModalButtons = React.useMemo(() => { return [ { @@ -75,11 +45,6 @@ const Addons = ({ urlParams, queryParams }) => { setSearch(event.currentTarget.value); }, []); const [sharedAddon, setSharedAddon] = React.useState(null); - const renderLogoFallback = React.useMemo(() => () => { - return ( - - ); - }, []); const clearSharedAddon = React.useCallback(() => { setSharedAddon(null); }, []); @@ -87,11 +52,11 @@ const Addons = ({ urlParams, queryParams }) => { setSharedAddon(event.dataset.addon); }, []); const onAddonToggle = React.useCallback((event) => { - navigate({ detailsTransportUrl: event.dataset.addon.transportUrl }); - }, [navigate]); + setAddonDetailsTransportUrl(event.dataset.addon.transportUrl); + }, [setAddonDetailsTransportUrl]); const closeAddonDetails = React.useCallback(() => { - navigate({ detailsTransportUrl: null }); - }, [navigate]); + setAddonDetailsTransportUrl(null); + }, [setAddonDetailsTransportUrl]); const searchFilterPredicate = React.useCallback((addon) => { return search.length === 0 || ( @@ -99,6 +64,11 @@ const Addons = ({ urlParams, queryParams }) => { (typeof addon.manifest.description === 'string' && addon.manifest.description.toLowerCase().includes(search.toLowerCase())) ); }, [search]); + const renderLogoFallback = React.useMemo(() => () => { + return ( + + ); + }, []); React.useLayoutEffect(() => { closeAddAddonModal(); setSearch(''); @@ -129,19 +99,19 @@ const Addons = ({ urlParams, queryParams }) => { { installedAddons.selected !== null ? - installedAddons.type_names.length === 0 ? + installedAddons.selectable.types.length === 0 ?
No addons ware installed!
: - installedAddons.addons.length === 0 ? + installedAddons.catalog.length === 0 ?
No addons ware installed for that type!
:
{ - installedAddons.addons + installedAddons.catalog .filter(searchFilterPredicate) .map((addon, index) => ( {
: remoteAddons.selected !== null ? - remoteAddons.catalog_resource.content.type === 'Err' ? + remoteAddons.catalog.content.type === 'Err' ?
Addons could not be loaded!
: - remoteAddons.catalog_resource.content.type === 'Loading' ? + remoteAddons.catalog.content.type === 'Loading' ?
Loading!
:
{ - remoteAddons.catalog_resource.content.content + remoteAddons.catalog.content.content .filter(searchFilterPredicate) .map((addon, index) => ( { null } { - typeof detailsTransportUrl === 'string' ? + typeof addonDetailsTransportUrl === 'string' ? : @@ -266,6 +236,7 @@ const Addons = ({ urlParams, queryParams }) => { Addons.propTypes = { urlParams: PropTypes.shape({ + path: PropTypes.string, transportUrl: PropTypes.string, catalogId: PropTypes.string, type: PropTypes.string diff --git a/src/routes/Addons/useAddonDetailsTransportUrl.js b/src/routes/Addons/useAddonDetailsTransportUrl.js new file mode 100644 index 000000000..c5cc183eb --- /dev/null +++ b/src/routes/Addons/useAddonDetailsTransportUrl.js @@ -0,0 +1,20 @@ +const React = require('react'); + +const useAddonDetailsTransportUrl = (urlParams, queryParams) => { + const transportUrl = React.useMemo(() => { + return queryParams.get('addon'); + }, [queryParams]); + const setTransportUrl = React.useCallback((transportUrl) => { + const nextQueryParams = new URLSearchParams(queryParams); + if (typeof transportUrl === 'string') { + nextQueryParams.set('addon', transportUrl); + } else { + nextQueryParams.delete('addon'); + } + + window.location = `#${urlParams.path}?${nextQueryParams}`; + }, [urlParams, queryParams]); + return [transportUrl, setTransportUrl]; +}; + +module.exports = useAddonDetailsTransportUrl; diff --git a/src/routes/Addons/useInstalledAddons.js b/src/routes/Addons/useInstalledAddons.js index b4e0b6fca..9df92ebe7 100644 --- a/src/routes/Addons/useInstalledAddons.js +++ b/src/routes/Addons/useInstalledAddons.js @@ -4,38 +4,21 @@ const React = require('react'); const { useServices } = require('stremio/services'); const { useModelState } = require('stremio/common'); -const mapAddonsState = (installedAddons) => { - const selected = installedAddons.selected; - const type_names = installedAddons.type_names; - const addons = installedAddons.addons.map((addon) => ({ - transportUrl: addon.transportUrl, - installed: true, - manifest: { - id: addon.manifest.id, - name: addon.manifest.name, - version: addon.manifest.version, - logo: addon.manifest.logo, - description: addon.manifest.description, - types: addon.manifest.types - } - })); - return { selected, type_names, addons }; -}; - const useInstalledAddons = (urlParams) => { const { core } = useServices(); - const initAddonsState = React.useMemo(() => { - const installedAddons = core.transport.getState('installed_addons'); - return mapAddonsState(installedAddons); + const init = React.useMemo(() => { + return core.transport.getState('installed_addons'); }, []); - const loadAddonsAction = React.useMemo(() => { + const action = React.useMemo(() => { if (typeof urlParams.transportUrl !== 'string' && typeof urlParams.catalogId !== 'string') { return { action: 'Load', args: { model: 'InstalledAddonsWithFilters', args: { - type_name: urlParams.type + request: { + type: typeof urlParams.type === 'string' ? urlParams.type : null + } } } }; @@ -45,12 +28,7 @@ const useInstalledAddons = (urlParams) => { }; } }, [urlParams]); - return useModelState({ - model: 'installed_addons', - action: loadAddonsAction, - map: mapAddonsState, - init: initAddonsState - }); + return useModelState({ model: 'installed_addons', action, init }); }; module.exports = useInstalledAddons; diff --git a/src/routes/Addons/useRemoteAddons.js b/src/routes/Addons/useRemoteAddons.js index 41314154d..dff86b77d 100644 --- a/src/routes/Addons/useRemoteAddons.js +++ b/src/routes/Addons/useRemoteAddons.js @@ -3,44 +3,17 @@ const React = require('react'); const { useModelState } = require('stremio/common'); -const initAddonsState = () => ({ +const init = () => ({ selected: null, selectable: { - types: [], - catalogs: [] + catalogs: [], + types: [] }, - catalog_resource: null + catalog: null, }); -const mapAddonsStateWithCtx = (addons, ctx) => { - const selected = addons.selected; - const selectable = addons.selectable; - const catalog_resource = addons.catalog_resource !== null && addons.catalog_resource.content.type === 'Ready' ? - { - request: addons.catalog_resource.request, - 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), - manifest: { - id: addon.manifest.id, - name: addon.manifest.name, - version: addon.manifest.version, - logo: addon.manifest.logo, - description: addon.manifest.description, - types: addon.manifest.types - } - })) - } - } - : - addons.catalog_resource; - return { selected, selectable, catalog_resource }; -}; - const useRemoteAddons = (urlParams) => { - const loadAddonsAction = React.useMemo(() => { + const action = React.useMemo(() => { if (typeof urlParams.type === 'string' && typeof urlParams.transportUrl === 'string' && typeof urlParams.catalogId === 'string') { return { action: 'Load', @@ -51,7 +24,7 @@ const useRemoteAddons = (urlParams) => { base: urlParams.transportUrl, path: { resource: 'addon_catalog', - type_name: urlParams.type, + type: urlParams.type, id: urlParams.catalogId, extra: [] } @@ -65,12 +38,7 @@ const useRemoteAddons = (urlParams) => { }; } }, [urlParams]); - return useModelState({ - model: 'remote_addons', - action: loadAddonsAction, - mapWithCtx: mapAddonsStateWithCtx, - init: initAddonsState - }); + return useModelState({ model: 'remote_addons', action, init }); }; module.exports = useRemoteAddons; diff --git a/src/routes/Addons/useSelectableInputs.js b/src/routes/Addons/useSelectableInputs.js index dc2082f6e..7af856c51 100644 --- a/src/routes/Addons/useSelectableInputs.js +++ b/src/routes/Addons/useSelectableInputs.js @@ -2,91 +2,79 @@ const React = require('react'); -const ALL_TYPES_OPTION = { - value: null, - label: 'All' -}; - -const mapSelectableInputs = (installedAddons, remoteAddons, navigate) => { +const mapSelectableInputs = (installedAddons, remoteAddons) => { const catalogSelect = { title: 'Select catalog', options: remoteAddons.selectable.catalogs - .map(({ name, request }) => ({ - value: JSON.stringify(request), - label: name + .map(({ catalog, addonName, deepLinks }) => ({ + value: deepLinks.addons, + label: catalog, + title: `${catalog} (${addonName})` })) - .concat({ - value: JSON.stringify(ALL_TYPES_OPTION.value), - label: 'Installed' - }), + .concat(installedAddons.selectable.catalogs.map(({ catalog, deepLinks }) => ({ + value: deepLinks.addons, + label: catalog, + title: catalog + }))), selected: remoteAddons.selectable.catalogs - .filter(({ request }) => { - return remoteAddons.selected !== null && - remoteAddons.selected.request.base === request.base && - remoteAddons.selected.request.path.id === request.path.id; - }) - .map(({ request }) => JSON.stringify(request)) - .concat(installedAddons.selected !== null ? JSON.stringify(ALL_TYPES_OPTION.value) : []), - onSelect: (event) => { - const value = JSON.parse(event.value); - if (value === ALL_TYPES_OPTION.value) { - navigate({ type_name: value }); - return; + .concat(installedAddons.selectable.catalogs) + .filter(({ selected }) => selected) + .map(({ deepLinks }) => deepLinks.addons), + renderLabelText: remoteAddons.selected !== null ? + () => { + const selectableCatalog = remoteAddons.selectable.catalogs + .find(({ request }) => request.path.id === remoteAddons.selected.request.path.id); + return selectableCatalog ? selectableCatalog.catalog : remoteAddons.selected.request.path.id; } - - navigate({ request: value }); + : + null, + onSelect: (event) => { + window.location = event.value; } }; const typeSelect = { title: 'Select type', options: installedAddons.selected !== null ? - [{ label: ALL_TYPES_OPTION.label, value: JSON.stringify(ALL_TYPES_OPTION.value) }].concat(installedAddons.type_names.map((type_name) => ({ - value: JSON.stringify(type_name), - label: type_name - }))) + installedAddons.selectable.types.map(({ type, deepLinks }) => ({ + value: deepLinks.addons, + label: type !== null ? type : 'All' + })) : - remoteAddons.selectable.types.map(({ name, request }) => ({ - value: JSON.stringify(request), - label: name + remoteAddons.selectable.types.map(({ type, deepLinks }) => ({ + value: deepLinks.addons, + label: type })), selected: installedAddons.selected !== null ? - [JSON.stringify(installedAddons.selected.type_name)] + installedAddons.selectable.types + .filter(({ selected }) => selected) + .map(({ deepLinks }) => deepLinks.addons) : remoteAddons.selectable.types - .filter(({ request }) => { - return remoteAddons.selected !== null && - remoteAddons.selected.request.path.type_name === request.path.type_name; - }) - .map(({ request }) => JSON.stringify(request)), + .filter(({ selected }) => selected) + .map(({ deepLinks }) => deepLinks.addons), renderLabelText: () => { return installedAddons.selected !== null ? - installedAddons.selected.type_name === ALL_TYPES_OPTION.value ? - ALL_TYPES_OPTION.label + installedAddons.selected.request.type === null ? + 'All' : - installedAddons.selected.type_name + installedAddons.selected.request.type : remoteAddons.selected !== null ? - remoteAddons.selected.request.path.type_name + remoteAddons.selected.request.path.type : typeSelect.title; }, onSelect: (event) => { - const value = JSON.parse(event.value); - if (value === ALL_TYPES_OPTION.value || typeof value === 'string') { - navigate({ type_name: value }); - return; - } - - navigate({ request: value }); + window.location = event.value; } }; return [catalogSelect, typeSelect]; }; -const useSelectableInputs = (installedAddons, remoteAddons, navigate) => { +const useSelectableInputs = (installedAddons, remoteAddons) => { const selectableInputs = React.useMemo(() => { - return mapSelectableInputs(installedAddons, remoteAddons, navigate); - }, [installedAddons, remoteAddons, navigate]); + return mapSelectableInputs(installedAddons, remoteAddons); + }, [installedAddons, remoteAddons]); return selectableInputs; };