diff --git a/src/renderer/component/wunderbar/view.jsx b/src/renderer/component/wunderbar/view.jsx index ec732f12f54..6db283e392b 100644 --- a/src/renderer/component/wunderbar/view.jsx +++ b/src/renderer/component/wunderbar/view.jsx @@ -1,175 +1,139 @@ import React from "react"; -import PropTypes from "prop-types"; import lbryuri from "lbryuri.js"; import { Icon } from "component/common.js"; import { parseQueryParams } from "util/query_params"; +import classnames from "classnames"; + +// type Props = { +// onSearch: () => void, +// onSubmit: () => void +// } class WunderBar extends React.PureComponent { static TYPING_TIMEOUT = 800; - static propTypes = { - onSearch: PropTypes.func.isRequired, - onSubmit: PropTypes.func.isRequired, - }; - constructor(props) { super(props); this._userTypingTimer = null; - this._isSearchDispatchPending = false; this._input = null; - this._stateBeforeSearch = null; - this._resetOnNextBlur = true; + this.onChange = this.onChange.bind(this); this.onFocus = this.onFocus.bind(this); this.onBlur = this.onBlur.bind(this); this.onKeyPress = this.onKeyPress.bind(this); - this.onReceiveRef = this.onReceiveRef.bind(this); + this.state = { - address: this.props.address, - icon: this.props.icon, + value: "", + isSearchPending: false, + isActive: false, }; } componentWillUnmount() { - if (this.userTypingTimer) { + if (this._userTypingTimer) { clearTimeout(this._userTypingTimer); } + + this._input = null; } onChange(event) { + const { value } = event.target; + if (this._userTypingTimer) { clearTimeout(this._userTypingTimer); } - this.setState({ address: event.target.value }); + let newState = { value }; - this._isSearchDispatchPending = true; + let uri; + try { + uri = lbryuri.normalize(value); + newState.uri = uri; + } catch (e) { + // search term isn't a valid uri (has spaces in it / other stuff) + newState.uri = ""; + } - let searchQuery = event.target.value; + this.setState(newState); this._userTypingTimer = setTimeout(() => { - const hasQuery = searchQuery.length === 0; - this._resetOnNextBlur = hasQuery; - this._isSearchDispatchPending = false; - if (searchQuery) { - this.props.onSearch(searchQuery.trim()); - } + // pull suggested search results/autocomplete value (same api call for both?) }, WunderBar.TYPING_TIMEOUT); // 800ms delay, tweak for faster/slower } - componentWillReceiveProps(nextProps) { - if ( - nextProps.viewingPage !== this.props.viewingPage || - nextProps.address != this.props.address - ) { - this.setState({ address: nextProps.address, icon: nextProps.icon }); - } - } - onFocus() { - this._stateBeforeSearch = this.state; - let newState = { - icon: "icon-search", - isActive: true, - }; + const { value } = this.state; - this._focusPending = true; - //below is hacking, improved when we have proper routing - if ( - !this.state.address.startsWith("lbry://") && - this.state.icon !== "icon-search" - ) { - //onFocus, if they are not on an exact URL or a search page, clear the bar - newState.address = ""; + if (value) { + // will need to do something for suggested results here } - this.setState(newState); + + this.setState({ isActive: true }); } onBlur() { - if (this._isSearchDispatchPending) { - setTimeout(() => { - this.onBlur(); - }, WunderBar.TYPING_TIMEOUT + 1); - } else { - let commonState = { isActive: false }; - if (this._resetOnNextBlur) { - this.setState(Object.assign({}, this._stateBeforeSearch, commonState)); - this._input.value = this.state.address; - } else { - this._resetOnNextBlur = true; - this._stateBeforeSearch = this.state; - this.setState(commonState); - } + if (this._userTypingTimer) { + clearTimeout(this._userTypingTimer); } + + this.setState({ isActive: false }); } componentDidUpdate() { - if (this._input) { - const start = this._input.selectionStart, - end = this._input.selectionEnd; - - this._input.value = this.state.address; //this causes cursor to go to end of input - - this._input.setSelectionRange(start, end); - - if (this._focusPending) { - this._input.select(); - this._focusPending = false; - } - } + // if (this._input) { + // const start = this._input.selectionStart, + // end = this._input.selectionEnd; + // + // this._input.value = this.state.address; //this causes cursor to go to end of input + // + // this._input.setSelectionRange(start, end); + // + // if (this._focusPending) { + // this._input.select(); + // this._focusPending = false; + // } + // } } onKeyPress(event) { - if (event.charCode == 13 && this._input.value) { - let uri = null, - method = "onSubmit", - extraParams = {}; - - this._resetOnNextBlur = false; - clearTimeout(this._userTypingTimer); - - const parts = this._input.value.trim().split("?"); - const value = parts.shift(); - if (parts.length > 0) extraParams = parseQueryParams(parts.join("")); + const { value, uri } = this.state; + const { onSearch, onSubmit } = this.props; - try { - uri = lbryuri.normalize(value); - this.setState({ value: uri }); - } catch (error) { - //then it's not a valid URL, so let's search - uri = value; - method = "onSearch"; - } - - this.props[method](uri, extraParams); + // user pressed enter + if (event.charCode === 13 && value) { + this.setState({ isActive: false }); this._input.blur(); + return uri ? onSubmit(uri) : onSearch(value); } } - onReceiveRef(ref) { - this._input = ref; - } - render() { + const { value, uri, isActive, icon } = this.state; return (
- {this.state.icon ? : ""} + (this._input = ref)} onFocus={this.onFocus} onBlur={this.onBlur} onChange={this.onChange} onKeyPress={this.onKeyPress} - value={this.state.address} - placeholder={__("Search for videos, movies, games, and more")} + value={value} + placeholder={__("Search for videos, movies, and more")} /> +
+ {value && + isActive && ( +
    {uri &&
  • {uri}
  • }
+ )} +
); } diff --git a/src/renderer/page/search/view.jsx b/src/renderer/page/search/view.jsx index d6a69bf9c3a..5c7acde2bc6 100644 --- a/src/renderer/page/search/view.jsx +++ b/src/renderer/page/search/view.jsx @@ -3,13 +3,14 @@ import lbryuri from "lbryuri"; import FileTile from "component/fileTile"; import FileListSearch from "component/fileListSearch"; import { ToolTip } from "component/tooltip.js"; +import Page from "component/common/page"; class SearchPage extends React.PureComponent { render() { const { query } = this.props; return ( -
+ {lbryuri.isValid(query) ? (

@@ -41,7 +42,7 @@ class SearchPage extends React.PureComponent {

-
+ ); } } diff --git a/src/renderer/scss/component/_header.scss b/src/renderer/scss/component/_header.scss index f9e1770b1eb..8990a47196e 100644 --- a/src/renderer/scss/component/_header.scss +++ b/src/renderer/scss/component/_header.scss @@ -24,7 +24,7 @@ } .header__wunderbar { - width: 335px; + width: 325px; height: 100%; border-left: 1px solid #eee; position: relative; @@ -34,8 +34,32 @@ left: 10px; top: 26px; //hacked for icon centering } + + .header__wunderbar-search-results { + position: absolute; + top: var(--header-height); + width: 100%; + background: var(--header-bg); + box-shadow: var(--box-shadow-layer); + + ul { + list-style: none; + } + + li { + display: flex; + align-items: center; + padding: 10px; + + // always highlighted link to lbry uri + &.wunderbar__uri { + background-color: #a3ffb0 + } + } + } } + .wunderbar__input { width: 100%; height: 100%;