feat: add support for search filters
This commit is contained in:
parent
541f6fc34a
commit
ff0478ade3
20 changed files with 316 additions and 177 deletions
|
@ -34,6 +34,7 @@
|
|||
"postinstall": "electron-builder install-app-deps && node build/downloadDaemon.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lbry/components": "^2.2.0",
|
||||
"@types/three": "^0.93.1",
|
||||
"bluebird": "^3.5.1",
|
||||
"breakdance": "^3.0.1",
|
||||
|
@ -52,7 +53,7 @@
|
|||
"hast-util-sanitize": "^1.1.2",
|
||||
"keytar": "^4.2.1",
|
||||
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
||||
"lbry-redux": "lbryio/lbry-redux#42c185e922a7c6091b0e1580bacbfd8e02f45a91",
|
||||
"lbry-redux": "lbryio/lbry-redux#3ab065b11a52d3e2e6a50a25459f9ff0aac03b13",
|
||||
"lbryinc": "lbryio/lbryinc#60d80401891743f991c040bafa8e51da7e939777",
|
||||
"localforage": "^1.7.1",
|
||||
"mammoth": "^1.4.6",
|
||||
|
@ -62,8 +63,8 @@
|
|||
"node-fetch": "^2.3.0",
|
||||
"qrcode.react": "^0.8.0",
|
||||
"rc-progress": "^2.0.6",
|
||||
"react": "^16.6.0",
|
||||
"react-dom": "^16.6.0",
|
||||
"react": "^16.8.2",
|
||||
"react-dom": "^16.8.2",
|
||||
"react-feather": "^1.0.8",
|
||||
"react-modal": "^3.1.7",
|
||||
"react-paginate": "^5.2.1",
|
||||
|
@ -89,7 +90,6 @@
|
|||
"y18n": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lbry/components": "^2.2.0",
|
||||
"babel-eslint": "^8.2.2",
|
||||
"babel-plugin-module-resolver": "^3.1.1",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
|
|
|
@ -29,14 +29,16 @@ type Props = {
|
|||
disabled?: boolean,
|
||||
},
|
||||
inputButton: ?React.Node,
|
||||
blockWrap: boolean,
|
||||
};
|
||||
|
||||
export class FormField extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
labelOnLeft: false,
|
||||
blockWrap: true,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.input = React.createRef();
|
||||
}
|
||||
|
@ -66,31 +68,39 @@ export class FormField extends React.PureComponent<Props> {
|
|||
autoFocus,
|
||||
inputButton,
|
||||
labelOnLeft,
|
||||
blockWrap,
|
||||
...inputProps
|
||||
} = this.props;
|
||||
const errorMessage = typeof error === 'object' ? error.message : error;
|
||||
|
||||
const Wrapper = blockWrap
|
||||
? ({ children: innerChildren }) => <fieldset-section>{innerChildren}</fieldset-section>
|
||||
: ({ children: innerChildren }) => <React.Fragment>{innerChildren}</React.Fragment>;
|
||||
|
||||
let input;
|
||||
if (type) {
|
||||
if (type === 'radio') {
|
||||
input = (
|
||||
<fieldset-section>
|
||||
<Wrapper>
|
||||
<radio-element>
|
||||
<input id={name} type="radio" {...inputProps} />
|
||||
<label htmlFor={name}>{label}</label>
|
||||
<radio-toggle onClick={inputProps.onChange} />
|
||||
</radio-element>
|
||||
</fieldset-section>
|
||||
</Wrapper>
|
||||
);
|
||||
} else if (type === 'checkbox') {
|
||||
// web components treat props weird
|
||||
// we need to fully remove it for proper component:attribute css styling
|
||||
const elementProps = inputProps.disabled ? { disabled: true } : {};
|
||||
input = (
|
||||
<fieldset-section>
|
||||
<checkbox-element>
|
||||
<Wrapper>
|
||||
<checkbox-element {...elementProps}>
|
||||
<input id={name} type="checkbox" {...inputProps} />
|
||||
<label htmlFor={name}>{label}</label>
|
||||
<checkbox-toggle onClick={inputProps.onChange} />
|
||||
</checkbox-element>
|
||||
</fieldset-section>
|
||||
</Wrapper>
|
||||
);
|
||||
} else if (type === 'setting') {
|
||||
// 'setting' should only be used for settings. Forms should use "checkbox"
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { makeSelectSearchUris, selectIsSearching, selectSearchDownloadUris } from 'lbry-redux';
|
||||
import {
|
||||
makeSelectSearchUris,
|
||||
selectIsSearching,
|
||||
selectSearchDownloadUris,
|
||||
makeSelectQueryWithOptions,
|
||||
} from 'lbry-redux';
|
||||
import FileListSearch from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
uris: makeSelectSearchUris(props.query)(state),
|
||||
uris: makeSelectSearchUris(makeSelectQueryWithOptions()(state))(state),
|
||||
downloadUris: selectSearchDownloadUris(props.query)(state),
|
||||
isSearching: selectIsSearching(state),
|
||||
});
|
||||
|
|
|
@ -11,26 +11,11 @@ type Props = {
|
|||
query: string,
|
||||
isSearching: boolean,
|
||||
uris: ?Array<string>,
|
||||
downloadUris: ?Array<string>,
|
||||
};
|
||||
|
||||
class FileListSearch extends React.PureComponent<Props> {
|
||||
render() {
|
||||
const { uris, query, downloadUris, isSearching } = this.props;
|
||||
|
||||
const fileResults = [];
|
||||
const channelResults = [];
|
||||
if (uris && uris.length) {
|
||||
uris.forEach(uri => {
|
||||
const isChannel = parseURI(uri).claimName[0] === '@';
|
||||
if (isChannel) {
|
||||
channelResults.push(uri);
|
||||
} else {
|
||||
fileResults.push(uri);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const { uris, query, isSearching } = this.props;
|
||||
return (
|
||||
query && (
|
||||
<React.Fragment>
|
||||
|
@ -38,26 +23,15 @@ class FileListSearch extends React.PureComponent<Props> {
|
|||
<section className="search__results-section">
|
||||
<div className="search__results-title">{__('Search Results')}</div>
|
||||
<HiddenNsfwClaims uris={uris} />
|
||||
{!isSearching && fileResults.length ? (
|
||||
fileResults.map(uri => <FileTile key={uri} uri={uri} />)
|
||||
{!isSearching && uris && uris.length ? (
|
||||
uris.map(
|
||||
uri =>
|
||||
parseURI(uri).claimName[0] === '@' ? (
|
||||
<ChannelTile key={uri} uri={uri} />
|
||||
) : (
|
||||
<NoResults />
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="search__results-section">
|
||||
<div className="search__results-title">{__('Channels')}</div>
|
||||
{!isSearching && channelResults.length ? (
|
||||
channelResults.map(uri => <ChannelTile key={uri} uri={uri} />)
|
||||
) : (
|
||||
<NoResults />
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="search__results-section">
|
||||
<div className="search__results-title">{__('Your downloads')}</div>
|
||||
{downloadUris && downloadUris.length ? (
|
||||
downloadUris.map(uri => <FileTile hideNoResult key={uri} uri={uri} />)
|
||||
<FileTile key={uri} uri={uri} />
|
||||
)
|
||||
)
|
||||
) : (
|
||||
<NoResults />
|
||||
)}
|
||||
|
|
|
@ -156,10 +156,12 @@ class FileTile extends React.PureComponent<Props> {
|
|||
<Fragment>
|
||||
<div className="media__title">
|
||||
{(title || name) && (
|
||||
<TruncatedText text={title || name} lines={size === 'small' ? 2 : 3} />
|
||||
<TruncatedText text={title || name} lines={size !== 'small' ? 1 : 2} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{size === 'small' && this.renderFileProperties()}
|
||||
|
||||
{size !== 'small' ? (
|
||||
<div className="media__subtext">
|
||||
{__('Published to')} <UriIndicator uri={uri} link />{' '}
|
||||
|
@ -169,10 +171,10 @@ class FileTile extends React.PureComponent<Props> {
|
|||
<Fragment>
|
||||
<div className="media__subtext">
|
||||
<UriIndicator uri={uri} link />
|
||||
</div>
|
||||
<div className="media__subtext">
|
||||
<div>
|
||||
<DateTime timeAgo block={height} />
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
|
@ -184,7 +186,7 @@ class FileTile extends React.PureComponent<Props> {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{this.renderFileProperties()}
|
||||
{size !== 'small' && this.renderFileProperties()}
|
||||
|
||||
{!name && (
|
||||
<Yrbl
|
||||
|
|
16
src/renderer/component/searchOptions/index.js
Normal file
16
src/renderer/component/searchOptions/index.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { selectSearchOptions, doUpdateSearchOptions } from 'lbry-redux';
|
||||
import SearchOptions from './view';
|
||||
|
||||
const select = state => ({
|
||||
options: selectSearchOptions(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
setSearchOption: (option, value) => dispatch(doUpdateSearchOptions({ [option]: value })),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
select,
|
||||
perform
|
||||
)(SearchOptions);
|
123
src/renderer/component/searchOptions/view.jsx
Normal file
123
src/renderer/component/searchOptions/view.jsx
Normal file
|
@ -0,0 +1,123 @@
|
|||
// @flow
|
||||
import * as ICONS from 'constants/icons';
|
||||
import React, { useState } from 'react';
|
||||
import { SEARCH_OPTIONS } from 'lbry-redux';
|
||||
import { Form, FormField } from 'component/common/form';
|
||||
import posed from 'react-pose';
|
||||
import Button from 'component/button';
|
||||
|
||||
const ExpandableOptions = posed.div({
|
||||
hide: { height: 0, opacity: 0 },
|
||||
show: { height: 280, opacity: 1 },
|
||||
});
|
||||
|
||||
type Props = {
|
||||
setSearchOption: (string, boolean | string | number) => void,
|
||||
options: {},
|
||||
};
|
||||
|
||||
const SearchOptions = (props: Props) => {
|
||||
const { options, setSearchOption } = props;
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const resultCount = options[SEARCH_OPTIONS.RESULT_COUNT];
|
||||
|
||||
return (
|
||||
<div className="card card--section search__options-wrapper">
|
||||
<div className="card--space-between">
|
||||
<Button
|
||||
label={__('SEARCH OPTIONS')}
|
||||
icon={ICONS.OPTIONS}
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
/>
|
||||
{/*
|
||||
Will be added back when api is ready
|
||||
<div className="media__action-group">
|
||||
<span>{__('Find what you were looking for?')}</span>
|
||||
<Button description={__('Yes')} icon={ICONS.YES} />
|
||||
<Button description={__('No')} icon={ICONS.NO} />
|
||||
</div> */}
|
||||
</div>
|
||||
<ExpandableOptions pose={expanded ? 'show' : 'hide'}>
|
||||
{expanded && (
|
||||
<Form className="card__content search__options">
|
||||
<fieldset>
|
||||
<legend className="search__legend--1">{__('Search For')}</legend>
|
||||
{[
|
||||
{
|
||||
option: SEARCH_OPTIONS.INCLUDE_FILES,
|
||||
label: __('Files'),
|
||||
},
|
||||
{
|
||||
option: SEARCH_OPTIONS.INCLUDE_CHANNELS,
|
||||
label: __('Channels'),
|
||||
},
|
||||
{
|
||||
option: SEARCH_OPTIONS.INCLUDE_FILES_AND_CHANNELS,
|
||||
label: __('Everything'),
|
||||
},
|
||||
].map(({ option, label }) => (
|
||||
<FormField
|
||||
key={option}
|
||||
type="radio"
|
||||
blockWrap={false}
|
||||
label={label}
|
||||
checked={options[SEARCH_OPTIONS.CLAIM_TYPE] === option}
|
||||
onChange={() => setSearchOption(SEARCH_OPTIONS.CLAIM_TYPE, option)}
|
||||
/>
|
||||
))}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend className="search__legend--2">{__('File Types')}</legend>
|
||||
{[
|
||||
{
|
||||
option: SEARCH_OPTIONS.MEDIA_VIDEO,
|
||||
label: __('Videos'),
|
||||
},
|
||||
{
|
||||
option: SEARCH_OPTIONS.MEDIA_AUDIO,
|
||||
label: __('Sounds'),
|
||||
},
|
||||
{
|
||||
option: SEARCH_OPTIONS.MEDIA_IMAGE,
|
||||
label: __('Images'),
|
||||
},
|
||||
{
|
||||
option: SEARCH_OPTIONS.MEDIA_TEXT,
|
||||
label: __('Text'),
|
||||
},
|
||||
{
|
||||
option: SEARCH_OPTIONS.MEDIA_APPLICATION,
|
||||
label: __('Other Files'),
|
||||
},
|
||||
].map(({ option, label }) => (
|
||||
<FormField
|
||||
key={option}
|
||||
type="checkbox"
|
||||
blockWrap={false}
|
||||
disabled={options[SEARCH_OPTIONS.CLAIM_TYPE] === SEARCH_OPTIONS.INCLUDE_CHANNELS}
|
||||
label={label}
|
||||
checked={options[option]}
|
||||
onChange={() => setSearchOption(option, !options[option])}
|
||||
/>
|
||||
))}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend className="search__legend--3">{__('Other Options')}</legend>
|
||||
<FormField
|
||||
type="number"
|
||||
value={resultCount}
|
||||
onChange={e => setSearchOption(SEARCH_OPTIONS.RESULT_COUNT, e.target.value)}
|
||||
blockWrap={false}
|
||||
label={__('Returned Results')}
|
||||
/>
|
||||
</fieldset>
|
||||
</Form>
|
||||
)}
|
||||
</ExpandableOptions>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchOptions;
|
|
@ -10,8 +10,6 @@ import {
|
|||
doToast,
|
||||
} from 'lbry-redux';
|
||||
import analytics from 'analytics';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import * as settings from 'constants/settings';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import Wunderbar from './view';
|
||||
|
||||
|
@ -27,13 +25,12 @@ const select = state => {
|
|||
...searchState,
|
||||
wunderbarValue,
|
||||
suggestions: selectSearchSuggestions(state),
|
||||
resultCount: makeSelectClientSetting(settings.RESULT_COUNT)(state),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = dispatch => ({
|
||||
onSearch: (query, size) => {
|
||||
dispatch(doSearch(query, size));
|
||||
onSearch: query => {
|
||||
dispatch(doSearch(query));
|
||||
dispatch(doNavigate(`/search`, { query }));
|
||||
analytics.apiLogSearch();
|
||||
},
|
||||
|
|
|
@ -12,13 +12,12 @@ const ESC_KEY_CODE = 27;
|
|||
|
||||
type Props = {
|
||||
updateSearchQuery: string => void,
|
||||
onSearch: (string, ?number) => void,
|
||||
onSearch: string => void,
|
||||
onSubmit: (string, {}) => void,
|
||||
wunderbarValue: ?string,
|
||||
suggestions: Array<string>,
|
||||
doFocus: () => void,
|
||||
doBlur: () => void,
|
||||
resultCount: number,
|
||||
focused: boolean,
|
||||
doShowSnackBar: ({}) => void,
|
||||
};
|
||||
|
@ -82,7 +81,7 @@ class WunderBar extends React.PureComponent<Props> {
|
|||
}
|
||||
|
||||
handleSubmit(value: string, suggestion?: { value: string, type: string }) {
|
||||
const { onSubmit, onSearch, resultCount } = this.props;
|
||||
const { onSubmit, onSearch } = this.props;
|
||||
const query = value.trim();
|
||||
const getParams = () => {
|
||||
const parts = query.split('?');
|
||||
|
@ -98,7 +97,7 @@ class WunderBar extends React.PureComponent<Props> {
|
|||
// User selected a suggestion
|
||||
if (suggestion) {
|
||||
if (suggestion.type === 'search') {
|
||||
onSearch(query, resultCount);
|
||||
onSearch(query);
|
||||
} else if (isURIValid(query)) {
|
||||
const params = getParams();
|
||||
const uri = normalizeURI(query);
|
||||
|
@ -125,7 +124,7 @@ class WunderBar extends React.PureComponent<Props> {
|
|||
});
|
||||
}
|
||||
} catch (e) {
|
||||
onSearch(query, resultCount);
|
||||
onSearch(query);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,3 +41,6 @@ export const WALLET = 'CreditCard';
|
|||
export const SETTINGS = 'Settings';
|
||||
export const INVITE = 'Users';
|
||||
export const FILE = 'File';
|
||||
export const OPTIONS = 'Sliders';
|
||||
export const YES = 'ThumbsUp';
|
||||
export const NO = 'ThumbsDown';
|
||||
|
|
|
@ -1,22 +1,16 @@
|
|||
import { connect } from 'react-redux';
|
||||
import * as settings from 'constants/settings';
|
||||
import { selectIsSearching, makeSelectCurrentParam, doUpdateSearchQuery } from 'lbry-redux';
|
||||
import { doSetClientSetting } from 'redux/actions/settings';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import SearchPage from './view';
|
||||
|
||||
const select = state => ({
|
||||
isSearching: selectIsSearching(state),
|
||||
query: makeSelectCurrentParam('query')(state),
|
||||
showUnavailable: makeSelectClientSetting(settings.SHOW_UNAVAILABLE)(state),
|
||||
resultCount: makeSelectClientSetting(settings.RESULT_COUNT)(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),
|
||||
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// @flow
|
||||
import * as SETTINGS from 'constants/settings';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import * as React from 'react';
|
||||
import { isURIValid, normalizeURI, parseURI } from 'lbry-redux';
|
||||
|
@ -9,32 +8,15 @@ import FileListSearch from 'component/fileListSearch';
|
|||
import Page from 'component/page';
|
||||
import ToolTip from 'component/common/tooltip';
|
||||
import Icon from 'component/common/icon';
|
||||
import SearchOptions from 'component/searchOptions';
|
||||
|
||||
type Props = {
|
||||
query: ?string,
|
||||
resultCount: number,
|
||||
setClientSetting: (string, number | boolean) => void,
|
||||
};
|
||||
|
||||
class SearchPage extends React.PureComponent<Props> {
|
||||
constructor() {
|
||||
super();
|
||||
(this: any).onShowUnavailableChange = this.onShowUnavailableChange.bind(this);
|
||||
(this: any).onSearchResultCountChange = this.onSearchResultCountChange.bind(this);
|
||||
}
|
||||
|
||||
onSearchResultCountChange(event: SyntheticInputEvent<*>) {
|
||||
const count = Number(event.target.value);
|
||||
this.props.setClientSetting(SETTINGS.RESULT_COUNT, count);
|
||||
}
|
||||
|
||||
onShowUnavailableChange(event: SyntheticInputEvent<*>) {
|
||||
this.props.setClientSetting(SETTINGS.SHOW_UNAVAILABLE, event.target.checked);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { query, resultCount } = this.props;
|
||||
|
||||
const { query } = this.props;
|
||||
const isValid = isURIValid(query);
|
||||
|
||||
let uri;
|
||||
|
@ -69,14 +51,9 @@ class SearchPage extends React.PureComponent<Props> {
|
|||
</header>
|
||||
)}
|
||||
|
||||
{/*
|
||||
Commented out until I figure out what to do with it in my next PR
|
||||
<div>
|
||||
<FormField type="text" value={resultCount} label={__("Returned results")} /
|
||||
</div>
|
||||
*/}
|
||||
|
||||
<div className="search__results-wrapper">
|
||||
<SearchOptions />
|
||||
|
||||
<FileListSearch query={query} />
|
||||
<div className="help">{__('These search results are provided by LBRY, Inc.')}</div>
|
||||
</div>
|
||||
|
|
|
@ -97,7 +97,11 @@
|
|||
}
|
||||
|
||||
.button--uri-indicator {
|
||||
color: rgba($lbry-white, 0.9);
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
transition: color 0.2s;
|
||||
|
||||
&:hover {
|
||||
|
|
|
@ -2,6 +2,17 @@
|
|||
|
||||
// lbry/components overrides and minor styles
|
||||
|
||||
input[type='number'] {
|
||||
padding: var(--spacing-s);
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
checkbox-element {
|
||||
&[disabled='true'] {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
checkbox-element,
|
||||
radio-element,
|
||||
fieldset:last-child,
|
||||
|
@ -9,6 +20,21 @@ fieldset-section:last-child {
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
checkbox-element,
|
||||
radio-element {
|
||||
input[type='checkbox']:checked + label {
|
||||
color: $lbry-black;
|
||||
|
||||
[data-mode='dark'] & {
|
||||
color: $lbry-white;
|
||||
|
||||
&:hover {
|
||||
color: $lbry-teal-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fieldset-group.fieldset-group--smushed {
|
||||
justify-content: flex-start;
|
||||
|
||||
|
@ -41,6 +67,10 @@ form {
|
|||
background-color: $lbry-teal-5;
|
||||
border-color: $lbry-teal-5;
|
||||
}
|
||||
|
||||
legend {
|
||||
background-color: $lbry-cyan-5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
.media-tile {
|
||||
display: flex;
|
||||
font-size: 1.5rem;
|
||||
position: relative;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
margin-bottom: var(--spacing-vertical-large);
|
||||
|
@ -26,6 +27,7 @@
|
|||
.media__info {
|
||||
margin-left: var(--spacing-vertical-medium);
|
||||
width: calc(80% - 20rem);
|
||||
min-width: 40rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +40,7 @@
|
|||
|
||||
.media__info {
|
||||
margin-left: var(--spacing-vertical-large);
|
||||
width: calc(80% - 30rem);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.media__subtext {
|
||||
|
@ -54,21 +56,26 @@
|
|||
}
|
||||
|
||||
.media__thumb {
|
||||
width: 10em;
|
||||
width: 11em;
|
||||
}
|
||||
|
||||
.media__info {
|
||||
// padding-left: var(--spacing-vertical-medium);
|
||||
width: calc(100% - 10em);
|
||||
min-width: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.media__title {
|
||||
margin-bottom: var(--spacing-vertical-small);
|
||||
}
|
||||
|
||||
.media__subtext:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.media__properties {
|
||||
bottom: -1.5rem;
|
||||
left: calc(-100% - 1.5rem);
|
||||
bottom: 0.5rem;
|
||||
left: calc(-100% - -2rem);
|
||||
position: absolute;
|
||||
padding: 0 var(--spacing-vertical-small);
|
||||
border-radius: 5px;
|
||||
|
@ -143,7 +150,7 @@
|
|||
|
||||
.media__action-group {
|
||||
> *:not(:last-child) {
|
||||
margin-right: var(--spacing-vertical-large);
|
||||
margin-right: var(--spacing-vertical-medium);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,6 +200,11 @@
|
|||
|
||||
.media__subtitle {
|
||||
font-size: 0.8em;
|
||||
color: rgba($lbry-black, 0.8);
|
||||
|
||||
[data-mode='dark'] & {
|
||||
color: rgba($lbry-white, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.media__subtitle--large {
|
||||
|
@ -412,7 +424,7 @@
|
|||
color: $lbry-white;
|
||||
}
|
||||
|
||||
.media__subtext {
|
||||
.media__subtitle {
|
||||
color: mix($lbry-cyan-5, $lbry-white, 20%);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
color: rgba($lbry-white, 0.6);
|
||||
}
|
||||
|
||||
.media__subtitle {
|
||||
color: rgba($lbry-white, 0.9);
|
||||
}
|
||||
|
||||
html[data-mode='dark'] & {
|
||||
background-color: transparent;
|
||||
border-bottom: 1px solid rgba($lbry-white, 0.1);
|
||||
|
@ -46,3 +50,43 @@
|
|||
@extend .media-group__header-title;
|
||||
margin-bottom: var(--spacing-vertical-large);
|
||||
}
|
||||
|
||||
.search__options-wrapper {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
.search__options {
|
||||
margin-top: var(--spacing-vertical-large);
|
||||
|
||||
legend {
|
||||
&.search__legend--1 {
|
||||
background-color: $lbry-teal-1;
|
||||
}
|
||||
|
||||
&.search__legend--2 {
|
||||
background-color: $lbry-cyan-1;
|
||||
}
|
||||
|
||||
&.search__legend--3 {
|
||||
background-color: $lbry-pink-1;
|
||||
}
|
||||
|
||||
[data-mode='dark'] & {
|
||||
&.search__legend--1 {
|
||||
background-color: $lbry-teal-5;
|
||||
}
|
||||
|
||||
&.search__legend--2 {
|
||||
background-color: $lbry-cyan-5;
|
||||
}
|
||||
|
||||
&.search__legend--3 {
|
||||
background-color: $lbry-pink-5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fieldset:not(:first-child) {
|
||||
margin-top: var(--spacing-vertical-large);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,9 @@ code {
|
|||
}
|
||||
|
||||
.truncated-text {
|
||||
@include truncate;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.busy-indicator__loader {
|
||||
|
|
|
@ -1,30 +1,3 @@
|
|||
@mixin between {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
@mixin ellipsis {
|
||||
// to take over for truncate on LBRY Web
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@mixin font-mono {
|
||||
font-family: Inconsolata, 'Fira Mono', 'Droid Sans Mono', 'Source Code Pro', Consolas,
|
||||
'Lucida Console', 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
@mixin font-sans {
|
||||
font-family: 'Inter UI', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial,
|
||||
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
}
|
||||
|
||||
@mixin font-serif {
|
||||
font-family: 'Apple Garamond', Baskerville, Georgia, 'Times New Roman', 'Droid Serif', Times,
|
||||
'Source Serif Pro', serif;
|
||||
}
|
||||
|
||||
@mixin placeholder {
|
||||
animation: pulse 2s infinite ease-in-out;
|
||||
background-color: $lbry-gray-2;
|
||||
|
@ -33,26 +6,3 @@
|
|||
background-color: rgba($lbry-white, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin thumbnail {
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
}
|
||||
|
||||
&::before {
|
||||
float: left;
|
||||
padding-top: var(--video-aspect-ratio);
|
||||
}
|
||||
|
||||
&::after {
|
||||
clear: both;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin truncate {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
|
|
@ -102,12 +102,13 @@ const fileInfoFilter = createFilter('fileInfo', [
|
|||
const appFilter = createFilter('app', ['hasClickedComment']);
|
||||
// We only need to persist the receiveAddress for the wallet
|
||||
const walletFilter = createFilter('wallet', ['receiveAddress']);
|
||||
const searchFilter = createFilter('search', ['options']);
|
||||
|
||||
const persistOptions = {
|
||||
whitelist: ['subscriptions', 'publish', 'wallet', 'content', 'fileInfo', 'app'],
|
||||
whitelist: ['subscriptions', 'publish', 'wallet', 'content', 'fileInfo', 'app', 'search'],
|
||||
// Order is important. Needs to be compressed last or other transforms can't
|
||||
// read the data
|
||||
transforms: [walletFilter, contentFilter, fileInfoFilter, appFilter, compressor],
|
||||
transforms: [walletFilter, contentFilter, fileInfoFilter, appFilter, searchFilter, compressor],
|
||||
debounce: 10000,
|
||||
storage: localForage,
|
||||
};
|
||||
|
|
36
yarn.lock
36
yarn.lock
|
@ -5660,9 +5660,9 @@ lazy-val@^1.0.3:
|
|||
tar-stream "^1.6.2"
|
||||
zstd-codec "^0.1.1"
|
||||
|
||||
lbry-redux@lbryio/lbry-redux#42c185e922a7c6091b0e1580bacbfd8e02f45a91:
|
||||
lbry-redux@lbryio/lbry-redux#2b725cb31729234ba73117e2a74688b8bba26e7c:
|
||||
version "0.0.1"
|
||||
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/42c185e922a7c6091b0e1580bacbfd8e02f45a91"
|
||||
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/2b725cb31729234ba73117e2a74688b8bba26e7c"
|
||||
dependencies:
|
||||
proxy-polyfill "0.1.6"
|
||||
reselect "^3.0.0"
|
||||
|
@ -7868,14 +7868,15 @@ rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.2.7:
|
|||
minimist "^1.2.0"
|
||||
strip-json-comments "~2.0.1"
|
||||
|
||||
react-dom@^16.6.0:
|
||||
version "16.7.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0.tgz#a17b2a7ca89ee7390bc1ed5eb81783c7461748b8"
|
||||
react-dom@^16.8.2:
|
||||
version "16.8.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.2.tgz#7c8a69545dd554d45d66442230ba04a6a0a3c3d3"
|
||||
integrity sha512-cPGfgFfwi+VCZjk73buu14pYkYBR1b/SRMSYqkLDdhSEHnSwcuYTPu6/Bh6ZphJFIk80XLvbSe2azfcRzNF+Xg==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
scheduler "^0.12.0"
|
||||
scheduler "^0.13.2"
|
||||
|
||||
react-feather@^1.0.8:
|
||||
version "1.1.1"
|
||||
|
@ -7952,14 +7953,15 @@ react@^0.14.5:
|
|||
envify "^3.0.0"
|
||||
fbjs "^0.6.1"
|
||||
|
||||
react@^16.6.0:
|
||||
version "16.7.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.7.0.tgz#b674ec396b0a5715873b350446f7ea0802ab6381"
|
||||
react@^16.8.2:
|
||||
version "16.8.2"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.8.2.tgz#83064596feaa98d9c2857c4deae1848b542c9c0c"
|
||||
integrity sha512-aB2ctx9uQ9vo09HVknqv3DGRpI7OIGJhCx3Bt0QqoRluEjHSaObJl+nG12GDdYH6sTgE7YiPJ6ZUyMx9kICdXw==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
scheduler "^0.12.0"
|
||||
scheduler "^0.13.2"
|
||||
|
||||
read-config-file@3.1.0, read-config-file@^3.0.0:
|
||||
version "3.1.0"
|
||||
|
@ -8532,13 +8534,6 @@ sass-loader@^6.0.7:
|
|||
neo-async "^2.5.0"
|
||||
pify "^3.0.0"
|
||||
|
||||
sass@^1.17.0:
|
||||
version "1.17.0"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.17.0.tgz#e370b9302af121c9eadad5639619127772094ae6"
|
||||
integrity sha512-aFi9RQqrCYkHB2DaLKBBbdUhos1N5o3l1ke9N5JqWzgSPmYwZsdmA+ViPVatUy/RPA21uejgYVUXM7GCh8lcdw==
|
||||
dependencies:
|
||||
chokidar "^2.0.0"
|
||||
|
||||
sax@1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
|
||||
|
@ -8551,9 +8546,10 @@ sax@~1.1.1:
|
|||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.6.tgz#5d616be8a5e607d54e114afae55b7eaf2fcc3240"
|
||||
|
||||
scheduler@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.12.0.tgz#8ab17699939c0aedc5a196a657743c496538647b"
|
||||
scheduler@^0.13.2:
|
||||
version "0.13.2"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.2.tgz#969eaee2764a51d2e97b20a60963b2546beff8fa"
|
||||
integrity sha512-qK5P8tHS7vdEMCW5IPyt8v9MJOHqTrOUgPXib7tqm9vh834ibBX5BNhwkplX/0iOzHW5sXyluehYfS9yrkz9+w==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
|
Loading…
Reference in a new issue