mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-04-17 20:22:55 +00:00
Merge branch 'development' of github.com:Stremio/stremio-web into linter
This commit is contained in:
commit
533f4cd751
4 changed files with 154 additions and 128 deletions
|
|
@ -1,38 +1,38 @@
|
|||
const routesRegexp = {
|
||||
intro: {
|
||||
regexp: /^\/intro\/?$/i,
|
||||
regexp: /^\/intro$/,
|
||||
urlParamsNames: []
|
||||
},
|
||||
board: {
|
||||
regexp: /^\/?$/i,
|
||||
regexp: /^\/$/,
|
||||
urlParamsNames: []
|
||||
},
|
||||
discover: {
|
||||
regexp: /^\/discover(?:\/([^\/]*)\/([^\/]*)\/([^\/]*))?\/?$/i,
|
||||
regexp: /^\/discover(?:\/([^\/]*)\/([^\/]*)\/([^\/]*))?$/,
|
||||
urlParamsNames: ['addonTransportUrl', 'type', 'catalogId']
|
||||
},
|
||||
library: {
|
||||
regexp: /^\/library(?:\/([^\/]*))?\/?$/i,
|
||||
regexp: /^\/library(?:\/([^\/]*))?$/,
|
||||
urlParamsNames: ['type']
|
||||
},
|
||||
search: {
|
||||
regexp: /^\/search\/?$/i,
|
||||
regexp: /^\/search$/,
|
||||
urlParamsNames: []
|
||||
},
|
||||
metadetails: {
|
||||
regexp: /^\/metadetails\/(?:([^\/]*))\/(?:([^\/]*))(?:\/([^\/]*)\/?)?$/i,
|
||||
regexp: /^\/metadetails\/([^\/]*)\/([^\/]*)(?:\/([^\/]*))?$/,
|
||||
urlParamsNames: ['type', 'id', 'videoId']
|
||||
},
|
||||
addons: {
|
||||
regexp: /^\/addons(?:\/([^\/]*?))?(?:\/([^\/]*?))?\/?$/i, // TODO both are required or none
|
||||
urlParamsNames: ['category', 'type']
|
||||
regexp: /^\/addons(?:\/([^\/]*)\/([^\/]*)\/([^\/]*))?$/,
|
||||
urlParamsNames: ['addonTransportUrl', 'catalogId', 'type']
|
||||
},
|
||||
settings: {
|
||||
regexp: /^\/settings\/?$/i,
|
||||
regexp: /^\/settings$/,
|
||||
urlParamsNames: []
|
||||
},
|
||||
player: {
|
||||
regexp: /^\/player\/(?:([^\/]+?))\/(?:([^\/]+?))\/(?:([^\/]+?))\/(?:([^\/]+?))\/?$/i,
|
||||
regexp: /^\/player\/([^\/]*)\/([^\/]*)\/([^\/]*)\/([^\/]*)$/,
|
||||
urlParamsNames: ['type', 'id', 'videoId', 'stream']
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,19 +17,22 @@ const Router = ({ className, onPathNotMatch, ...props }) => {
|
|||
const [views, setViews] = React.useState(() => {
|
||||
return Array(viewsConfig.length).fill(null);
|
||||
});
|
||||
React.useEffect(() => {
|
||||
React.useLayoutEffect(() => {
|
||||
if (typeof homePath === 'string') {
|
||||
const { pathname, path } = UrlUtils.parse(window.location.hash.slice(1));
|
||||
if (homePath !== path) {
|
||||
window.location.replace(`#${homePath}`);
|
||||
const routeConfig = routeConfigForPath(viewsConfig, pathname);
|
||||
const routeConfig = typeof pathname === 'string' ?
|
||||
routeConfigForPath(viewsConfig, pathname)
|
||||
:
|
||||
null;
|
||||
if (routeConfig) {
|
||||
window.location = `#${path}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
React.useEffect(() => {
|
||||
React.useLayoutEffect(() => {
|
||||
const onLocationHashChange = () => {
|
||||
const { pathname, query } = UrlUtils.parse(window.location.hash.slice(1));
|
||||
const queryParams = new URLSearchParams(typeof query === 'string' ? query : '');
|
||||
|
|
|
|||
|
|
@ -1,129 +1,80 @@
|
|||
const React = require('react');
|
||||
const { useServices } = require('stremio/services');
|
||||
const { useModelState } = require('stremio/common');
|
||||
|
||||
const DEFAULT_TYPE = 'movie';
|
||||
const DEFAULT_CATEGORY = 'thirdparty';
|
||||
const initAddonsState = () => ({
|
||||
selectable: {
|
||||
types: [],
|
||||
catalogs: [],
|
||||
extra: [],
|
||||
has_next_page: false,
|
||||
has_prev_page: false
|
||||
},
|
||||
catalog_resource: null
|
||||
});
|
||||
|
||||
const useAddons = (urlParams, queryParams) => {
|
||||
const { core } = useServices();
|
||||
const [addons, setAddons] = React.useState([[], [], [], [], null]);
|
||||
const installAddon = React.useCallback(descriptor => {
|
||||
core.dispatch({
|
||||
action: 'AddonOp',
|
||||
args: {
|
||||
addonOp: 'Install',
|
||||
args: descriptor
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
const uninstallAddon = React.useCallback(descriptor => {
|
||||
core.dispatch({
|
||||
action: 'AddonOp',
|
||||
args: {
|
||||
addonOp: 'Remove',
|
||||
args: {
|
||||
transport_url: descriptor.transportUrl
|
||||
}
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
React.useEffect(() => {
|
||||
const type = typeof urlParams.type === 'string' && urlParams.type.length > 0 ? urlParams.type : DEFAULT_TYPE;
|
||||
const category = typeof urlParams.category === 'string' && urlParams.category.length > 0 ? urlParams.category : DEFAULT_CATEGORY;
|
||||
const onNewState = () => {
|
||||
const state = core.getState();
|
||||
[...new Set(
|
||||
['all'].concat(...state.ctx.content.addons.map(addon => addon.manifest.types))
|
||||
)]
|
||||
.map((type) => (
|
||||
{
|
||||
is_selected: urlParams.category === 'my' && urlParams.type === type,
|
||||
name: 'my',
|
||||
load: {
|
||||
base: 'https://v3-cinemeta.strem.io/manifest.json',
|
||||
path: {
|
||||
resource: 'addon_catalog',
|
||||
type_name: type,
|
||||
id: 'my',
|
||||
extra: []
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
.forEach(addon => state.addons.catalogs.push(addon));
|
||||
const selectAddon = (transportUrl) => {
|
||||
window.location = `#/addons/${category}/${type}?addon=${transportUrl}`;
|
||||
};
|
||||
const selectInputs = [
|
||||
{
|
||||
selected: state.addons.catalogs
|
||||
.filter(({ is_selected }) => is_selected)
|
||||
.map(({ load }) => load.path.id),
|
||||
options: state.addons.catalogs
|
||||
.filter((catalog, index, catalogs) => {
|
||||
return catalogs.map(ctg => ctg.name).indexOf(catalog.name) === index;
|
||||
})
|
||||
.map(({ name, load }) => ({
|
||||
value: load.path.id,
|
||||
label: name
|
||||
})),
|
||||
onSelect: (event) => {
|
||||
const load = state.addons.catalogs.find(({ load: { path: { id } } }) => {
|
||||
return id === event.value;
|
||||
}).load;
|
||||
window.location = `#/addons/${encodeURIComponent(load.path.id)}/${encodeURIComponent(load.path.type_name)}`;
|
||||
}
|
||||
},
|
||||
{
|
||||
selected: state.addons.catalogs
|
||||
.filter(({ is_selected }) => is_selected)
|
||||
.map(({ load }) => JSON.stringify(load)),
|
||||
options: state.addons.catalogs
|
||||
.filter(({ load: { path: { id } } }) => {
|
||||
return id === category;
|
||||
})
|
||||
.map(({ load }) => ({
|
||||
value: JSON.stringify(load),
|
||||
label: load.path.type_name
|
||||
})),
|
||||
onSelect: (event) => {
|
||||
const load = JSON.parse(event.value);
|
||||
window.location = `#/addons/${encodeURIComponent(load.path.id)}/${encodeURIComponent(load.path.type_name)}`;
|
||||
}
|
||||
}
|
||||
];
|
||||
const installedAddons = state.ctx.is_loaded ? state.ctx.content.addons : [];
|
||||
const addonsItems = urlParams.category === 'my' ?
|
||||
installedAddons.filter(addon => urlParams.type === 'all' || addon.manifest.types.includes(urlParams.type))
|
||||
:
|
||||
state.addons.content.type === 'Ready' ?
|
||||
state.addons.content.content
|
||||
:
|
||||
[];
|
||||
const error = state.addons.content.type === 'Err' && !state.ctx.is_loaded ? state.addons.content.content : null;
|
||||
setAddons([addonsItems, selectInputs, selectAddon, installedAddons, error]);
|
||||
};
|
||||
core.on('NewModel', onNewState);
|
||||
core.dispatch({
|
||||
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
|
||||
return { selectable, catalog_resource };
|
||||
};
|
||||
|
||||
const onNewAddonsState = (addons) => {
|
||||
if (addons.catalog_resource === null && addons.selectable.catalogs.length > 0) {
|
||||
return {
|
||||
action: 'Load',
|
||||
args: {
|
||||
load: 'CatalogFiltered',
|
||||
args: addons.selectable.catalogs[0].load_request
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const useAddons = (urlParams) => {
|
||||
const { core } = useServices();
|
||||
const loadAddonsAction = React.useMemo(() => {
|
||||
if (typeof urlParams.addonTransportUrl === 'string' && typeof urlParams.catalogId === 'string' && typeof urlParams.type === 'string') {
|
||||
return {
|
||||
action: 'Load',
|
||||
args: {
|
||||
base: 'https://v3-cinemeta.strem.io/manifest.json',
|
||||
path: {
|
||||
resource: 'addon_catalog',
|
||||
type_name: type,
|
||||
id: category,
|
||||
extra: []
|
||||
load: 'CatalogFiltered',
|
||||
args: {
|
||||
base: urlParams.addonTransportUrl,
|
||||
path: {
|
||||
resource: 'addon_catalog',
|
||||
type_name: urlParams.type,
|
||||
id: urlParams.catalogId,
|
||||
extra: []
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
const addons = core.getState('addons');
|
||||
if (addons.selectable.catalogs.length > 0) {
|
||||
return {
|
||||
action: 'Load',
|
||||
args: {
|
||||
load: 'CatalogFiltered',
|
||||
args: addons.selectable.catalogs[0].load_request
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
action: 'Unload'
|
||||
};
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
core.off('NewModel', onNewState);
|
||||
};
|
||||
}, [urlParams, queryParams]);
|
||||
return [addons, installAddon, uninstallAddon];
|
||||
}
|
||||
}, [urlParams]);
|
||||
return useModelState({
|
||||
model: 'addons',
|
||||
action: loadAddonsAction,
|
||||
mapWithCtx: mapAddonsStateWithCtx,
|
||||
init: initAddonsState,
|
||||
onNewState: onNewAddonsState
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = useAddons;
|
||||
|
|
|
|||
72
src/routes/Addons/useSelectableInputs.js
Normal file
72
src/routes/Addons/useSelectableInputs.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
const React = require('react');
|
||||
|
||||
const navigateWithLoadRequest = (load_request) => {
|
||||
const addonTransportUrl = encodeURIComponent(load_request.base);
|
||||
const catalogId = encodeURIComponent(load_request.path.id);
|
||||
const type = encodeURIComponent(load_request.path.type_name);
|
||||
window.location.replace(`#/addons/${addonTransportUrl}/${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 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
|
||||
.map(({ name, load_request }) => ({
|
||||
value: JSON.stringify(load_request),
|
||||
label: name
|
||||
})),
|
||||
selected: addons.selectable.catalogs
|
||||
.filter(({ load_request: { path: { id } } }) => {
|
||||
return id === selectedCatalogRequest.path.id;
|
||||
})
|
||||
.map(({ load_request }) => JSON.stringify(load_request)),
|
||||
onSelect: (event) => {
|
||||
navigateWithLoadRequest(JSON.parse(event.value));
|
||||
}
|
||||
};
|
||||
const typeSelect = {
|
||||
title: 'Select type',
|
||||
options: addons.selectable.types
|
||||
.map(({ name, load_request }) => ({
|
||||
value: JSON.stringify(load_request),
|
||||
label: name
|
||||
})),
|
||||
selected: addons.selectable.types
|
||||
.filter(({ load_request }) => {
|
||||
return equalWithouExtra(load_request, selectedCatalogRequest);
|
||||
})
|
||||
.map(({ load_request }) => JSON.stringify(load_request)),
|
||||
onSelect: (event) => {
|
||||
navigateWithLoadRequest(JSON.parse(event.value));
|
||||
}
|
||||
};
|
||||
return [catalogSelect, typeSelect];
|
||||
};
|
||||
|
||||
const useSelectableInputs = (addons) => {
|
||||
const selectableInputs = React.useMemo(() => {
|
||||
return mapSelectableInputs(addons);
|
||||
}, [addons]);
|
||||
return selectableInputs;
|
||||
};
|
||||
|
||||
module.exports = useSelectableInputs;
|
||||
Loading…
Reference in a new issue