diff --git a/src/routes/Discover/Discover.js b/src/routes/Discover/Discover.js index 620dac978..2e1df1274 100644 --- a/src/routes/Discover/Discover.js +++ b/src/routes/Discover/Discover.js @@ -1,134 +1,43 @@ const React = require('react'); -const classnames = require('classnames'); -const { Input } = require('stremio-navigation'); -const Icon = require('stremio-icons/dom'); -const { MainNavBar, MetaItem, MetaPreview, Popup, useBinaryState } = require('stremio-common'); +const { MainNavBar, MetaItem, MetaPreview } = require('stremio-common'); +const PickerMenu = require('./PickerMenu'); const useCatalog = require('./useCatalog'); const styles = require('./styles'); // TODO impl refocus to left of the scroll view -const Discover = ({ urlParams }) => { - const catalog = useCatalog(urlParams); - const [typePickerOpen, typePickerOnOpen, typePickerOnClose] = useBinaryState(false); - const [catalogPickerOpen, catalogPickerOnOpen, catalogPickerOnClose] = useBinaryState(false); - const [categoryPickerOpen, categoryPickerOnOpen, categoryPickerOnClose] = useBinaryState(false); - React.useEffect(() => { - if (typeof urlParams.type !== 'string' || typeof urlParams.catalog !== 'string') { - const type = urlParams.type || 'movie'; - const catalog = urlParams.catalog || 'com.linvo.cinemeta:top'; - const category = urlParams.category || ''; - window.location.replace(`#/discover/${type}/${catalog}/${category}`); - } - }, [urlParams.type, urlParams.catalog]); +const Discover = ({ urlParams, queryParams }) => { + const [pickers, metaItems] = useCatalog(urlParams, queryParams); + const [selectedItem, setSelectedItem] = React.useState(metaItems[0]); + const changeMetaItem = React.useCallback((event) => { + const metaItem = metaItems.find(({ id }) => id === event.currentTarget.dataset.metaItemId); + setSelectedItem(metaItem); + }, [metaItems]); return (
- { - typeof urlParams.type === 'string' || typeof urlParams.catalog === 'string' ? -
-
- - - -
{urlParams.type}
- - -
- -
- -
movie
- - -
series
- - -
channel
- - -
TV channels
- -
-
-
- - - -
{urlParams.catalog}
- - -
- -
- -
catalog1
- - -
catalog2
- -
-
-
- - - -
{urlParams.category !== null ? urlParams.category : 'Select category'}
- - -
- -
- -
category1
- - -
category2
- -
-
-
+
+
+ {pickers.map((picker) => ( + + ))} +
+
+ {metaItems.map((metaItem) => ( +
+
-
- {catalog.map(({ id, type, name, posterShape }) => ( -
- -
- ))} -
- { }} - /> -
- : - null - } + ))} +
+ +
); }; diff --git a/src/routes/Discover/PickerMenu.js b/src/routes/Discover/PickerMenu.js new file mode 100644 index 000000000..302aa8c07 --- /dev/null +++ b/src/routes/Discover/PickerMenu.js @@ -0,0 +1,49 @@ +const React = require('react'); +const PropTypes = require('prop-types'); +const classnames = require('classnames'); +const Icon = require('stremio-icons/dom'); +const { Input } = require('stremio-navigation'); +const { Popup, useBinaryState } = require('stremio-common'); +const styles = require('./styles'); + +// TODO support optionsLimit +const PickerMenu = ({ name, value, options, toggle }) => { + const [open, onOpen, onClose] = useBinaryState(false); + const label = typeof value === 'string' ? value : name; + return ( + + + +
{label}
+ + +
+ +
+ { + Array.isArray(options) ? + options.map(({ value, label }) => ( + +
{label}
+ + )) + : + null + } +
+
+
+ ); +}; + +PickerMenu.propTypes = { + name: PropTypes.string, + value: PropTypes.string, + options: PropTypes.arrayOf(PropTypes.shape({ + value: PropTypes.string, + label: PropTypes.string + })), + toggle: PropTypes.func +}; + +module.exports = PickerMenu; diff --git a/src/routes/Discover/styles.less b/src/routes/Discover/styles.less index 9f715c1e1..7ea7c389f 100644 --- a/src/routes/Discover/styles.less +++ b/src/routes/Discover/styles.less @@ -20,29 +20,23 @@ align-self: stretch; display: grid; grid-template-columns: 1fr 26em; - grid-template-rows: 4.6em 1fr; + grid-template-rows: auto 1fr; grid-template-areas: - "picker-area meta-preview-area" + "pickers-area meta-preview-area" "meta-items-area meta-preview-area"; - overflow: hidden; .pickers-container { - grid-area: picker-area; - display: flex; - flex-direction: row; - align-items: stretch; - padding: 0.8em; + grid-area: pickers-area; + padding: 0.4em; .picker-button { - flex-grow: 0; - flex-shrink: 1; - flex-basis: 16em; - display: flex; + width: 16em; + height: 3em; + margin: 0.4em; + padding: 0 0.8em; + display: inline-flex; flex-direction: row; align-items: center; - justify-content: space-between; - margin-right: 0.8em; - padding: 0 0.8em; background-color: var(--color-backgroundlighter); cursor: pointer; @@ -62,20 +56,16 @@ } } - &.types-picker-button { - .picker-label { - text-transform: capitalize; - } - } - .picker-label { flex: 1; font-size: 1.1em; - line-height: 1.1em; - max-height: 2.2em; + line-height: 1.2em; + max-height: 2.4em; color: var(--color-surfacelighter); - word-break: break-all; - word-break: break-word; + + &.capitalized { + text-transform: capitalize; + } } .picker-icon { @@ -90,31 +80,17 @@ .meta-items-container { grid-area: meta-items-area; - display: flex; - flex-direction: row; - justify-content: flex-start; - align-content: flex-start; - flex-wrap: wrap; padding: 0 0.4em; overflow-x: hidden; overflow-y: auto; - &::after { - width: 100%; - height: 0.4em; - display: block; - content: ""; - } - .meta-item-container { - flex-grow: 0; - flex-shrink: 0; - flex-basis: calc(100% / var(--items-per-row)); + display: inline-block; + width: calc(100% / var(--items-per-row)); padding: 0.4em; .meta-item { width: 100%; - height: 100%; } } } @@ -130,22 +106,11 @@ --border-color: var(--color-backgroundlighter40); --box-shadow: -0.6em 0.6em 0.5em -0.1em var(--color-backgrounddark40); - .menu-items-container { + .menu-container { width: 16em; - max-height: 18em; - overflow-y: auto; background-color: var(--color-backgroundlighter); - &.menu-types-container { - .menu-item-container { - .menu-label { - text-transform: capitalize; - } - } - } - .menu-item-container { - width: 100%; padding: 0.8em; cursor: pointer; @@ -154,13 +119,14 @@ } .menu-label { - line-height: 1.1em; - max-height: 2.2em; + line-height: 1.2em; + max-height: 2.4em; color: var(--color-surfacelighter); - word-break: break-all; - word-break: break-word; + + &.capitalized { + text-transform: capitalize; + } } } } - } \ No newline at end of file diff --git a/src/routes/Discover/useCatalog.js b/src/routes/Discover/useCatalog.js index 109a4ca79..8db060faf 100644 --- a/src/routes/Discover/useCatalog.js +++ b/src/routes/Discover/useCatalog.js @@ -1,15 +1,104 @@ const React = require('react'); -const useCatalog = (addonId, catalogId, extra) => { - return React.useMemo(() => { +const useCatalog = (urlParams, queryParams) => { + const query = new URLSearchParams(queryParams).toString(); + const [addon, catalog] = React.useMemo(() => { + // TODO impl this logic to stremio-core for code-reuse: + // TODO use type if it is part of user's addons + // TODO fallback to first type from user's addons + // TODO find catalog for addonId, catalogId and type + // TODO fallback to first catalog for the type from user's catalogs + const addon = { + id: 'com.linvo.cinemeta', + version: '2.11.0', + name: 'Cinemeta' + }; + const catalog = { + id: 'top', + type: 'movie', + name: 'Top', + extra: [ + { + name: 'genre', + isRequired: false, + options: ['Action', 'drama', 'Boring'] + }, + { + name: 'year', + isRequired: false, + options: ['2017', '2016', '2015'] + } + ] + }; + return [addon, catalog]; + }, [urlParams.type, urlParams.catalog]); + const pickers = React.useMemo(() => { + const replaceType = (event) => { + const { value } = event.currentTarget.dataset; + const query = new URLSearchParams(queryParams); + window.location = `#/discover/${value}/${addon.id}:${catalog.id}?${query}`; + }; + const replaceCatalog = (event) => { + const { value } = event.currentTarget.dataset; + const query = new URLSearchParams(queryParams); + window.location = `#/discover/${catalog.type}/${value}?${query}`; + }; + const replaceQueryParam = (event) => { + const { name, value } = event.currentTarget.dataset; + const query = new URLSearchParams({ ...queryParams, [name]: value }); + window.location = `#/discover/${catalog.type}/${addon.id}:${catalog.id}?${query}`; + }; + const requiredPickers = [ + { + name: 'type', + value: catalog.type, + options: [ + { value: 'movie', label: 'movie' }, + { value: 'series', label: 'series' }, + { value: 'channels', label: 'channels' }, + { value: 'games', label: 'games' } + ], + toggle: replaceType + }, + { + name: 'catalog', + value: catalog.name, + options: [ + { value: 'com.linvo.cinemeta:top', label: 'Top' }, + { value: 'com.linvo.cinemeta:year', label: 'By year' } + ], + toggle: replaceCatalog + } + ]; + const extraPickers = catalog.extra + .filter((extra) => { + return extra.name !== 'skip' && extra.name !== 'search'; + }) + .map((extra) => ({ + ...extra, + toggle: replaceQueryParam, + options: extra.options.map((option) => ({ value: option, label: option })), + value: extra.options.includes(queryParams[extra.name]) ? + queryParams[extra.name] + : + extra.isRequired ? + extra.options[0] + : + null + })); + return requiredPickers.concat(extraPickers); + }, [addon, catalog, query]); + const items = React.useMemo(() => { return Array(303).fill(null).map((_, index) => ({ id: `tt${index}`, type: 'movie', - name: 'Stremio demo item', + name: `Stremio demo item${index}`, poster: `https://dummyimage.com/300x400/000/0011ff.jpg&text=${index + 1}`, + logo: `https://dummyimage.com/300x400/000/0011ff.jpg&text=${index + 1}`, posterShape: 'poster' })); }, []); + return [pickers, items]; }; module.exports = useCatalog;