diff --git a/ui/js/actions/app.js b/ui/js/actions/app.js
index 8ede77fee..d7ac439a4 100644
--- a/ui/js/actions/app.js
+++ b/ui/js/actions/app.js
@@ -6,7 +6,12 @@ import {
selectUpgradeDownloadItem,
selectUpgradeFilename,
selectPageTitle,
+ selectCurrentPage,
+ selectCurrentParams,
} from 'selectors/app'
+import {
+ doSearch,
+} from 'actions/search'
const {remote, ipcRenderer, shell} = require('electron');
const path = require('path');
@@ -43,6 +48,16 @@ export function doChangePath(path) {
path,
}
})
+
+ const state = getState()
+ const pageTitle = selectPageTitle(state)
+ window.document.title = pageTitle
+
+ const currentPage = selectCurrentPage(state)
+ if (currentPage === 'search') {
+ const params = selectCurrentParams(state)
+ dispatch(doSearch(params.query))
+ }
}
}
@@ -59,7 +74,6 @@ export function doHistoryPush(params, title, relativeUrl) {
const url = pathParts.join('/')
title += " - LBRY"
history.pushState(params, title, url)
- window.document.title = title
}
}
@@ -210,3 +224,16 @@ export function doDaemonReady() {
type: types.DAEMON_READY
}
}
+
+export function doShowSnackBar(data) {
+ return {
+ type: types.SHOW_SNACKBAR,
+ data,
+ }
+}
+
+export function doRemoveSnackBarSnack() {
+ return {
+ type: types.REMOVE_SNACKBAR_SNACK,
+ }
+}
diff --git a/ui/js/actions/content.js b/ui/js/actions/content.js
index efb1851e2..0e7366dca 100644
--- a/ui/js/actions/content.js
+++ b/ui/js/actions/content.js
@@ -223,8 +223,11 @@ export function doPurchaseUri(uri) {
// the blobs. Or perhaps there's another way to see if a file was already
// purchased?
- // we already fully downloaded the file
- if (fileInfo && fileInfo.completed) {
+ // we already fully downloaded the file. If completed is true but
+ // writtenBytes is false then we downloaded it before but deleted it again,
+ // which means it needs to be reconstructed from the blobs by dispatching
+ // doLoadVideo.
+ if (fileInfo && fileInfo.completed && !!fileInfo.writtenBytes) {
return Promise.resolve()
}
diff --git a/ui/js/actions/search.js b/ui/js/actions/search.js
index 8dd85d822..149aa78ea 100644
--- a/ui/js/actions/search.js
+++ b/ui/js/actions/search.js
@@ -31,26 +31,24 @@ export function doSearch(query) {
if(page != 'search') {
dispatch(doNavigate('search', { query: query }))
} else {
- dispatch(doHistoryPush({ query }, "Search for " + query, '/search'))
- }
-
- 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,
+ 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(doResolveUri(uri))
- })
- dispatch({
- type: types.SEARCH_COMPLETED,
- data: {
- query,
- results,
- }
+ dispatch({
+ type: types.SEARCH_COMPLETED,
+ data: {
+ query,
+ results,
+ }
+ })
})
- })
+ }
}
}
diff --git a/ui/js/app.js b/ui/js/app.js
index e69c40218..b354036ba 100644
--- a/ui/js/app.js
+++ b/ui/js/app.js
@@ -1,6 +1,6 @@
import store from 'store.js';
-const env = process.env.NODE_ENV || 'production';
+const env = ENV;
const config = require(`./config/${env}`);
const logs = [];
const app = {
diff --git a/ui/js/component/fileListSearch/view.jsx b/ui/js/component/fileListSearch/view.jsx
index 4c9cfc625..986d8aa5c 100644
--- a/ui/js/component/fileListSearch/view.jsx
+++ b/ui/js/component/fileListSearch/view.jsx
@@ -58,11 +58,17 @@ class FileListSearch extends React.Component{
} = this.props
return (
- isSearching ?
- :
- (results && results.length) ?
+
+ {isSearching && !results &&
+ }
+
+ {isSearching && results &&
+ }
+
+ {(results && !!results.length) ?
:
-
+ }
+
)
}
}
diff --git a/ui/js/component/snack-bar.js b/ui/js/component/snack-bar.js
deleted file mode 100644
index 46dc3bd98..000000000
--- a/ui/js/component/snack-bar.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import React from 'react';
-
-export class SnackBar extends React.Component {
- constructor(props) {
- super(props);
-
- this._displayTime = 5; // in seconds
- this._hideTimeout = null;
-
- this.state = {
- snacks: []
- };
- }
-
- handleSnackReceived(event) {
- // if (this._hideTimeout) {
- // clearTimeout(this._hideTimeout);
- // }
-
- let snacks = this.state.snacks;
- snacks.push(event.detail);
- this.setState({ snacks: snacks});
- }
-
- componentWillMount() {
- document.addEventListener('globalNotice', this.handleSnackReceived);
- }
-
- componentWillUnmount() {
- document.removeEventListener('globalNotice', this.handleSnackReceived);
- }
-
- render() {
- if (!this.state.snacks.length) {
- this._hideTimeout = null; //should be unmounting anyway, but be safe?
- return null;
- }
-
- let snack = this.state.snacks[0];
-
- if (this._hideTimeout === null) {
- this._hideTimeout = setTimeout(() => {
- this._hideTimeout = null;
- let snacks = this.state.snacks;
- snacks.shift();
- this.setState({ snacks: snacks });
- }, this._displayTime * 1000);
- }
-
- return (
-
- {snack.message}
- {snack.linkText && snack.linkTarget ?
-
{snack.linkText} : ''}
-
- );
- }
-}
-
-export default SnackBar;
\ No newline at end of file
diff --git a/ui/js/component/snackBar/index.js b/ui/js/component/snackBar/index.js
new file mode 100644
index 000000000..0d624ce87
--- /dev/null
+++ b/ui/js/component/snackBar/index.js
@@ -0,0 +1,23 @@
+import React from 'react'
+import {
+ connect,
+} from 'react-redux'
+import {
+ doNavigate,
+ doRemoveSnackBarSnack,
+} from 'actions/app'
+import {
+ selectSnackBarSnacks,
+} from 'selectors/app'
+import SnackBar from './view'
+
+const perform = (dispatch) => ({
+ navigate: (path) => dispatch(doNavigate(path)),
+ removeSnack: () => dispatch(doRemoveSnackBarSnack()),
+})
+
+const select = (state) => ({
+ snacks: selectSnackBarSnacks(state),
+})
+
+export default connect(select, perform)(SnackBar)
diff --git a/ui/js/component/snackBar/view.jsx b/ui/js/component/snackBar/view.jsx
new file mode 100644
index 000000000..98206ae2b
--- /dev/null
+++ b/ui/js/component/snackBar/view.jsx
@@ -0,0 +1,49 @@
+import React from 'react';
+import Link from 'component/link'
+
+class SnackBar extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this._displayTime = 5; // in seconds
+ this._hideTimeout = null;
+ }
+
+ render() {
+ const {
+ navigate,
+ snacks,
+ removeSnack,
+ } = this.props
+
+ if (!snacks.length) {
+ this._hideTimeout = null; //should be unmounting anyway, but be safe?
+ return null;
+ }
+
+ const snack = snacks[0];
+ const {
+ message,
+ linkText,
+ linkTarget,
+ } = snack
+
+ if (this._hideTimeout === null) {
+ this._hideTimeout = setTimeout(() => {
+ this._hideTimeout = null;
+ removeSnack()
+ }, this._displayTime * 1000);
+ }
+
+ return (
+
+ {message}
+ {linkText && linkTarget &&
+ navigate(linkTarget)} label={linkText} />
+ }
+
+ );
+ }
+}
+
+export default SnackBar;
\ No newline at end of file
diff --git a/ui/js/component/wunderbar/index.js b/ui/js/component/wunderbar/index.js
index 9e597220b..8cde7704c 100644
--- a/ui/js/component/wunderbar/index.js
+++ b/ui/js/component/wunderbar/index.js
@@ -7,9 +7,6 @@ import {
selectWunderBarAddress,
selectWunderBarIcon
} from 'selectors/search'
-import {
- doSearch,
-} from 'actions/search'
import {
doNavigate,
} from 'actions/app'
@@ -21,7 +18,7 @@ const select = (state) => ({
})
const perform = (dispatch) => ({
- onSearch: (query) => dispatch(doSearch(query)),
+ onSearch: (query) => dispatch(doNavigate('/search', { query, })),
onSubmit: (query) => dispatch(doNavigate('/show', { uri: lbryuri.normalize(query) } ))
})
diff --git a/ui/js/constants/action_types.js b/ui/js/constants/action_types.js
index 3607583e3..1d1dab869 100644
--- a/ui/js/constants/action_types.js
+++ b/ui/js/constants/action_types.js
@@ -2,6 +2,8 @@ export const CHANGE_PATH = 'CHANGE_PATH'
export const OPEN_MODAL = 'OPEN_MODAL'
export const CLOSE_MODAL = 'CLOSE_MODAL'
export const HISTORY_BACK = 'HISTORY_BACK'
+export const SHOW_SNACKBAR = 'SHOW_SNACKBAR'
+export const REMOVE_SNACKBAR_SNACK = 'REMOVE_SNACKBAR_SNACK'
export const DAEMON_READY = 'DAEMON_READY'
diff --git a/ui/js/main.js b/ui/js/main.js
index 1fa350eff..9dd2a5347 100644
--- a/ui/js/main.js
+++ b/ui/js/main.js
@@ -5,9 +5,10 @@ import lbryio from './lbryio.js';
import lighthouse from './lighthouse.js';
import App from 'component/app/index.js';
import SplashScreen from 'component/splash.js';
-import SnackBar from 'component/snack-bar.js';
+import SnackBar from 'component/snackBar';
import {AuthOverlay} from 'component/auth.js';
import { Provider } from 'react-redux';
+import batchActions from 'util/batchActions'
import store from 'store.js';
import {
doChangePath,
@@ -21,7 +22,9 @@ import {
import {
doFileList
} from 'actions/file_info'
-import parseQueryParams from 'util/query_params'
+import {
+ toQueryString,
+} from 'util/query_params'
const {remote, ipcRenderer, shell} = require('electron');
const contextMenu = remote.require('./menu/context-menu');
@@ -36,15 +39,19 @@ window.addEventListener('contextmenu', (event) => {
});
window.addEventListener('popstate', (event, param) => {
- const queryString = document.location.search
+ const params = event.state
const pathParts = document.location.pathname.split('/')
const route = '/' + pathParts[pathParts.length - 1]
+ const queryString = toQueryString(params)
- if (route.match(/html$/)) return
+ let action
+ if (route.match(/html$/)) {
+ action = doChangePath('/discover')
+ } else {
+ action = doChangePath(`${route}?${queryString}`)
+ }
- console.log('title should be set here, but it is not in popstate? TODO')
-
- app.store.dispatch(doChangePath(`${route}${queryString}`))
+ app.store.dispatch(action)
})
ipcRenderer.on('open-uri-requested', (event, uri) => {
@@ -73,12 +80,16 @@ const initialState = app.store.getState();
var init = function() {
function onDaemonReady() {
- app.store.dispatch(doDaemonReady())
window.sessionStorage.setItem('loaded', 'y'); //once we've made it here once per session, we don't need to show splash again
- app.store.dispatch(doHistoryPush({}, "" +
- "Discover", "/discover"))
- app.store.dispatch(doFetchDaemonSettings())
- app.store.dispatch(doFileList())
+ const actions = []
+
+ actions.push(doDaemonReady())
+ actions.push(doChangePath('/discover'))
+ actions.push(doFetchDaemonSettings())
+ actions.push(doFileList())
+
+ app.store.dispatch(batchActions(actions))
+
ReactDOM.render({ lbryio.enabled ?
: '' }
, canvas)
}
diff --git a/ui/js/page/discover/view.jsx b/ui/js/page/discover/view.jsx
index 024f0d913..91cdfafda 100644
--- a/ui/js/page/discover/view.jsx
+++ b/ui/js/page/discover/view.jsx
@@ -34,24 +34,23 @@ class DiscoverPage extends React.Component{
fetchingFeaturedUris,
} = this.props
- let content
-
- if (fetchingFeaturedUris) {
- content =
- } else {
- if (typeof featuredUris === "object") {
- content = Object.keys(featuredUris).map(category => {
- return featuredUris[category].length ?
- :
- '';
- })
- } else if (featuredUris !== undefined) {
- content = Failed to load landing content.
- }
- }
-
return (
- {content}
+
+ {
+ fetchingFeaturedUris &&
+
+ }
+ {
+ typeof featuredUris === "object" &&
+ Object.keys(featuredUris).map(category => (
+ featuredUris[category].length ? : ''
+ ))
+ }
+ {
+ typeof featuredUris !== undefined &&
+ Failed to load landing content.
+ }
+
)
}
}
diff --git a/ui/js/reducers/app.js b/ui/js/reducers/app.js
index e3070ed40..f80baae0b 100644
--- a/ui/js/reducers/app.js
+++ b/ui/js/reducers/app.js
@@ -93,6 +93,44 @@ reducers[types.DAEMON_READY] = function(state, action) {
})
}
+reducers[types.SHOW_SNACKBAR] = function(state, action) {
+ const {
+ message,
+ linkText,
+ linkTarget,
+ isError,
+ } = action.data
+ const snackBar = Object.assign({}, state.snackBar)
+ const snacks = Object.assign([], snackBar.snacks)
+ snacks.push({
+ message,
+ linkText,
+ linkTarget,
+ isError,
+ })
+ const newSnackBar = Object.assign({}, snackBar, {
+ snacks,
+ })
+
+ return Object.assign({}, state, {
+ snackBar: newSnackBar,
+ })
+}
+
+reducers[types.REMOVE_SNACKBAR_SNACK] = function(state, action) {
+ const snackBar = Object.assign({}, state.snackBar)
+ const snacks = Object.assign([], snackBar.snacks)
+ snacks.shift()
+
+ const newSnackBar = Object.assign({}, snackBar, {
+ snacks,
+ })
+
+ return Object.assign({}, state, {
+ snackBar: newSnackBar,
+ })
+}
+
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
diff --git a/ui/js/rewards.js b/ui/js/rewards.js
index 9a2235130..eae27eb88 100644
--- a/ui/js/rewards.js
+++ b/ui/js/rewards.js
@@ -1,5 +1,8 @@
-import lbry from './lbry.js';
-import lbryio from './lbryio.js';
+import lbry from 'lbry';
+import lbryio from 'lbryio';
+import {
+ doShowSnackBar,
+} from 'actions/app'
function rewardMessage(type, amount) {
return {
@@ -40,14 +43,13 @@ rewards.claimReward = function (type) {
};
// Display global notice
- document.dispatchEvent(new CustomEvent('globalNotice', {
- detail: {
- message: message,
- linkText: "Show All",
- linkTarget: "/rewards",
- isError: false,
- },
- }));
+ const action = doShowSnackBar({
+ message,
+ linkText: "Show All",
+ linkTarget: "/rewards",
+ isError: false,
+ })
+ window.app.store.dispatch(action)
// Add more events here to display other places
diff --git a/ui/js/selectors/app.js b/ui/js/selectors/app.js
index f0af3b5a1..2563eb81b 100644
--- a/ui/js/selectors/app.js
+++ b/ui/js/selectors/app.js
@@ -1,5 +1,7 @@
import {createSelector} from 'reselect'
-import parseQueryParams from 'util/query_params'
+import {
+ parseQueryParams,
+} from 'util/query_params'
import lbryuri from 'lbryuri'
export const _selectState = state => state.app || {}
@@ -37,7 +39,7 @@ export const selectPageTitle = createSelector(
(page, params) => {
switch (page) {
case 'search':
- return 'Search'
+ return params.query ? `Search results for ${params.query}` : 'Search'
case 'settings':
return 'Settings'
case 'help':
@@ -191,4 +193,14 @@ export const selectDaemonReady = createSelector(
export const selectObscureNsfw = createSelector(
_selectState,
(state) => !!state.obscureNsfw
+)
+
+export const selectSnackBar = createSelector(
+ _selectState,
+ (state) => state.snackBar || {}
+)
+
+export const selectSnackBarSnacks = createSelector(
+ selectSnackBar,
+ (snackBar) => snackBar.snacks || []
)
\ No newline at end of file
diff --git a/ui/js/store.js b/ui/js/store.js
index 3c582ce5e..5ed542db5 100644
--- a/ui/js/store.js
+++ b/ui/js/store.js
@@ -1,6 +1,6 @@
const redux = require('redux');
const thunk = require("redux-thunk").default;
-const env = process.env.NODE_ENV || 'production';
+const env = ENV;
import {
createLogger
diff --git a/ui/js/util/query_params.js b/ui/js/util/query_params.js
index 45abc635c..9f89900d2 100644
--- a/ui/js/util/query_params.js
+++ b/ui/js/util/query_params.js
@@ -1,4 +1,4 @@
-function parseQueryParams(queryString) {
+export function parseQueryParams(queryString) {
if (queryString === '') return {};
const parts = queryString
.split('?')
@@ -13,4 +13,14 @@ function parseQueryParams(queryString) {
return params;
}
-export default parseQueryParams
+export function toQueryString(params) {
+ if (!params) return ""
+
+ const parts = []
+ for (const key in params) {
+ if (params.hasOwnProperty(key) && params[key]) {
+ parts.push(key + "=" + params[key])
+ }
+ }
+ return parts.join("&")
+}
diff --git a/ui/webpack.config.js b/ui/webpack.config.js
index 9afb1d82f..fe0a5277b 100644
--- a/ui/webpack.config.js
+++ b/ui/webpack.config.js
@@ -1,4 +1,5 @@
const path = require('path');
+const webpack = require('webpack')
const appPath = path.resolve(__dirname, 'js');
const PATHS = {
@@ -18,6 +19,11 @@ module.exports = {
root: appPath,
extensions: ['', '.js', '.jsx', '.css'],
},
+ plugins: [
+ new webpack.DefinePlugin({
+ ENV: JSON.stringify("development"),
+ }),
+ ],
module: {
preLoaders: [
{
diff --git a/ui/webpack.dev.config.js b/ui/webpack.dev.config.js
index cc5372370..15ec5652d 100644
--- a/ui/webpack.dev.config.js
+++ b/ui/webpack.dev.config.js
@@ -1,4 +1,5 @@
const path = require('path');
+const webpack = require('webpack')
const WebpackNotifierPlugin = require('webpack-notifier')
const appPath = path.resolve(__dirname, 'js');
@@ -25,6 +26,9 @@ module.exports = {
},
plugins: [
new WebpackNotifierPlugin(),
+ new webpack.DefinePlugin({
+ ENV: JSON.stringify("development"),
+ }),
],
module: {
preLoaders: [