Search kind of working, but nasty
This commit is contained in:
parent
daabe975e1
commit
29c53a9bf8
7 changed files with 139 additions and 168 deletions
|
@ -13,6 +13,7 @@ import RewardsPage from 'page/rewards.js';
|
|||
import FileListDownloaded from 'page/fileListDownloaded'
|
||||
import FileListPublished from 'page/fileListPublished'
|
||||
import ChannelPage from 'page/channel'
|
||||
import SearchPage from 'page/search'
|
||||
|
||||
const route = (page, routesMap) => {
|
||||
const component = routesMap[page]
|
||||
|
@ -42,6 +43,7 @@ const Router = (props) => {
|
|||
'developer': <DeveloperPage {...props} />,
|
||||
'discover': <DiscoverPage {...props} />,
|
||||
'rewards': <RewardsPage {...props} />,
|
||||
'search': <SearchPage {...props} />,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ const perform = (dispatch) => ({
|
|||
// navigate: (path) => dispatch(doNavigate(path)),
|
||||
onSearch: (query) => dispatch(doSearchContent(query)),
|
||||
onSubmit: (query) => dispatch(doSearchContent(query)),
|
||||
// activateSearch: () => dispatch(doActivateSearch()),
|
||||
// deactivateSearch: () => setTimeout(() => { dispatch(doDeactivateSearch()) }, 50),
|
||||
activateSearch: () => dispatch(doActivateSearch()),
|
||||
deactivateSearch: () => setTimeout(() => { dispatch(doDeactivateSearch()) }, 50),
|
||||
})
|
||||
|
||||
export default connect(select, perform)(Wunderbar)
|
||||
|
|
|
@ -61,6 +61,8 @@ class WunderBar extends React.PureComponent {
|
|||
isActive: true
|
||||
}
|
||||
|
||||
this.props.activateSearch()
|
||||
|
||||
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
|
||||
|
@ -71,6 +73,7 @@ class WunderBar extends React.PureComponent {
|
|||
}
|
||||
|
||||
onBlur() {
|
||||
this.props.deactivateSearch()
|
||||
let commonState = {isActive: false};
|
||||
if (this._resetOnNextBlur) {
|
||||
this.setState(Object.assign({}, this._stateBeforeSearch, commonState));
|
||||
|
|
|
@ -1,165 +0,0 @@
|
|||
import React from 'react';
|
||||
import lbry from '../lbry.js';
|
||||
import lbryio from '../lbryio.js';
|
||||
import lbryuri from '../lbryuri.js';
|
||||
import lighthouse from '../lighthouse.js';
|
||||
import {FileTile, FileTileStream} from '../component/file-tile.js';
|
||||
import {Link} from '../component/link';
|
||||
import {ToolTip} from '../component/tooltip.js';
|
||||
import {BusyMessage} from '../component/common.js';
|
||||
|
||||
var SearchNoResults = React.createClass({
|
||||
render: function() {
|
||||
return <section>
|
||||
<span className="empty">
|
||||
No one has checked anything in for {this.props.query} yet.
|
||||
<Link label="Be the first" href="?publish" />
|
||||
</span>
|
||||
</section>;
|
||||
}
|
||||
});
|
||||
|
||||
var SearchResultList = React.createClass({
|
||||
render: function() {
|
||||
var rows = [],
|
||||
seenNames = {}; //fix this when the search API returns claim IDs
|
||||
|
||||
for (let {name, claim, claim_id, channel_name, channel_id, txid, nout} of this.props.results) {
|
||||
const uri = lbryuri.build({
|
||||
channelName: channel_name,
|
||||
contentName: name,
|
||||
claimId: channel_id || claim_id,
|
||||
});
|
||||
|
||||
rows.push(
|
||||
<FileTileStream key={uri} uri={uri} outpoint={txid + ':' + nout} metadata={claim.stream.metadata} contentType={claim.stream.source.contentType} />
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>{rows}</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let SearchResults = React.createClass({
|
||||
propTypes: {
|
||||
query: React.PropTypes.string.isRequired
|
||||
},
|
||||
|
||||
_isMounted: false,
|
||||
|
||||
search: function(term) {
|
||||
lighthouse.search(term).then(this.searchCallback);
|
||||
if (!this.state.searching) {
|
||||
this.setState({ searching: true })
|
||||
}
|
||||
},
|
||||
|
||||
componentWillMount: function () {
|
||||
this._isMounted = true;
|
||||
this.search(this.props.query);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function (nextProps) {
|
||||
if (nextProps.query != this.props.query) {
|
||||
this.search(nextProps.query);
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount: function () {
|
||||
this._isMounted = false;
|
||||
},
|
||||
|
||||
getInitialState: function () {
|
||||
return {
|
||||
results: [],
|
||||
searching: true
|
||||
};
|
||||
},
|
||||
|
||||
searchCallback: function (results) {
|
||||
if (this._isMounted) //could have canceled while results were pending, in which case nothing to do
|
||||
{
|
||||
this.setState({
|
||||
results: results,
|
||||
searching: false //multiple searches can be out, we're only done if we receive one we actually care about
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
render: function () {
|
||||
return this.state.searching ?
|
||||
<BusyMessage message="Looking up the Dewey Decimals" /> :
|
||||
(this.state.results && this.state.results.length ?
|
||||
<SearchResultList results={this.state.results} /> :
|
||||
<SearchNoResults query={this.props.query} />);
|
||||
}
|
||||
});
|
||||
|
||||
let SearchPage = React.createClass({
|
||||
|
||||
_isMounted: false,
|
||||
|
||||
propTypes: {
|
||||
query: React.PropTypes.string.isRequired
|
||||
},
|
||||
|
||||
isValidUri: function(query) {
|
||||
try {
|
||||
lbryuri.parse(query);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this._isMounted = true;
|
||||
lighthouse.search(this.props.query).then(this.searchCallback);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this._isMounted = false;
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
results: [],
|
||||
searching: true
|
||||
};
|
||||
},
|
||||
|
||||
searchCallback: function(results) {
|
||||
if (this._isMounted) //could have canceled while results were pending, in which case nothing to do
|
||||
{
|
||||
this.setState({
|
||||
results: results,
|
||||
searching: false //multiple searches can be out, we're only done if we receive one we actually care about
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
{ this.isValidUri(this.props.query) ?
|
||||
<section className="section-spaced">
|
||||
<h3 className="card-row__header">
|
||||
Exact URL
|
||||
<ToolTip label="?" body="This is the resolution of a LBRY URL and not controlled by LBRY Inc." className="tooltip--header"/>
|
||||
</h3>
|
||||
<FileTile uri={this.props.query} showEmpty={true} />
|
||||
</section> : '' }
|
||||
<section className="section-spaced">
|
||||
<h3 className="card-row__header">
|
||||
Search Results for {this.props.query}
|
||||
<ToolTip label="?" body="These search results are provided by LBRY Inc." className="tooltip--header"/>
|
||||
</h3>
|
||||
<SearchResults query={this.props.query} />
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default SearchPage;
|
30
ui/js/page/search/index.js
Normal file
30
ui/js/page/search/index.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
import React from 'react'
|
||||
import {
|
||||
connect,
|
||||
} from 'react-redux'
|
||||
import {
|
||||
doSearchContent,
|
||||
} from 'actions/search'
|
||||
import {
|
||||
selectIsSearching,
|
||||
selectSearchQuery,
|
||||
selectCurrentSearchResults,
|
||||
selectSearchActivated,
|
||||
} from 'selectors/search'
|
||||
import {
|
||||
doNavigate,
|
||||
} from 'actions/app'
|
||||
import SearchPage from './view'
|
||||
|
||||
const select = (state) => ({
|
||||
isSearching: selectIsSearching(state),
|
||||
query: selectSearchQuery(state),
|
||||
results: selectCurrentSearchResults(state),
|
||||
searchActive: selectSearchActivated(state),
|
||||
})
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
navigate: (path) => dispatch(doNavigate(path)),
|
||||
})
|
||||
|
||||
export default connect(select, perform)(SearchPage)
|
92
ui/js/page/search/view.jsx
Normal file
92
ui/js/page/search/view.jsx
Normal file
|
@ -0,0 +1,92 @@
|
|||
import React from 'react';
|
||||
import lbry from 'lbry';
|
||||
import lbryio from 'lbryio';
|
||||
import lbryuri from 'lbryuri';
|
||||
import lighthouse from 'lighthouse';
|
||||
import FileTile from 'component/fileTile'
|
||||
import FileTileStream from 'component/fileTileStream'
|
||||
import Link from 'component/link'
|
||||
import {ToolTip} from 'component/tooltip.js';
|
||||
import {BusyMessage} from 'component/common.js';
|
||||
|
||||
const SearchNoResults = (props) => {
|
||||
const {
|
||||
navigate,
|
||||
query,
|
||||
} = props
|
||||
|
||||
return <section>
|
||||
<span className="empty">
|
||||
No one has checked anything in for {query} yet.
|
||||
<Link label="Be the first" href="#" onClick={() => navigate('publish')} />
|
||||
</span>
|
||||
</section>;
|
||||
}
|
||||
|
||||
const SearchResultList = (props) => {
|
||||
const {
|
||||
results,
|
||||
} = props
|
||||
|
||||
const rows = [],
|
||||
seenNames = {}; //fix this when the search API returns claim IDs
|
||||
|
||||
for (let {name, claim, claim_id, channel_name, channel_id, txid, nout} of results) {
|
||||
const uri = lbryuri.build({
|
||||
channelName: channel_name,
|
||||
contentName: name,
|
||||
claimId: channel_id || claim_id,
|
||||
});
|
||||
|
||||
rows.push(
|
||||
<FileTileStream key={uri} uri={uri} />
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>{rows}</div>
|
||||
);
|
||||
}
|
||||
|
||||
const SearchResults = (props) => {
|
||||
const {
|
||||
searching,
|
||||
results,
|
||||
query,
|
||||
} = props
|
||||
|
||||
return (
|
||||
searching ?
|
||||
<BusyMessage message="Looking up the Dewey Decimals" /> :
|
||||
(results && results.length) ?
|
||||
<SearchResultList {...props} /> :
|
||||
<SearchNoResults {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
const SearchPage = (props) => {
|
||||
const isValidUri = (query) => true
|
||||
const {
|
||||
query,
|
||||
} = props
|
||||
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
{ isValidUri(query) ?
|
||||
<section className="section-spaced">
|
||||
<h3 className="card-row__header">
|
||||
Exact URL
|
||||
<ToolTip label="?" body="This is the resolution of a LBRY URL and not controlled by LBRY Inc." className="tooltip--header" />
|
||||
</h3>
|
||||
<FileTile uri={query} showEmpty={true} />
|
||||
</section> : '' }
|
||||
<section className="section-spaced">
|
||||
<h3 className="card-row__header">
|
||||
Search Results for {query}
|
||||
<ToolTip label="?" body="These search results are provided by LBRY, Inc." className="tooltip--header" />
|
||||
</h3>
|
||||
<SearchResults {...props} />
|
||||
</section>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
export default SearchPage;
|
|
@ -1,4 +1,8 @@
|
|||
import {createSelector} from 'reselect'
|
||||
import {
|
||||
selectIsSearching,
|
||||
selectSearchActivated,
|
||||
} from 'selectors/search'
|
||||
|
||||
export const _selectState = state => state.app || {}
|
||||
|
||||
|
@ -14,7 +18,12 @@ export const selectCurrentPath = createSelector(
|
|||
|
||||
export const selectCurrentPage = createSelector(
|
||||
selectCurrentPath,
|
||||
(path) => path.split('=')[0]
|
||||
selectSearchActivated,
|
||||
(path, searchActivated) => {
|
||||
if (searchActivated) return 'search'
|
||||
|
||||
return path.split('=')[0]
|
||||
}
|
||||
)
|
||||
|
||||
export const selectCurrentUri = createSelector(
|
||||
|
|
Loading…
Reference in a new issue