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

This commit is contained in:
NikolaBorislavovHristov 2019-12-03 13:30:33 +02:00
commit 1126ff1482
7 changed files with 238 additions and 53 deletions

View file

@ -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
<div key={index} className={classnames(styles['meta-item'], styles['poster-shape-poster'])} />
))}
</div>
<Button className={styles['see-all-container']} title={'SEE ALL'}>
<Button className={styles['see-all-container']} title={'SEE ALL'} href={catalogHref}>
<div className={styles['label']}>SEE ALL</div>
<Icon className={styles['icon']} icon={'ic_arrow_thin_right'} />
</Button>
@ -58,7 +58,8 @@ MetaRow.propTypes = {
posterShape: PropTypes.string
})),
maximumItemsCount: PropTypes.number,
itemMenuOptions: PropTypes.any
itemMenuOptions: PropTypes.any,
catalogHref: PropTypes.string
};
module.exports = MetaRow;

View file

@ -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(() => {

View file

@ -20,32 +20,32 @@ const Board = () => {
<div className={styles['board-container']}>
<MainNavBar className={styles['nav-bar']} />
<div className={styles['board-content']}>
{catalogs.map(({ req, content }, index) => {
{catalogs.map(({ request, content }, index) => {
switch (content.type) {
case 'Ready':
return (
<MetaRow
key={`${index}${req.base}${content.type}`}
key={`${index}${request.base}${content.type}`}
className={styles['board-row']}
title={`${req.path.id} - ${req.path.type_name}`}
title={`${request.path.id} - ${request.path.type_name}`}
items={content.content}
/>
);
case 'Message':
return (
<MetaRow
key={`${index}${req.base}${content.type}`}
key={`${index}${request.base}${content.type}`}
className={styles['board-row']}
title={`${req.path.id} - ${req.path.type_name}`}
title={`${request.path.id} - ${request.path.type_name}`}
message={content.content}
/>
);
case 'Loading':
return (
<MetaRow.Placeholder
key={`${index}${req.base}${content.type}`}
key={`${index}${request.base}${content.type}`}
className={styles['board-row-placeholder']}
title={`${req.path.id} - ${req.path.type_name}`}
title={`${request.path.id} - ${request.path.type_name}`}
/>
);
}

View file

@ -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: [] }
}
});

View file

@ -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 (
<div className={styles['search-container']}>
<MainNavBar className={styles['nav-bar']} />
<div className={styles['search-content']}>
{groups.map((group, index) => (
<MetaRow
{...group}
key={index}
className={styles['search-row']}
/>
))}
{
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 (
<MetaRow
key={`${index}${request.base}${content.type} Ready`}
className={styles['search-row']}
title={`${request.path.id} - ${request.path.type_name}`}
items={content.content}
catalogHref={href}
/>
);
case 'Err':
return (
<MetaRow
key={`${index}${request.base}${content.type}`}
className={styles['search-row']}
title={`${request.path.id} - ${request.path.type_name} Err`}
message={`${content.content.type} ${typeof content.content.content === 'string' ? content.content.content : ''}`}
/>
);
case 'Loading':
return (
<MetaRow.Placeholder
key={`${index}${request.base}${content.type} Loading`}
className={styles['search-row-placeholder']}
title={`${request.path.id} - ${request.path.type_name}`}
/>
);
}
})
:
<div className={styles['message-container']}>
<div className={styles['message-content']}>
<div className={styles['label']}>No metadata was found</div>
</div>
</div>
:
<div className={styles['message-container']}>
<div className={styles['message-content']}>
<div className={styles['label']}> No addons were requested for metadata</div>
</div>
</div>
:
<div className={styles['message-container']}>
<div className={styles['message-content']}>
<Icon className={styles['icon']} icon={'ic_movies'} />
<div className={styles['label']}>Search for movies, series, YouTube and TV channels</div>
</div>
<div className={styles['message-content']}>
<Icon className={styles['icon']} icon={'ic_actor'} />
<div className={styles['label']}>Search for actors, directors and writers</div>
</div>
</div>
}
</div>
</div>
);

View file

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

View file

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