Merge pull request #118 from 6ea86b96/redux

Redux
This commit is contained in:
Jeremy Kauffman 2017-05-07 09:02:20 -04:00 committed by GitHub
commit c0053363ff
28 changed files with 287 additions and 194 deletions

View file

@ -15,23 +15,43 @@ const app = require('electron').remote.app;
const {download} = remote.require('electron-dl');
const fs = remote.require('fs');
export function doNavigate(path) {
return function(dispatch, getState) {
const state = getState()
const previousPath = selectCurrentPath(state)
const previousTitle = selectPageTitle(state)
history.pushState(state, previousTitle, previousPath);
const queryStringFromParams = (params) => {
return Object
.keys(params)
.map(key => `${key}=${params[key]}`)
.join('&')
}
export function doNavigate(path, params = {}) {
return function(dispatch, getState) {
let url = path
if (params)
url = `${url}?${queryStringFromParams(params)}`
dispatch(doChangePath(url))
const state = getState()
const pageTitle = selectPageTitle(state)
history.pushState(params, pageTitle, url)
window.document.title = pageTitle
}
}
export function doChangePath(path) {
return function(dispatch, getState) {
dispatch({
type: types.NAVIGATE,
type: types.CHANGE_PATH,
data: {
path,
}
})
const pageTitle = selectPageTitle(state)
}
}
window.document.title = pageTitle
export function doHistoryBack() {
return function(dispatch, getState) {
history.back()
}
}
@ -53,16 +73,6 @@ export function doCloseModal() {
}
}
export function doHistoryBack() {
return function(dispatch, getState) {
if (window.history.length > 1) {
window.history.back();
} else {
dispatch(doNavigate('discover'))
}
}
}
export function doUpdateDownloadProgress(percent) {
return {
type: types.UPGRADE_DOWNLOAD_PROGRESSED,

View file

@ -3,9 +3,6 @@ import lbry from 'lbry'
import lbryio from 'lbryio'
import lbryuri from 'lbryuri'
import lighthouse from 'lighthouse'
import {
selectSearchQuery,
} from 'selectors/search'
import {
doResolveUri,
} from 'actions/content'
@ -14,6 +11,7 @@ import {
} from 'actions/app'
import {
selectCurrentPage,
selectSearchQuery,
} from 'selectors/app'
export function doSearchContent(query) {
@ -33,7 +31,9 @@ export function doSearchContent(query) {
data: { query }
})
if(page != 'discover' && query != undefined) dispatch(doNavigate('discover'))
if(page != 'search' && query != undefined) {
dispatch(doNavigate('search', { query: query }))
}
lighthouse.search(query).then(results => {
results.forEach(result => {
@ -75,3 +75,49 @@ export function doDeactivateSearch() {
type: types.DEACTIVATE_SEARCH,
}
}
export function doSetSearchQuery(query) {
return function(dispatch, getState) {
const state = getState()
dispatch(doNavigate('/search', { query }))
}
}
export function doSearch() {
return function(dispatch, getState) {
const state = getState()
const page = selectCurrentPage(state)
const query = selectSearchQuery(state)
if (!query) {
return dispatch({
type: types.SEARCH_CANCELLED,
})
}
dispatch({
type: types.SEARCH_STARTED,
data: { query }
})
lighthouse.search(query).then(results => {
results.forEach(result => {
const uri = lbryuri.build({
channelName: result.channel_name,
contentName: result.name,
claimId: result.channel_id || result.claim_id,
})
dispatch(doResolveUri(uri))
})
dispatch({
type: types.SEARCH_COMPLETED,
data: {
query,
results,
}
})
})
}
}

View file

@ -41,7 +41,6 @@ export function doGetNewAddress() {
type: types.GET_NEW_ADDRESS_STARTED
})
console.log('do get new address');
lbry.wallet_new_address().then(function(address) {
localStorage.setItem('wallet_address', address);
dispatch({

View file

@ -44,7 +44,7 @@ const makeSelect = () => {
}
const perform = (dispatch) => ({
navigate: (path) => dispatch(doNavigate(path)),
navigate: (path, params) => dispatch(doNavigate(path, params)),
})
export default connect(makeSelect, perform)(FileCardStream)

View file

@ -67,7 +67,7 @@ class FileCardStream extends React.Component {
const isConfirmed = !!metadata;
const title = isConfirmed ? metadata.title : uri;
const obscureNsfw = this.props.obscureNsfw && isConfirmed && metadata.nsfw;
const primaryUrl = 'show=' + uri;
const primaryUrl = '/show?uri=' + uri;
let description = ""
if (isConfirmed) {
description = metadata.description
@ -80,7 +80,7 @@ class FileCardStream extends React.Component {
return (
<section className={ 'card card--small card--link ' + (obscureNsfw ? 'card--obscured ' : '') } onMouseEnter={this.handleMouseOver.bind(this)} onMouseLeave={this.handleMouseOut.bind(this)}>
<div className="card__inner">
<a href="#" onClick={() => navigate(primaryUrl)} className="card__link">
<Link onClick={() => navigate('/show', { uri })} className="card__link">
<div className="card__title-identity">
<h5 title={title}><TruncatedText lines={1}>{title}</TruncatedText></h5>
<div className="card__subtitle">
@ -94,12 +94,12 @@ class FileCardStream extends React.Component {
<div className="card__content card__subtext card__subtext--two-lines">
<TruncatedText lines={2}>{description}</TruncatedText>
</div>
</a>
</Link>
{this.state.showNsfwHelp && this.state.hovered
? <div className='card-overlay'>
<p>
This content is Not Safe For Work.
To view adult content, please change your <Link className="button-text" href="?settings" label="Settings" />.
To view adult content, please change your <Link className="button-text" onClick={() => navigate('settings')} label="Settings" />.
</p>
</div>
: null}

View file

@ -49,7 +49,7 @@ const makeSelect = () => {
}
const perform = (dispatch) => ({
navigate: (path) => dispatch(doNavigate(path))
navigate: (path, params) => dispatch(doNavigate(path, params))
})
export default connect(makeSelect, perform)(FileTileStream)

View file

@ -83,49 +83,81 @@ class FileTileStream extends React.Component {
}
return (
<section className={ 'file-tile card ' + (obscureNsfw ? 'card--obscured ' : '') } onMouseEnter={this.handleMouseOver.bind(this)} onMouseLeave={this.handleMouseOut.bind(this)}>
<div className={"row-fluid card__inner file-tile__row"}>
<div className="span3 file-tile__thumbnail-container">
<a href="#" onClick={() => navigate(`show=${uri}`)}>
{metadata && metadata.thumbnail ?
<Thumbnail key={this.props.uri} className="file-tile__thumbnail" src={metadata.thumbnail} alt={'Photo for ' + this.props.uri} />
:
<Thumbnail className="file-tile__thumbnail" alt={'Photo for ' + this.props.uri} />
}
</a>
</div>
<div className="span9">
<div className="card__title-primary">
{ !this.props.hidePrice
? <FilePrice uri={this.props.uri} />
: null}
<div className="meta"><a href="#" onClick={() => navigate(`show=${uri}`)}>{uri}</a></div>
<h3>
<a href="#" onClick={() => navigate(`show=${uri}`)} title={title}>
<TruncatedText lines={1}>
{title}
</TruncatedText>
</a>
</h3>
<section className={ 'card card--small card--link ' + (obscureNsfw ? 'card--obscured ' : '') } onMouseEnter={this.handleMouseOver.bind(this)} onMouseLeave={this.handleMouseOut.bind(this)}>
<div className="card__inner">
<Link onClick={() => navigate('/show', { uri })} className="card__link">
<div className="card__title-identity">
<h5 title={title}><TruncatedText lines={1}>{title}</TruncatedText></h5>
<div className="card__subtitle">
{ !this.props.hidePrice ? <span style={{float: "right"}}><FilePrice uri={uri} /></span> : null}
<UriIndicator uri={uri} />
</div>
</div>
<div className="card__actions">
{ metadata && metadata.thumbnail ?
<div className="card__media" style={{ backgroundImage: "url('" + metadata.thumbnail + "')" }}></div> :
<div className="card__media" style={{ backgroundImage: "url('" + lbry.imagePath('default-thumb.svg') + "')" }}></div>
}
<div className="card__content card__subtext card__subtext--two-lines">
<TruncatedText lines={2}>
{isConfirmed
? metadata.description
: <span className="empty">This file is pending confirmation.</span>}
</TruncatedText>
</div>
<div className="card__content">
<p className="file-tile__description">
<TruncatedText lines={2}>{description}</TruncatedText>
</p>
</div>
</div>
</Link>
{this.state.showNsfwHelp && this.state.hovered
? <div className='card-overlay'>
<p>
This content is Not Safe For Work.
To view adult content, please change your <Link className="button-text" onClick={() => navigate('/settings')} label="Settings" />.
</p>
</div>
: null}
</div>
{this.state.showNsfwHelp
? <div className='card-overlay'>
<p>
This content is Not Safe For Work.
To view adult content, please change your <Link className="button-text" href="#" onClick={() => navigate('settings')} label="Settings" />.
</p>
</div>
: null}
</section>
// <section className={ 'file-tile card ' + (obscureNsfw ? 'card--obscured ' : '') } onMouseEnter={this.handleMouseOver.bind(this)} onMouseLeave={this.handleMouseOut.bind(this)}>
// <div className={"row-fluid card__inner file-tile__row"}>
// <div className="span3 file-tile__thumbnail-container">
// <Link onClick={() => navigate('/show', { uri })}>
// {metadata && metadata.thumbnail ?
// <Thumbnail key={this.props.uri} className="file-tile__thumbnail" src={metadata.thumbnail} alt={'Photo for ' + this.props.uri} />
// :
// <Thumbnail className="file-tile__thumbnail" alt={'Photo for ' + this.props.uri} />
// }
// </Link>
// </div>
// <div className="span9">
// <div className="card__title-primary">
// { !this.props.hidePrice
// ? <FilePrice uri={this.props.uri} />
// : null}
// <div className="meta"><Link onClick={() => navigate('/show', { uri })}>{uri}</Link></div>
// <h3>
// <Link onClick={() => navigate('/show', { uri })} title={title}>
// <TruncatedText lines={1}>
// {title}
// </TruncatedText>
// </Link>
// </h3>
// </div>
// <div className="card__actions">
// </div>
// <div className="card__content">
// <p className="file-tile__description">
// <TruncatedText lines={2}>{description}</TruncatedText>
// </p>
// </div>
// </div>
// </div>
// {this.state.showNsfwHelp
// ? <div className='card-overlay'>
// <p>
// This content is Not Safe For Work.
// To view adult content, please change your <Link className="button-text" onClick={() => navigate('/settings')} label="Settings" />.
// </p>
// </div>
// : null}
// </section>
);
}
}

View file

@ -14,22 +14,22 @@ export const Header = (props) => {
<Link onClick={back} button="alt button--flat" icon="icon-arrow-left" />
</div>
<div className="header__item">
<Link onClick={() => navigate('discover')} button="alt button--flat" icon="icon-home" />
<Link onClick={() => navigate('/discover')} button="alt button--flat" icon="icon-home" />
</div>
<div className="header__item header__item--wunderbar">
<WunderBar/>
</div>
<div className="header__item">
<Link onClick={() => navigate('wallet')} button="text" icon="icon-bank" label={balance} ></Link>
<Link onClick={() => navigate('/wallet')} button="text" icon="icon-bank" label={balance} ></Link>
</div>
<div className="header__item">
<Link onClick={() => navigate('publish')} button="primary button--flat" icon="icon-upload" label="Publish" />
<Link onClick={() => navigate('/publish')} button="primary button--flat" icon="icon-upload" label="Publish" />
</div>
<div className="header__item">
<Link onClick={() => navigate('downloaded')} button="alt button--flat" icon="icon-folder" />
<Link onClick={() => navigate('/downloaded')} button="alt button--flat" icon="icon-folder" />
</div>
<div className="header__item">
<Link onClick={() => navigate('settings')} button="alt button--flat" icon="icon-gear" />
<Link onClick={() => navigate('/settings')} button="alt button--flat" icon="icon-gear" />
</div>
</header>
}

View file

@ -1,4 +1,5 @@
import React from 'react'
import Link from 'component/link'
const SubHeader = (props) => {
const {
@ -12,9 +13,9 @@ const SubHeader = (props) => {
for(let link of Object.keys(subLinks)) {
links.push(
<a href="#" onClick={() => navigate(link)} key={link} className={link == currentPage ? 'sub-header-selected' : 'sub-header-unselected' }>
<Link onClick={(event) => navigate(`/${link}`, event)} key={link} className={link == currentPage ? 'sub-header-selected' : 'sub-header-unselected' }>
{subLinks[link]}
</a>
</Link>
)
}

View file

@ -23,10 +23,12 @@ const select = (state) => ({
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),
// onSearch: (query) => dispatch(doSearchContent(query)),
// onSubmit: (query) => dispatch(doSearchContent(query)),
// activateSearch: () => dispatch(doActivateSearch()),
// deactivateSearch: () => setTimeout(() => { dispatch(doDeactivateSearch()) }, 50),
onSearch: (query) => dispatch(doNavigate('/search', { query })),
onSubmit: (query) => console.debug('you submitted'),
})
export default connect(select, perform)(Wunderbar)

View file

@ -61,8 +61,6 @@ 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
@ -73,7 +71,6 @@ class WunderBar extends React.PureComponent {
}
onBlur() {
this.props.deactivateSearch()
let commonState = {isActive: false};
if (this._resetOnNextBlur) {
this.setState(Object.assign({}, this._stateBeforeSearch, commonState));
@ -123,7 +120,7 @@ class WunderBar extends React.PureComponent {
return (
<div className={'wunderbar' + (this.state.isActive ? ' wunderbar--active' : '')}>
{this.state.icon ? <Icon fixed icon={this.state.icon} /> : '' }
<input className="wunderbar__input" type="search" placeholder="Type a LBRY address or search term"
<input className="wunderbar__input" type="search"
ref={this.onReceiveRef}
onFocus={this.onFocus}
onBlur={this.onBlur}

View file

@ -1,4 +1,4 @@
export const NAVIGATE = 'NAVIGATE'
export const CHANGE_PATH = 'CHANGE_PATH'
export const OPEN_MODAL = 'OPEN_MODAL'
export const CLOSE_MODAL = 'CLOSE_MODAL'
export const HISTORY_BACK = 'HISTORY_BACK'

View file

@ -11,8 +11,10 @@ import { Provider } from 'react-redux';
import store from 'store.js';
import { runTriggers } from 'triggers'
import {
doDaemonReady
doDaemonReady,
doChangePath,
} from 'actions/app'
import parseQueryParams from 'util/query_params'
const {remote} = require('electron');
const contextMenu = remote.require('./menu/context-menu');
@ -26,6 +28,14 @@ window.addEventListener('contextmenu', (event) => {
event.preventDefault();
});
window.addEventListener('popstate', (event) => {
const pathname = document.location.pathname
const queryString = document.location.search
if (pathname.match(/dist/)) return
app.store.dispatch(doChangePath(`${pathname}${queryString}`))
})
const initialState = app.store.getState();
app.store.subscribe(runTriggers);
runTriggers();
@ -41,6 +51,7 @@ var init = function() {
function onDaemonReady() {
app.store.dispatch(doDaemonReady())
window.history.pushState({}, "Discover", '/discover');
ReactDOM.render(<Provider store={store}><div>{ lbryio.enabled ? <AuthOverlay/> : '' }<App /><SnackBar /></div></Provider>, canvas)
}

View file

@ -3,21 +3,14 @@ import {
connect
} from 'react-redux'
import {
selectFeaturedUris
selectFeaturedUris,
selectFetchingFeaturedUris,
} from 'selectors/content'
import {
doSearchContent,
} from 'actions/search'
import {
selectIsSearching,
selectSearchQuery,
selectCurrentSearchResults,
selectSearchActivated,
} from 'selectors/search'
import DiscoverPage from './view'
const select = (state) => ({
featuredUris: selectFeaturedUris(state),
fetchingFeaturedUris: selectFetchingFeaturedUris(state),
})
const perform = (dispatch) => ({

View file

@ -1,5 +1,6 @@
import React from 'react';
import lbryio from 'lbryio.js';
import lbryuri from 'lbryuri'
import FileTile from 'component/fileTile';
import { FileTileStream } from 'component/fileTileStream'
import {ToolTip} from 'component/tooltip.js';
@ -24,80 +25,26 @@ const FeaturedCategory = (props) => {
const DiscoverPage = (props) => {
const {
featuredUris
featuredUris,
fetchingFeaturedUris,
} = props
const failed = Object.keys(featuredUris).length === 0
return <main>{
Object.keys(featuredUris).length === 0 ?
<div className="empty">Failed to load landing content.</div> :
<div>
{
Object.keys(featuredUris).map((category) => {
return featuredUris[category].length ?
<FeaturedCategory key={category} category={category} names={featuredUris[category]} /> :
'';
})
}
</div>
}</main>
let content
if (fetchingFeaturedUris) content = <div className="empty">Fetching landing content.</div>
if (!fetchingFeaturedUris && failed) content = <div className="empty">Failed to load landing content.</div>
if (!fetchingFeaturedUris && !failed) {
content = Object.keys(featuredUris).map(category => {
return featuredUris[category].length ?
<FeaturedCategory key={category} category={category} names={featuredUris[category]} /> :
'';
})
}
return (
<main>{content}</main>
)
}
//
// let DiscoverPage = React.createClass({
// getInitialState: function() {
// return {
// featuredUris: {},
// failed: false
// };
// },
// componentWillMount: function() {
// lbryio.call('discover', 'list', { version: "early-access" } ).then(({Categories, Uris}) => {
// let featuredUris = {}
// Categories.forEach((category) => {
// if (Uris[category] && Uris[category].length) {
// featuredUris[category] = Uris[category]
// }
// })
// this.setState({ featuredUris: featuredUris });
// }, () => {
// this.setState({
// failed: true
// })
// });
// },
// render: function() {
// return <main>{
// this.state.failed ?
// <div className="empty">Failed to load landing content.</div> :
// <div>
// {
// Object.keys(this.state.featuredUris).map((category) => {
// return this.state.featuredUris[category].length ?
// <FeaturedCategory key={category} category={category} names={this.state.featuredUris[category]} /> :
// '';
// })
// }
// </div>
// }</main>;
// }
// })
// const DiscoverPage = (props) => {
// const {
// isSearching,
// query,
// results,
// searchActive,
// } = props
//
// return (
// <main>
// { (!searchActive || (!isSearching && !query)) && <FeaturedContent {...props} /> }
// { searchActive && isSearching ? <SearchActive /> : null }
// { searchActive && !isSearching && query && results.length ? <SearchResults results={results} /> : null }
// { searchActive && !isSearching && query && !results.length ? <SearchNoResults query={query} /> : null }
// </main>
// );
// }
export default DiscoverPage;

View file

@ -22,7 +22,7 @@ class FileListDownloaded extends React.Component {
if (fetching) {
content = <BusyMessage message="Loading" />
} else if (!downloadedContent.length) {
content = <span>You haven't downloaded anything from LBRY yet. Go <Link href="#" onClick={() => navigate('discover')} label="search for your first download" />!</span>
content = <span>You haven't downloaded anything from LBRY yet. Go <Link onClick={() => navigate('/discover')} label="search for your first download" />!</span>
} else {
content = <FileList fileInfos={downloadedContent} hidePrices={true} />
}

View file

@ -43,7 +43,7 @@ class FileListPublished extends React.Component {
if (fetching) {
content = <BusyMessage message="Loading" />
} else if (!publishedContent.length) {
content = <span>You haven't downloaded anything from LBRY yet. Go <Link href="#" onClick={() => navigate('discover')} label="search for your first download" />!</span>
content = <span>You haven't downloaded anything from LBRY yet. Go <Link onClick={() => navigate('/discover')} label="search for your first download" />!</span>
} else {
content = <FileList fileInfos={publishedContent} hidePrices={true} />
}

View file

@ -77,7 +77,7 @@ var HelpPage = React.createClass({
<div className="card__title-primary"><h3>Report a Bug</h3></div>
<div className="card__content">
<p>Did you find something wrong?</p>
<p><Link href="?report" label="Submit a Bug Report" icon="icon-bug" button="alt" /></p>
<p><Link onClick={() => navigate('report')} label="Submit a Bug Report" icon="icon-bug" button="alt" /></p>
<div className="meta">Thanks! LBRY is made by its users.</div>
</div>
</section>

View file

@ -147,7 +147,7 @@ var PublishPage = React.createClass({
});
},
handlePublishStartedConfirmed: function() {
this.props.navigate('published')
this.props.navigate('/published')
},
handlePublishError: function(error) {
this.setState({

View file

@ -7,10 +7,12 @@ import {
} from 'actions/search'
import {
selectIsSearching,
selectSearchQuery,
selectCurrentSearchResults,
selectSearchActivated,
} from 'selectors/search'
import {
selectSearchQuery,
} from 'selectors/app'
import {
doNavigate,
} from 'actions/app'

View file

@ -18,7 +18,7 @@ const SearchNoResults = (props) => {
return <section>
<span className="empty">
No one has checked anything in for {query} yet.
<Link label="Be the first" href="#" onClick={() => navigate('publish')} />
<Link label="Be the first" onClick={() => navigate('/publish')} />
</span>
</section>;
}

View file

@ -53,7 +53,7 @@ const ShowPage = (props) => {
</div>
<div className="card__content">
{ uriLookupComplete ?
<p>This location is not yet in use. { ' ' }<Link href="#" onClick={() => navigate('publish')} label="Put something here" />.</p> :
<p>This location is not yet in use. { ' ' }<Link onClick={() => navigate('/publish')} label="Put something here" />.</p> :
<BusyMessage message="Loading magic decentralized data..." />
}
</div>

View file

@ -19,7 +19,7 @@ reducers[types.DAEMON_READY] = function(state, action) {
})
}
reducers[types.NAVIGATE] = function(state, action) {
reducers[types.CHANGE_PATH] = function(state, action) {
return Object.assign({}, state, {
currentPath: action.data.path,
})

View file

@ -1,7 +1,7 @@
import * as types from 'constants/action_types'
const reducers = {}
const address = sessionStorage.getItem('receiveAddress')
const address = localStorage.getItem('receiveAddress')
const buildDraftTransaction = () => ({
amount: undefined,
address: undefined
@ -50,7 +50,7 @@ reducers[types.GET_NEW_ADDRESS_STARTED] = function(state, action) {
reducers[types.GET_NEW_ADDRESS_COMPLETED] = function(state, action) {
const { address } = action.data
sessionStorage.setItem('receiveAddress', address)
localStorage.setItem('receiveAddress', address)
return Object.assign({}, state, {
gettingNewAddress: false,
receiveAddress: address

View file

@ -1,8 +1,6 @@
import {createSelector} from 'reselect'
import {
selectIsSearching,
selectSearchActivated,
} from 'selectors/search'
import parseQueryParams from 'util/query_params'
import lbryuri from 'lbryuri'
export const _selectState = state => state.app || {}
@ -18,14 +16,26 @@ export const selectCurrentPath = createSelector(
export const selectCurrentPage = createSelector(
selectCurrentPath,
selectSearchActivated,
(path, searchActivated) => {
if (searchActivated) return 'search'
return path.split('=')[0]
(path) => {
return path.replace(/^\//, '').split('?')[0]
}
)
export const selectCurrentParams = createSelector(
selectCurrentPath,
(path) => {
if (path === undefined) return {}
if (!path.match(/\?/)) return {}
return parseQueryParams(path.split('?')[1])
}
)
export const selectSearchQuery = createSelector(
selectCurrentParams,
(params) => params.query
)
export const selectCurrentUri = createSelector(
selectCurrentPath,
(path) => {
@ -62,7 +72,7 @@ export const selectPageTitle = createSelector(
case 'rewards':
return page.charAt(0).toUpperCase() + page.slice(1)
case 'show':
return lbryuri.normalize(page)
return lbryuri.normalize(uri)
case 'downloaded':
return 'Downloads & Purchases'
case 'published':
@ -85,7 +95,8 @@ export const selectPageTitle = createSelector(
export const selectWunderBarAddress = createSelector(
selectPageTitle,
(title) => title
selectSearchQuery,
(title, query) => query || title
)
export const selectWunderBarIcon = createSelector(

View file

@ -1,12 +1,12 @@
import { createSelector } from 'reselect'
import {
selectCurrentParams,
selectDaemonReady,
selectSearchQuery,
} from 'selectors/app'
export const _selectState = state => state.search || {}
export const selectSearchQuery = createSelector(
_selectState,
(state) => state.query
)
export const selectIsSearching = createSelector(
_selectState,
(state) => !!state.searching
@ -32,3 +32,18 @@ export const selectSearchActivated = createSelector(
_selectState,
(state) => !!state.activated
)
export const shouldSearch = createSelector(
selectDaemonReady,
selectSearchQuery,
selectIsSearching,
selectSearchResultsByQuery,
(daemonReady, query, isSearching, resultsByQuery) => {
if (!daemonReady) return false
if (!query) return false
if (isSearching) return false
if (Object.keys(resultsByQuery).indexOf(query) != -1) return false
return true
}
)

View file

@ -34,6 +34,12 @@ import {
import {
doFetchCurrentUriAvailability,
} from 'actions/availability'
import {
shouldSearch,
} from 'selectors/search'
import {
doSearch,
} from 'actions/search'
const triggers = []
@ -77,6 +83,11 @@ triggers.push({
action: doFetchCurrentUriAvailability,
})
triggers.push({
selector: shouldSearch,
action: doSearch,
})
const runTriggers = function() {
triggers.forEach(function(trigger) {
const state = app.store.getState();

View file

@ -0,0 +1,16 @@
function parseQueryParams(queryString) {
if (queryString === '') return {};
const parts = queryString
.split('?')
.pop()
.split('&')
.map(function(p) { return p.split('=') })
const params = {};
parts.forEach(function(arr) {
params[arr[0]] = arr[1];
})
return params;
}
export default parseQueryParams