diff --git a/src/common/MetaRow/MetaRow.js b/src/common/MetaRow/MetaRow.js index 9658dff1f..b416207a3 100644 --- a/src/common/MetaRow/MetaRow.js +++ b/src/common/MetaRow/MetaRow.js @@ -7,7 +7,7 @@ const MetaItem = require('stremio/common/MetaItem'); const MetaRowPlaceholder = require('./MetaRowPlaceholder'); const styles = require('./styles'); -const MetaRow = ({ className, title, message, items, maximumItemsCount, itemMenuOptions }) => { +const MetaRow = ({ className, title, message, items, maximumItemsCount, itemMenuOptions, catalogHref }) => { maximumItemsCount = maximumItemsCount !== null && isFinite(maximumItemsCount) ? maximumItemsCount : 20; items = Array.isArray(items) ? items.slice(0, maximumItemsCount) : []; return ( @@ -38,7 +38,7 @@ const MetaRow = ({ className, title, message, items, maximumItemsCount, itemMenu
))}
- @@ -58,7 +58,8 @@ MetaRow.propTypes = { posterShape: PropTypes.string })), maximumItemsCount: PropTypes.number, - itemMenuOptions: PropTypes.any + itemMenuOptions: PropTypes.any, + catalogHref: PropTypes.string }; module.exports = MetaRow; diff --git a/src/common/NavBar/SearchBar/SearchBar.js b/src/common/NavBar/SearchBar/SearchBar.js index 5e33d44ec..2097c79af 100644 --- a/src/common/NavBar/SearchBar/SearchBar.js +++ b/src/common/NavBar/SearchBar/SearchBar.js @@ -20,7 +20,7 @@ const SearchBar = ({ className }) => { if (routeActive) { const { search: locationSearch } = UrlUtils.parse(locationHash.slice(1)); const queryParams = new URLSearchParams(locationSearch); - return queryParams.has('q') ? queryParams.get('q') : ''; + return queryParams.has('search') ? queryParams.get('search') : ''; } return ''; @@ -32,7 +32,7 @@ const SearchBar = ({ className }) => { }, [routeActive]); const queryInputOnSubmit = React.useCallback(() => { if (routeActive) { - window.location.replace(`#/search?q=${searchInputRef.current.value}`); + window.location.replace(`#/search?search=${searchInputRef.current.value}`); } }, [routeActive]); React.useEffect(() => { diff --git a/src/routes/Board/Board.js b/src/routes/Board/Board.js index 1b626f6ca..980b47f11 100644 --- a/src/routes/Board/Board.js +++ b/src/routes/Board/Board.js @@ -20,32 +20,32 @@ const Board = () => {
- {catalogs.map(({ req, content }, index) => { + {catalogs.map(({ request, content }, index) => { switch (content.type) { case 'Ready': return ( ); case 'Message': return ( ); case 'Loading': return ( ); } diff --git a/src/routes/Board/useCatalogs.js b/src/routes/Board/useCatalogs.js index d2470ff9e..ce865f2d5 100644 --- a/src/routes/Board/useCatalogs.js +++ b/src/routes/Board/useCatalogs.js @@ -7,13 +7,13 @@ const useCatalogs = () => { React.useEffect(() => { const onNewState = () => { const state = core.getState(); - setCatalogs(state.board.groups); + setCatalogs(state.board.items_groups); }; core.on('NewModel', onNewState); core.dispatch({ action: 'Load', args: { - load: 'CatalogGrouped', + load: 'CatalogsGrouped', args: { extra: [] } } }); diff --git a/src/routes/Search/Search.js b/src/routes/Search/Search.js index b97096a5b..b91fbea06 100644 --- a/src/routes/Search/Search.js +++ b/src/routes/Search/Search.js @@ -1,21 +1,77 @@ const React = require('react'); +const Icon = require('stremio-icons/dom'); const { MainNavBar, MetaRow } = require('stremio/common'); const useSearch = require('./useSearch'); const styles = require('./styles'); const Search = ({ queryParams }) => { - const groups = useSearch(queryParams.get('q')); + const search = useSearch(queryParams); + const searchSelected = React.useMemo(() => { + return search.selected.some(([name, value]) => name === 'search' && value.length > 0) + }, [search.selected]); return (
- {groups.map((group, index) => ( - - ))} + { + searchSelected ? + search.items_groups && search.items_groups.length > 0 ? + search.items_groups.some(group => group.content.type !== 'Err') ? + search.items_groups.map(({ href, request, content }, index) => { + switch (content.type) { + case 'Ready': + return ( + + ); + case 'Err': + return ( + + ); + case 'Loading': + return ( + + ); + } + }) + : +
+
+
No metadata was found
+
+
+ : +
+
+
No addons were requested for metadata
+
+
+ : +
+
+ +
Search for movies, series, YouTube and TV channels
+
+
+ +
Search for actors, directors and writers
+
+
+ }
); diff --git a/src/routes/Search/styles.less b/src/routes/Search/styles.less index 40e01a4d2..c5f3308a9 100644 --- a/src/routes/Search/styles.less +++ b/src/routes/Search/styles.less @@ -1,7 +1,13 @@ +@import (reference) '~stremio/common/screen-sizes.less'; + :import('~stremio/common/MetaRow/styles.less') { meta-item: meta-item; } +:import('~stremio/common/MetaRow/MetaRowPlaceholder/styles.less') { + meta-item-placeholder: meta-item; +} + .search-container { display: flex; flex-direction: column; @@ -19,7 +25,7 @@ align-self: stretch; overflow-y: auto; - .search-row { + .search-row, .search-row-placeholder { margin: 4rem 2rem; &:first-child { @@ -29,10 +35,111 @@ &:last-child { margin-bottom: 2rem; } + } - .meta-item { - &:nth-child(n+6) { - display: none; + .message-container { + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: center; + margin: 5rem; + + .message-content { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + .icon { + flex: none; + width: 6rem; + height: 6rem; + margin-bottom: 2rem; + fill: var(--color-surfacelighter40); + } + + .label { + flex-grow: 0; + flex-shrink: 1; + flex-basis: auto; + font-size: 1.2rem; + color: var(--color-surfacelighter40); + text-align: center; + } + + &:not(:last-child) { + margin-right: 5rem;; + } + } + } + } +} + +@media only screen and (min-width: @large) { + .board-container { + .board-content { + .board-row, .board-row-placeholder { + .meta-item, .meta-item-placeholder { + &:nth-child(n+9) { + display: none; + } + } + } + } + } +} + +@media only screen and (max-width: @large) { + .search-container { + .search-content { + .search-row, .search-row-placeholder { + .meta-item, .meta-item-placeholder { + &:nth-child(n+8) { + display: none; + } + } + } + } + } +} + +@media only screen and (max-width: @medium) { + .search-container { + .search-content { + .search-row, .search-row-placeholder { + .meta-item, .meta-item-placeholder { + &:nth-child(n+7) { + display: none; + } + } + } + } + } +} + +@media only screen and (max-width: @small) { + .search-container { + .search-content { + .search-row, .search-row-placeholder { + .meta-item, .meta-item-placeholder { + &:nth-child(n+6) { + display: none; + } + } + } + } + } +} + +@media only screen and (max-width: @xsmall) { + .search-container { + .search-content { + .search-row, .search-row-placeholder { + .meta-item, .meta-item-placeholder { + &:nth-child(n+5) { + display: none; + } } } } diff --git a/src/routes/Search/useSearch.js b/src/routes/Search/useSearch.js index 68289c202..331a34143 100644 --- a/src/routes/Search/useSearch.js +++ b/src/routes/Search/useSearch.js @@ -1,32 +1,53 @@ const React = require('react'); +const { useServices } = require('stremio/services'); -const useSearch = (query) => { - const items = React.useMemo(() => { - return [ - { - title: 'demo addon', - items: [ - { - id: '1', - type: 'movie', - name: 'Stremio demo item movie 1', - poster: '/images/intro_background.jpg', - logo: '/images/default_avatar.png', - posterShape: 'poster' - }, - { - id: '2', - type: 'movie', - name: 'Stremio demo item movie 2', - poster: '/images/intro_background.jpg', - logo: '/images/default_avatar.png', - posterShape: 'poster' - }, - ] - } - ]; - }, [query]); - return items; +const mapSearchState = (state) => { + const query = state.search.selected.reduceRight((query, [name, value]) => { + if (name === 'search') { + return value; + } + return query; + }, ''); + const selected = state.search.selected; + const items_groups = state.search.items_groups.map((group) => { + group.href = `#/discover/${encodeURIComponent(group.request.base)}/${encodeURIComponent(group.request.path.id)}/${encodeURIComponent(group.request.path.type_name)}?search=${query}`; + return group; + }); + return { selected, items_groups }; +}; + +const useSearch = (queryParams) => { + const { core } = useServices(); + const [search, setSearch] = React.useState(() => { + const state = core.getState(); + const search = mapSearchState(state); + return search; + }); + React.useEffect(() => { + const onNewState = () => { + const state = core.getState(); + const search = mapSearchState(state); + setSearch(search); + }; + core.on('NewModel', onNewState); + if (queryParams.has('search')) { + core.dispatch({ + action: 'Load', + args: { + load: 'CatalogsGrouped', + args: { + extra: [ + ['search', queryParams.get('search')] + ] + } + } + }); + } + return () => { + core.off('NewModel', onNewState); + }; + }, [queryParams]); + return search; }; module.exports = useSearch;