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;
};