diff --git a/src/routes/Addons/Addons.js b/src/routes/Addons/Addons.js index 0a8152a17..6e47ee363 100644 --- a/src/routes/Addons/Addons.js +++ b/src/routes/Addons/Addons.js @@ -1,49 +1,69 @@ const React = require('react'); const Icon = require('stremio-icons/dom'); -const { Button, Multiselect, NavBar, TextInput, SharePrompt, ModalDialog } = require('stremio/common'); +const { Button, Multiselect, NavBar, TextInput, SharePrompt, ModalDialog, useBinaryState } = require('stremio/common'); const Addon = require('./Addon'); -const AddonPrompt = require('./AddonPrompt'); const useAddons = require('./useAddons'); -const useSelectedAddon = require('./useSelectedAddon'); +const useSelectableInputs = require('./useSelectableInputs'); const styles = require('./styles'); const Addons = ({ urlParams, queryParams }) => { - const inputRef = React.useRef(null); - const [query, setQuery] = React.useState(''); - const queryOnChange = React.useCallback((event) => { - setQuery(event.currentTarget.value); - }, []); - const [[addons, dropdowns, setSelectedAddon, installedAddons, error], installSelectedAddon, uninstallSelectedAddon] = useAddons(urlParams, queryParams); - const [addAddonModalOpened, setAddAddonModalOpened] = React.useState(false); - const [selectedAddon, clearSelectedAddon] = useSelectedAddon(queryParams.get('addon')); - const [sharedAddon, setSharedAddon] = React.useState(null); - const onAddAddonButtonClicked = React.useCallback(() => { - setAddAddonModalOpened(true); - }, []); - const onAddButtonClicked = React.useCallback(() => { - if (inputRef.current.value.length > 0) { - setSelectedAddon(inputRef.current.value); - setAddAddonModalOpened(false); + const addons = useAddons(urlParams); + const selectInputs = useSelectableInputs(addons); + const [addAddonModalOpen, openAddAddonModal, closeAddAddonModal] = useBinaryState(false); + const addAddonUrlInputRef = React.useRef(null); + const addAddonOnSubmit = React.useCallback(() => { + if (addAddonUrlInputRef.current !== null) { + // TODO install addon } - }, [setSelectedAddon]); - const installedAddon = React.useCallback((currentAddon) => { - return installedAddons.some((installedAddon) => installedAddon.transportUrl === currentAddon.transportUrl); - }, [installedAddons]); - const toggleAddon = React.useCallback(() => { - installedAddon(selectedAddon) ? uninstallSelectedAddon(selectedAddon) : installSelectedAddon(selectedAddon); - clearSelectedAddon(); - }, [selectedAddon]); + }, []); + const addAddonModalButtons = React.useMemo(() => { + return [ + { + className: styles['cancel-button'], + label: 'Cancel', + props: { + onClick: closeAddAddonModal + } + }, + { + label: 'Add', + props: { + onClick: addAddonOnSubmit + } + } + ]; + }, []); + const [search, setSearch] = React.useState(''); + const searchInputOnChange = React.useCallback((event) => { + setSearch(event.currentTarget.value); + }, []); + const [sharedTransportUrl, setSharedTransportUrl] = React.useState(null); + const shareModalOnClose = React.useCallback(() => { + setSharedTransportUrl(null); + }, []); + const onAddonShare = React.useCallback((event) => { + setSharedTransportUrl(event.dataset.transportUrl); + }, []); + React.useLayoutEffect(() => { + closeAddAddonModal(null); + setSearch(''); + setSharedTransportUrl(null); + }, [urlParams, queryParams]); return (
-
- - {dropdowns.map((dropdown, index) => ( - + {selectInputs.map((selectInput, index) => ( + ))}
-
- { - error !== null ? + { + addons.selectable.catalogs.length === 0 && addons.catalog_resource === null ? +
+ No addons +
+ : + addons.catalog_resource === null ?
- {error.type}{error.type === 'Other' ? ` - ${error.content}` : null} + No select
: - Array.isArray(addons) ? - addons.filter((addon) => query.length === 0 || - ((typeof addon.manifest.name === 'string' && addon.manifest.name.toLowerCase().includes(query.toLowerCase())) || - (typeof addon.manifest.description === 'string' && addon.manifest.description.toLowerCase().includes(query.toLowerCase())) - )) - .map((addon, index) => ( - setSelectedAddon(addon.transportUrl)} - onShareButtonClicked={() => setSharedAddon(addon)} - /> - )) - : + addons.catalog_resource.content.type === 'Err' ?
- Loading + Addons could not be loaded
- } -
- { - addAddonModalOpened ? - setAddAddonModalOpened(false) - } - }, - { - label: 'Add', - props: { - title: 'Add', - onClick: onAddButtonClicked - } - } - ]} - onCloseRequest={() => setAddAddonModalOpened(false)} - > - - - : - null - } - { - selectedAddon !== null ? - - - - : - null - } - { - sharedAddon !== null ? - setSharedAddon(null)}> - setSharedAddon(null)} - /> - - : - null + : + addons.catalog_resource.content.type === 'Loading' ? +
+ Loading +
+ : +
+ { + addons.catalog_resource.content.content + .filter((addon) => { + return search.length === 0 || + ( + (typeof addon.manifest.name === 'string' && addon.manifest.name.toLowerCase().includes(search.toLowerCase())) || + (typeof addon.manifest.description === 'string' && addon.manifest.description.toLowerCase().includes(search.toLowerCase())) + ); + }) + .map((addon, index) => ( + + )) + } +
}
+ { + addAddonModalOpen ? + + + + : + null + } + { + typeof sharedTransportUrl === 'string' ? + + + + : + null + }
); }; diff --git a/src/routes/Addons/styles.less b/src/routes/Addons/styles.less index ff3b7f2c1..b17fbfd46 100644 --- a/src/routes/Addons/styles.less +++ b/src/routes/Addons/styles.less @@ -1,3 +1,7 @@ +:import('~stremio/common/Multiselect/styles.less') { + multiselect-menu-container: menu-container; +} + .addons-container { display: flex; flex-direction: column; @@ -16,11 +20,12 @@ display: flex; flex-direction: column; - .top-bar-container { + .selectable-inputs-container { flex: none; + align-self: stretch; display: flex; flex-direction: row; - margin: 2rem; + padding: 1.5rem; overflow: visible; .add-button-container { @@ -30,7 +35,7 @@ align-items: center; height: 3rem; max-width: 15rem; - margin-right: 1rem; + margin-right: 1.5rem; padding: 0 1rem; background-color: var(--color-signal5); @@ -40,8 +45,8 @@ .icon { flex: none; - width: 1.5rem; - height: 1.5rem; + width: 1.2rem; + height: 1.2rem; margin-right: 1rem; fill: var(--color-surfacelighter); } @@ -56,12 +61,17 @@ } } - .dropdown { + .select-input-container { flex-grow: 0; flex-shrink: 1; flex-basis: 15rem; height: 3rem; - margin-right: 1rem; + margin-right: 1.5rem; + + .multiselect-menu-container { + max-height: calc(3.2rem * 7); + overflow: auto; + } } .search-bar-container { @@ -72,7 +82,6 @@ flex-direction: row; align-items: center; height: 3rem; - margin-right: 1rem; padding: 0 1rem; background-color: var(--color-backgroundlighter); cursor: text; @@ -82,7 +91,7 @@ } .icon { - display: block; + flex: none; width: 1.2rem; height: 1.2rem; margin-right: 1rem; @@ -91,7 +100,6 @@ .search-input { flex: 1; - align-self: stretch; color: var(--color-surfacelighter); &::placeholder { @@ -103,32 +111,31 @@ } } - .addons-list-container { + .message-container { flex: 1; align-self: stretch; - padding: 0 2rem; + padding: 0 1.5rem; + font-size: 2rem; + color: var(--color-surfacelighter); + } + + .addons-container { + flex: 1; + align-self: stretch; + padding: 0 1.5rem; overflow-y: auto; .addon { - width: 100%; - margin-bottom: 2rem; - } - - .message-container { - padding: 0 2rem; - font-size: 2rem; - color: var(--color-surfacelighter); + margin-bottom: 1.5rem; } } } } -.add-addon-prompt-container { - .url-content { - flex: 1; - width: 100%; - padding: 0.5rem; - font-size: 0.9rem; +.add-addon-modal-container { + .addon-url-input { + width: 25rem; + padding: 0.5rem 1rem; color: var(--color-surfacedark); border: thin solid var(--color-surface); } @@ -138,8 +145,8 @@ } } -.addon-prompt-container { - .cancel-button { - background-color: var(--color-surfacedark); +.share-modal-container { + .share-prompt-container { + width: 25rem; } } \ No newline at end of file diff --git a/src/routes/Addons/useAddons.js b/src/routes/Addons/useAddons.js index b29b23cd4..aad7086bc 100644 --- a/src/routes/Addons/useAddons.js +++ b/src/routes/Addons/useAddons.js @@ -15,8 +15,29 @@ const initAddonsState = () => ({ const mapAddonsStateWithCtx = (addons, ctx) => { const selectable = addons.selectable; - const catalog_resource = addons.catalog_resource; - // TODO add MY catalogId replace catalog content if resource catalog id is MY + // 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, + content: { + ...addons.catalog_resource.content, + content: addons.catalog_resource.content.content.map((descriptor) => ({ + transportUrl: descriptor.transportUrl, + installed: ctx.content.addons.some((addon) => addon.transportUrl === descriptor.transportUrl), + manifest: { + id: descriptor.manifest.id, + name: descriptor.manifest.name, + version: descriptor.manifest.version, + logo: descriptor.manifest.logo, + description: descriptor.manifest.description, + types: descriptor.manifest.types, + catalogs: descriptor.manifest.catalogs, + } + })) + } + } + : + addons.catalog_resource; return { selectable, catalog_resource }; }; diff --git a/src/routes/Addons/useSelectableInputs.js b/src/routes/Addons/useSelectableInputs.js index 8376fa846..1b1c4dd15 100644 --- a/src/routes/Addons/useSelectableInputs.js +++ b/src/routes/Addons/useSelectableInputs.js @@ -15,18 +15,6 @@ const equalWithouExtra = (request1, request2) => { }; const mapSelectableInputs = (addons) => { - const selectedCatalogRequest = addons.catalog_resource !== null ? - addons.catalog_resource.request - : - { - base: null, - path: { - resource: 'addon_catalog', - id: null, - type_name: null, - extra: [] - } - }; const catalogSelect = { title: 'Select catalog', options: addons.selectable.catalogs @@ -36,7 +24,8 @@ const mapSelectableInputs = (addons) => { })), selected: addons.selectable.catalogs .filter(({ load_request: { path: { id } } }) => { - return id === selectedCatalogRequest.path.id; + return addons.catalog_resource !== null && + addons.catalog_resource.request.path.id === id; }) .map(({ load_request }) => JSON.stringify(load_request)), onSelect: (event) => { @@ -52,7 +41,8 @@ const mapSelectableInputs = (addons) => { })), selected: addons.selectable.types .filter(({ load_request }) => { - return equalWithouExtra(load_request, selectedCatalogRequest); + return addons.catalog_resource !== null && + equalWithouExtra(addons.catalog_resource.request, load_request); }) .map(({ load_request }) => JSON.stringify(load_request)), onSelect: (event) => {