Merge branch 'development' of github.com:Stremio/stremio-web into linter

This commit is contained in:
svetlagasheva 2019-12-11 14:04:55 +02:00
commit 533f4cd751
4 changed files with 154 additions and 128 deletions

View file

@ -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']
}
};

View file

@ -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 : '');

View file

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

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