lbry-desktop/ui/component/wunderbar/view.jsx

226 lines
6.5 KiB
React
Raw Normal View History

2018-03-26 23:32:43 +02:00
// @flow
import { URL, URL_LOCAL, URL_DEV } from 'config';
2019-03-28 17:53:13 +01:00
import * as PAGES from 'constants/pages';
2018-11-26 02:21:25 +01:00
import * as ICONS from 'constants/icons';
import React from 'react';
2018-03-26 23:32:43 +02:00
import classnames from 'classnames';
2019-07-11 20:16:39 +02:00
import { normalizeURI, SEARCH_TYPES, isURIValid } from 'lbry-redux';
import { withRouter } from 'react-router';
2018-03-26 23:32:43 +02:00
import Icon from 'component/common/icon';
2018-03-27 23:25:23 +02:00
import Autocomplete from './internal/autocomplete';
2019-07-11 20:16:39 +02:00
import Tag from 'component/tag';
2018-03-26 23:32:43 +02:00
2018-10-04 07:59:47 +02:00
const L_KEY_CODE = 76;
const ESC_KEY_CODE = 27;
const WEB_DEV_PREFIX = `${URL_DEV}/`;
const WEB_LOCAL_PREFIX = `${URL_LOCAL}/`;
const WEB_PROD_PREFIX = `${URL}/`;
const SEARCH_PREFIX = `$/${PAGES.SEARCH}q=`;
2018-10-04 07:59:47 +02:00
2018-03-26 23:32:43 +02:00
type Props = {
2019-03-28 17:53:13 +01:00
searchQuery: ?string,
2018-03-26 23:32:43 +02:00
updateSearchQuery: string => void,
2019-02-18 18:24:56 +01:00
onSearch: string => void,
2019-03-28 17:53:13 +01:00
onSubmit: string => void,
2018-03-26 23:32:43 +02:00
wunderbarValue: ?string,
suggestions: Array<string>,
2018-05-16 20:32:25 +02:00
doFocus: () => void,
doBlur: () => void,
2018-10-04 07:59:47 +02:00
focused: boolean,
doShowSnackBar: string => void,
2019-07-11 20:16:39 +02:00
history: { push: string => void },
2018-03-26 23:32:43 +02:00
};
2019-03-28 17:53:13 +01:00
type State = {
query: ?string,
};
class WunderBar extends React.PureComponent<Props, State> {
2018-10-04 07:59:47 +02:00
constructor() {
super();
2017-05-04 05:44:08 +02:00
2019-03-28 17:53:13 +01:00
this.state = {
query: null,
};
2018-03-26 23:32:43 +02:00
(this: any).handleSubmit = this.handleSubmit.bind(this);
(this: any).handleChange = this.handleChange.bind(this);
2018-10-05 20:20:28 +02:00
(this: any).handleKeyDown = this.handleKeyDown.bind(this);
2018-10-04 07:59:47 +02:00
}
componentDidMount() {
2018-10-05 20:20:28 +02:00
window.addEventListener('keydown', this.handleKeyDown);
2018-10-04 07:59:47 +02:00
}
componentWillUnmount() {
window.removeEventListener('keydown', this.handleKeyDown);
}
2018-03-26 23:32:43 +02:00
getSuggestionIcon = (type: string) => {
switch (type) {
2019-07-11 20:16:39 +02:00
case SEARCH_TYPES.FILE:
return ICONS.FILE;
2019-07-11 20:16:39 +02:00
case SEARCH_TYPES.CHANNEL:
2019-01-22 21:36:28 +01:00
return ICONS.CHANNEL;
2019-07-11 20:16:39 +02:00
case SEARCH_TYPES.TAG:
return ICONS.TAG;
2018-03-26 23:32:43 +02:00
default:
2018-11-26 02:21:25 +01:00
return ICONS.SEARCH;
}
2018-03-26 23:32:43 +02:00
};
2017-05-04 05:44:08 +02:00
2018-10-04 07:59:47 +02:00
handleKeyDown(event: SyntheticKeyboardEvent<*>) {
const { ctrlKey, metaKey, keyCode } = event;
const { doFocus, doBlur, focused } = this.props;
2019-01-19 19:54:06 +01:00
if (this.input) {
if (focused && keyCode === ESC_KEY_CODE) {
this.input.blur();
doBlur();
return;
}
2018-10-04 07:59:47 +02:00
2019-03-28 17:53:13 +01:00
// @if TARGET='app'
2019-01-19 19:54:06 +01:00
const shouldFocus =
2019-05-07 23:38:29 +02:00
process.platform === 'darwin' ? keyCode === L_KEY_CODE && metaKey : keyCode === L_KEY_CODE && ctrlKey;
2018-10-04 07:59:47 +02:00
2019-01-19 19:54:06 +01:00
if (shouldFocus) {
this.input.focus();
doFocus();
}
2019-03-28 17:53:13 +01:00
// @endif
2018-10-04 07:59:47 +02:00
}
}
2018-03-26 23:32:43 +02:00
handleChange(e: SyntheticInputEvent<*>) {
const { value } = e.target;
2019-03-28 17:53:13 +01:00
const { updateSearchQuery } = this.props;
2018-03-26 23:32:43 +02:00
updateSearchQuery(value);
}
2018-03-26 23:32:43 +02:00
handleSubmit(value: string, suggestion?: { value: string, type: string }) {
2019-07-11 20:16:39 +02:00
const { onSubmit, onSearch, doShowSnackBar, history } = this.props;
let query = value.trim();
2019-09-03 20:59:59 +02:00
this.input && this.input.blur();
2019-03-15 00:42:49 +01:00
const showSnackError = () => {
doShowSnackBar('Invalid LBRY URL entered. Only A-Z, a-z, 0-9, and "-" allowed.');
2019-03-15 00:42:49 +01:00
};
// Allow copying a lbry.tv url and pasting it into the search bar
const includesLbryTvProd = query.includes(WEB_PROD_PREFIX);
const includesLbryTvLocal = query.includes(WEB_LOCAL_PREFIX);
const includesLbryTvDev = query.includes(WEB_DEV_PREFIX);
const wasCopiedFromWeb = includesLbryTvDev || includesLbryTvLocal || includesLbryTvProd;
if (wasCopiedFromWeb) {
if (includesLbryTvDev) {
query = query.slice(WEB_DEV_PREFIX.length);
} else if (includesLbryTvLocal) {
query = query.slice(WEB_LOCAL_PREFIX.length);
} else {
query = query.slice(WEB_PROD_PREFIX.length);
}
query = query.replace(/:/g, '#');
if (query.includes(SEARCH_PREFIX)) {
query = query.slice(SEARCH_PREFIX.length);
onSearch(query);
return;
} else {
query = `lbry://${query}`;
onSubmit(query);
return;
}
}
2018-03-26 23:32:43 +02:00
// User selected a suggestion
if (suggestion) {
2019-07-11 20:16:39 +02:00
if (suggestion.type === SEARCH_TYPES.SEARCH) {
2019-02-18 18:24:56 +01:00
onSearch(query);
2019-07-11 20:16:39 +02:00
} else if (suggestion.type === SEARCH_TYPES.TAG) {
history.push(`/$/${PAGES.TAGS}?t=${suggestion.value}`);
2018-10-18 23:05:32 +02:00
} else if (isURIValid(query)) {
const uri = normalizeURI(query);
2019-03-28 17:53:13 +01:00
onSubmit(uri);
} else {
2019-03-15 00:42:49 +01:00
showSnackError();
}
2017-05-04 05:44:08 +02:00
2018-03-26 23:32:43 +02:00
return;
2017-05-04 05:44:08 +02:00
}
2018-03-26 23:32:43 +02:00
// Currently no suggestion is highlighted. The user may have started
// typing, then lost focus and came back later on the same page
try {
if (isURIValid(query)) {
const uri = normalizeURI(query);
2019-03-28 17:53:13 +01:00
onSubmit(uri);
} else {
2019-03-15 00:42:49 +01:00
showSnackError();
}
2018-03-26 23:32:43 +02:00
} catch (e) {
2019-02-18 18:24:56 +01:00
onSearch(query);
2018-01-04 06:05:20 +01:00
}
2017-05-04 05:44:08 +02:00
}
2018-03-26 23:32:43 +02:00
input: ?HTMLInputElement;
2018-01-04 06:05:20 +01:00
render() {
2019-03-28 17:53:13 +01:00
const { suggestions, doFocus, doBlur, searchQuery } = this.props;
2018-03-26 23:32:43 +02:00
2017-05-04 05:44:08 +02:00
return (
2018-03-26 23:32:43 +02:00
<div className="wunderbar">
2018-11-26 02:21:25 +01:00
<Icon icon={ICONS.SEARCH} />
2018-03-26 23:32:43 +02:00
<Autocomplete
autoHighlight
2018-06-21 00:58:55 +02:00
wrapperStyle={{ flex: 1, position: 'relative' }}
2019-03-28 17:53:13 +01:00
value={searchQuery}
2018-03-26 23:32:43 +02:00
items={suggestions}
getItemValue={item => item.value}
onChange={this.handleChange}
onSelect={this.handleSubmit}
2018-05-16 20:32:25 +02:00
inputProps={{
onFocus: doFocus,
onBlur: doBlur,
}}
2018-03-26 23:32:43 +02:00
renderInput={props => (
<input
{...props}
2018-10-04 07:59:47 +02:00
ref={el => {
props.ref(el);
this.input = el;
}}
2018-03-26 23:32:43 +02:00
className="wunderbar__input"
2019-10-11 16:13:07 +02:00
placeholder={__('Enter a LBRY URL or search for videos, music, games and more')}
2018-03-26 23:32:43 +02:00
/>
)}
2018-06-19 19:59:55 +02:00
renderItem={({ value, type }, isHighlighted) => (
2018-03-26 23:32:43 +02:00
<div
2019-07-11 20:16:39 +02:00
// Use value + type for key because there might be suggestions with same value but different type
key={`${value}-${type}`}
2018-03-26 23:32:43 +02:00
className={classnames('wunderbar__suggestion', {
'wunderbar__active-suggestion': isHighlighted,
})}
>
<Icon icon={this.getSuggestionIcon(type)} />
2019-07-11 20:16:39 +02:00
<span className="wunderbar__suggestion-label">
{type === SEARCH_TYPES.TAG ? <Tag name={value} /> : value}
</span>
2018-06-19 19:59:55 +02:00
{isHighlighted && (
2018-03-26 23:32:43 +02:00
<span className="wunderbar__suggestion-label--action">
2018-06-19 19:59:55 +02:00
{type === SEARCH_TYPES.SEARCH && __('Search')}
{type === SEARCH_TYPES.CHANNEL && __('View channel')}
{type === SEARCH_TYPES.FILE && __('View file')}
2019-07-11 20:16:39 +02:00
{type === SEARCH_TYPES.TAG && __('View Tag')}
2018-03-26 23:32:43 +02:00
</span>
)}
</div>
)}
2017-06-06 23:19:12 +02:00
/>
2017-05-04 05:44:08 +02:00
</div>
);
}
}
2019-07-11 20:16:39 +02:00
export default withRouter(WunderBar);