Fix bugs in history navigation in stack ordering and loss of scrolling

and refactor history
This commit is contained in:
Jeremy Kauffman 2017-08-30 08:48:32 -04:00
parent 7e23b62c7b
commit 339945018d
44 changed files with 399 additions and 450 deletions

View file

@ -5,18 +5,10 @@ import {
selectUpgradeDownloadPath,
selectUpgradeDownloadItem,
selectUpgradeFilename,
selectPageTitle,
selectCurrentPage,
selectCurrentParams,
selectHistoryBack,
selectHistoryForward,
} from "selectors/app";
import { doSearch } from "actions/search";
import { doFetchDaemonSettings } from "actions/settings";
import { doAuthenticate } from "actions/user";
import { doFileList } from "actions/file_info";
import { toQueryString } from "util/query_params";
import { parseQueryParams } from "util/query_params";
const { remote, ipcRenderer, shell } = require("electron");
const path = require("path");
@ -24,130 +16,6 @@ const { download } = remote.require("electron-dl");
const fs = remote.require("fs");
const { lbrySettings: config } = require("../../../app/package.json");
export function doNavigate(path, params = {}, options = {}) {
return function(dispatch, getState) {
if (!path) {
return;
}
let url = path;
if (params) url = `${url}?${toQueryString(params)}`;
dispatch(doChangePath(url));
const state = getState();
const pageTitle = selectPageTitle(state);
const historyState = history.state;
dispatch(
doHistoryPush({ params, page: historyState.page + 1 }, pageTitle, url)
);
};
}
export function doAuthNavigate(pathAfterAuth = null, params = {}) {
return function(dispatch, getState) {
if (pathAfterAuth) {
dispatch({
type: types.CHANGE_AFTER_AUTH_PATH,
data: {
path: `${pathAfterAuth}?${toQueryString(params)}`,
},
});
}
dispatch(doNavigate("/auth"));
};
}
export function doChangePath(path, options = {}) {
return function(dispatch, getState) {
dispatch({
type: types.CHANGE_PATH,
data: {
path,
},
});
const state = getState();
const pageTitle = selectPageTitle(state);
const scrollY = options.scrollY;
window.document.title = pageTitle;
if (scrollY) window.scrollTo(0, scrollY);
else window.scrollTo(0, 0);
const currentPage = selectCurrentPage(state);
if (currentPage === "search") {
const params = selectCurrentParams(state);
dispatch(doSearch(params.query));
}
};
}
export function doHistoryBack() {
return function(dispatch, getState) {
// Get back history from stack
const back = selectHistoryBack(getState());
if (back) {
// Set location
dispatch(doChangePath(back.location));
dispatch({
type: types.HISTORY_NAVIGATE,
data: { page: back },
});
}
};
}
export function doHistoryForward() {
return function(dispatch, getState) {
// Get forward history from stack
const forward = selectHistoryForward(getState());
if (forward) {
// Set location
dispatch(doChangePath(forward.location));
dispatch({
type: types.HISTORY_NAVIGATE,
data: { page: forward },
});
}
};
}
export function doHistoryPush(currentState, title, relativeUrl) {
return function(dispatch, getState) {
title += " - LBRY";
history.pushState(currentState, title, `#${relativeUrl}`);
dispatch({
type: types.HISTORY_NAVIGATE,
data: {
location: relativeUrl,
},
});
};
}
export function doRecordScroll(scroll) {
return function(dispatch, getState) {
const state = getState();
const historyState = history.state;
if (!historyState) return;
historyState.scrollY = scroll;
history.replaceState(
historyState,
document.title,
`#${state.app.currentPath}`
);
};
}
export function doOpenModal(modal) {
return {
type: types.OPEN_MODAL,
@ -305,25 +173,8 @@ export function doAlertError(errorList) {
export function doDaemonReady() {
return function(dispatch, getState) {
const path = window.location.hash || "#/discover";
const params = parseQueryParams(path.split("?")[1] || "");
// Get first page
const page = {
index: 0,
location: path.replace(/^#/, ""),
};
history.replaceState(
{ params, is_first_page: true, page: 1 },
document.title,
`${path}`
);
dispatch(doAuthenticate());
dispatch({
type: types.DAEMON_READY,
data: { page },
});
dispatch({ type: types.DAEMON_READY });
dispatch(doFetchDaemonSettings());
dispatch(doFileList());
};

View file

@ -12,7 +12,8 @@ import {
selectUrisLoading,
selectTotalDownloadProgress,
} from "selectors/file_info";
import { doCloseModal, doHistoryBack } from "actions/app";
import { doCloseModal } from "actions/app";
import { doHistoryBack } from "actions/navigation";
import setProgressBar from "util/setProgressBar";
import batchActions from "util/batchActions";

104
ui/js/actions/navigation.js Normal file
View file

@ -0,0 +1,104 @@
import * as types from "constants/action_types";
import {
selectPageTitle,
selectCurrentPage,
selectCurrentParams,
selectHistoryStack,
selectHistoryIndex,
} from "selectors/navigation";
import { doSearch } from "actions/search";
import { toQueryString } from "util/query_params";
export function doNavigate(path, params = {}, options = {}) {
return function(dispatch, getState) {
if (!path) {
return;
}
let url = path;
if (params && Object.values(params).length) {
url += "?" + toQueryString(params);
}
dispatch(doChangePath(url, options));
const pageTitle = selectPageTitle(getState()) + " - LBRY";
dispatch({
type: types.HISTORY_NAVIGATE,
data: { url, index: options.index },
});
};
}
export function doAuthNavigate(pathAfterAuth = null, params = {}) {
return function(dispatch, getState) {
if (pathAfterAuth) {
dispatch({
type: types.CHANGE_AFTER_AUTH_PATH,
data: {
path: `${pathAfterAuth}?${toQueryString(params)}`,
},
});
}
dispatch(doNavigate("/auth"));
};
}
export function doChangePath(path, options = {}) {
return function(dispatch, getState) {
dispatch({
type: types.CHANGE_PATH,
data: {
path,
},
});
const state = getState();
const scrollY = options.scrollY;
//I wasn't seeing it scroll to the proper position without this -- possibly because the page isn't fully rendered? Not sure - Jeremy
setTimeout(() => {
window.scrollTo(0, scrollY ? scrollY : 0);
}, 100);
const currentPage = selectCurrentPage(state);
if (currentPage === "search") {
const params = selectCurrentParams(state);
dispatch(doSearch(params.query));
}
};
}
export function doHistoryTraverse(dispatch, state, modifier) {
const stack = selectHistoryStack(state),
index = selectHistoryIndex(state) + modifier;
if (index >= 0 && index < stack.length) {
const historyItem = stack[index];
return dispatch(
doNavigate(historyItem.path, {}, { scrollY: historyItem.scrollY, index })
);
}
}
export function doHistoryBack() {
return function(dispatch, getState) {
return doHistoryTraverse(dispatch, getState(), -1);
};
}
export function doHistoryForward() {
return function(dispatch, getState) {
return doHistoryTraverse(dispatch, getState(), 1);
};
}
export function doRecordScroll(scroll) {
return function(dispatch, getState) {
dispatch({
type: types.WINDOW_SCROLLED,
data: { scrollY: scroll },
});
};
}

View file

@ -2,8 +2,8 @@ import * as types from "constants/action_types";
import lbryuri from "lbryuri";
import lighthouse from "lighthouse";
import { doResolveUri } from "actions/content";
import { doNavigate, doHistoryPush } from "actions/app";
import { selectCurrentPage } from "selectors/app";
import { doNavigate } from "actions/navigation";
import { selectCurrentPage } from "selectors/navigation";
import batchActions from "util/batchActions";
export function doSearch(query) {

View file

@ -1,16 +1,18 @@
import React from "react";
import { connect } from "react-redux";
import { selectPageTitle } from "selectors/navigation";
import { selectUser } from "selectors/user";
import {
doCheckUpgradeAvailable,
doAlertError,
doRecordScroll,
} from "actions/app";
import { doRecordScroll } from "actions/navigation";
import { doFetchRewardedContent } from "actions/content";
import { doUpdateBalance } from "actions/wallet";
import { selectUser } from "selectors/user";
import App from "./view";
const select = (state, props) => ({
pageTitle: selectPageTitle(state),
user: selectUser(state),
});

View file

@ -30,12 +30,22 @@ class App extends React.PureComponent {
this.scrollListener = () => this.props.recordScroll(window.scrollY);
window.addEventListener("scroll", this.scrollListener);
this.setTitleFromProps(this.props);
}
componentWillUnmount() {
window.removeEventListener("scroll", this.scrollListener);
}
componentWillReceiveProps(props) {
this.setTitleFromProps(props);
}
setTitleFromProps(props) {
window.document.title = props.pageTitle;
}
render() {
return (
<div id="window">

View file

@ -1,13 +1,12 @@
import React from "react";
import { connect } from "react-redux";
import { selectPlatform } from "selectors/app";
import { selectPlatform, selectCurrentModal } from "selectors/app";
import {
makeSelectFileInfoForUri,
makeSelectDownloadingForUri,
makeSelectLoadingForUri,
} from "selectors/file_info";
import { makeSelectIsAvailableForUri } from "selectors/availability";
import { selectCurrentModal } from "selectors/app";
import { makeSelectCostInfoForUri } from "selectors/cost_info";
import { doCloseModal, doOpenModal } from "actions/app";
import { doFetchAvailability } from "actions/availability";

View file

@ -1,6 +1,6 @@
import React from "react";
import { connect } from "react-redux";
import { doNavigate } from "actions/app";
import { doNavigate } from "actions/navigation";
import { doResolveUri } from "actions/content";
import { selectShowNsfw } from "selectors/settings";
import {

View file

@ -6,7 +6,7 @@ import {
selectCurrentSearchResults,
selectSearchQuery,
} from "selectors/search";
import { doNavigate } from "actions/app";
import { doNavigate } from "actions/navigation";
import FileListSearch from "./view";
const select = state => ({

View file

@ -1,6 +1,6 @@
import React from "react";
import { connect } from "react-redux";
import { doNavigate } from "actions/app";
import { doNavigate } from "actions/navigation";
import { doResolveUri } from "actions/content";
import {
makeSelectClaimForUri,

View file

@ -1,9 +1,16 @@
import React from "react";
import { formatCredits } from "util/formatCredits";
import { connect } from "react-redux";
import { selectIsBackDisabled, selectIsForwardDisabled } from "selectors/app";
import {
selectIsBackDisabled,
selectIsForwardDisabled,
} from "selectors/navigation";
import { selectBalance } from "selectors/wallet";
import { doNavigate, doHistoryBack, doHistoryForward } from "actions/app";
import {
doNavigate,
doHistoryBack,
doHistoryForward,
} from "actions/navigation";
import Header from "./view";
const select = state => ({

View file

@ -1,6 +1,6 @@
import React from "react";
import { connect } from "react-redux";
import { doNavigate } from "actions/app";
import { doNavigate } from "actions/navigation";
import NsfwOverlay from "./view";
const perform = dispatch => ({

View file

@ -5,7 +5,7 @@ import {
makeSelectRewardByType,
makeSelectIsRewardClaimPending,
} from "selectors/rewards";
import { doNavigate } from "actions/app";
import { doNavigate } from "actions/navigation";
import { doClaimRewardType, doClaimRewardClearError } from "actions/rewards";
import RewardLink from "./view";

View file

@ -1,7 +1,10 @@
import React from "react";
import { connect } from "react-redux";
import Router from "./view.jsx";
import { selectCurrentPage, selectCurrentParams } from "selectors/app.js";
import {
selectCurrentPage,
selectCurrentParams,
} from "selectors/navigation.js";
const select = state => ({
params: selectCurrentParams(state),

View file

@ -1,11 +1,10 @@
import React from "react";
import { connect } from "react-redux";
import { doNavigate, doRemoveSnackBarSnack } from "actions/app";
import { doRemoveSnackBarSnack } from "actions/app";
import { selectSnackBarSnacks } from "selectors/app";
import SnackBar from "./view";
const perform = dispatch => ({
navigate: path => dispatch(doNavigate(path)),
removeSnack: () => dispatch(doRemoveSnackBarSnack()),
});

View file

@ -10,7 +10,7 @@ class SnackBar extends React.PureComponent {
}
render() {
const { navigate, snacks, removeSnack } = this.props;
const { snacks, removeSnack } = this.props;
if (!snacks.length) {
this._hideTimeout = null; //should be unmounting anyway, but be safe?
@ -33,7 +33,7 @@ class SnackBar extends React.PureComponent {
{linkText &&
linkTarget &&
<Link
onClick={() => navigate(linkTarget)}
navigate={linkTarget}
className="snack-bar__action"
label={linkText}
/>}

View file

@ -1,7 +1,7 @@
import React from "react";
import { connect } from "react-redux";
import { selectCurrentPage, selectHeaderLinks } from "selectors/app";
import { doNavigate } from "actions/app";
import { selectCurrentPage, selectHeaderLinks } from "selectors/navigation";
import { doNavigate } from "actions/navigation";
import SubHeader from "./view";
const select = (state, props) => ({

View file

@ -1,6 +1,6 @@
import React from "react";
import { connect } from "react-redux";
import { doNavigate } from "actions/app";
import { doNavigate } from "actions/navigation";
import { doUserIdentityVerify } from "actions/user";
import rewards from "rewards";
import { makeSelectRewardByType } from "selectors/rewards";

View file

@ -1,7 +1,7 @@
import React from "react";
import { connect } from "react-redux";
import { doCloseModal } from "actions/app";
import { doNavigate, doChangeVolume } from "actions/app";
import { doChangeVolume } from "actions/app";
import { selectCurrentModal, selectVolume } from "selectors/app";
import { doPurchaseUri, doLoadVideo } from "actions/content";
import {

View file

@ -2,7 +2,7 @@ import React from "react";
import { connect } from "react-redux";
import lbryuri from "lbryuri.js";
import { selectWunderBarAddress, selectWunderBarIcon } from "selectors/search";
import { doNavigate } from "actions/app";
import { doNavigate } from "actions/navigation";
import Wunderbar from "./view";
const select = state => ({

View file

@ -1,16 +1,19 @@
export const CHANGE_PATH = "CHANGE_PATH";
export const OPEN_MODAL = "OPEN_MODAL";
export const CLOSE_MODAL = "CLOSE_MODAL";
export const HISTORY_NAVIGATE = "HISTORY_NAVIGATE";
export const SHOW_SNACKBAR = "SHOW_SNACKBAR";
export const REMOVE_SNACKBAR_SNACK = "REMOVE_SNACKBAR_SNACK";
export const WINDOW_FOCUSED = "WINDOW_FOCUSED";
export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH";
export const DAEMON_READY = "DAEMON_READY";
export const DAEMON_VERSION_MATCH = "DAEMON_VERSION_MATCH";
export const DAEMON_VERSION_MISMATCH = "DAEMON_VERSION_MISMATCH";
export const VOLUME_CHANGED = "VOLUME_CHANGED";
// Navigation
export const CHANGE_PATH = "CHANGE_PATH";
export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH";
export const WINDOW_SCROLLED = "WINDOW_SCROLLED";
export const HISTORY_NAVIGATE = "HISTORY_NAVIGATE";
// Upgrades
export const UPGRADE_CANCELLED = "UPGRADE_CANCELLED";
export const DOWNLOAD_UPGRADE = "DOWNLOAD_UPGRADE";

View file

@ -6,9 +6,9 @@ import SnackBar from "component/snackBar";
import { Provider } from "react-redux";
import store from "store.js";
import SplashScreen from "component/splash";
import { doChangePath, doNavigate, doDaemonReady } from "actions/app";
import { doDaemonReady } from "actions/app";
import { doNavigate } from "actions/navigation";
import { doDownloadLanguages } from "actions/settings";
import { toQueryString } from "util/query_params";
import * as types from "constants/action_types";
const env = ENV;
@ -28,23 +28,6 @@ window.addEventListener("contextmenu", event => {
event.preventDefault();
});
window.addEventListener("popstate", (event, param) => {
event.preventDefault();
const hash = document.location.hash;
let action;
if (hash !== "") {
const url = hash.replace(/^#/, "");
const { params, scrollY } = event.state || {};
const queryString = toQueryString(params);
app.store.dispatch(doChangePath(`${url}?${queryString}`, { scrollY }));
} else {
app.store.dispatch(doChangePath("/discover"));
}
});
ipcRenderer.on("open-uri-requested", (event, uri) => {
if (uri && uri.startsWith("lbry://")) {
app.store.dispatch(doNavigate("/show", { uri }));

View file

@ -1,6 +1,7 @@
import React from "react";
import { connect } from "react-redux";
import { doCloseModal, doAuthNavigate } from "actions/app";
import { doCloseModal } from "actions/app";
import { doAuthNavigate } from "actions/navigation";
import { doSetClientSetting } from "actions/settings";
import { selectUserIsRewardApproved } from "selectors/user";
import { selectBalance } from "selectors/wallet";

View file

@ -1,6 +1,7 @@
import React from "react";
import { connect } from "react-redux";
import { doCloseModal, doNavigate } from "actions/app";
import { doCloseModal } from "actions/app";
import { doNavigate } from "actions/navigation";
import ModalInsufficientCredits from "./view";
const select = state => ({});

View file

@ -1,9 +1,8 @@
import React from "react";
import { connect } from "react-redux";
import { doCloseModal, doHistoryBack } from "actions/app";
import { doCloseModal } from "actions/app";
import { doDeleteFileAndGoBack } from "actions/file_info";
import { makeSelectClaimForUriIsMine } from "selectors/claims";
import batchActions from "util/batchActions";
import ModalRemoveFile from "./view";

View file

@ -1,11 +1,12 @@
import React from "react";
import { connect } from "react-redux";
import { selectCurrentModal, selectCurrentPage } from "selectors/app";
import { doOpenModal } from "actions/app";
import * as settings from "constants/settings";
import { selectCurrentModal } from "selectors/app";
import { selectCurrentPage } from "selectors/navigation";
import { selectCostForCurrentPageUri } from "selectors/cost_info";
import { makeSelectClientSetting } from "selectors/settings";
import { selectUser } from "selectors/user";
import { selectCostForCurrentPageUri } from "selectors/cost_info";
import * as settings from "constants/settings";
import { selectBalance } from "selectors/wallet";
import ModalRouter from "./view";

View file

@ -1,7 +1,7 @@
import React from "react";
import { doNavigate } from "actions/app";
import { doNavigate } from "actions/navigation";
import { connect } from "react-redux";
import { selectPathAfterAuth } from "selectors/app";
import { selectPathAfterAuth } from "selectors/navigation";
import {
selectAuthenticationIsPending,
selectEmailToVerify,

View file

@ -9,8 +9,8 @@ import {
makeSelectClaimsInChannelForCurrentPage,
makeSelectFetchingChannelClaims,
} from "selectors/claims";
import { selectCurrentParams } from "selectors/app";
import { doNavigate } from "actions/app";
import { selectCurrentParams } from "selectors/navigation";
import { doNavigate } from "actions/navigation";
import { makeSelectTotalPagesForChannel } from "selectors/content";
import ChannelPage from "./view";

View file

@ -1,6 +1,6 @@
import React from "react";
import { connect } from "react-redux";
import { doNavigate } from "actions/app";
import { doNavigate } from "actions/navigation";
import { doFetchFileInfo } from "actions/file_info";
import { makeSelectFileInfoForUri } from "selectors/file_info";
import { selectRewardContentClaimIds } from "selectors/content";

View file

@ -10,7 +10,7 @@ import {
selectIsFetchingClaimListMine,
} from "selectors/claims";
import { doFetchClaimListMine } from "actions/content";
import { doNavigate } from "actions/app";
import { doNavigate } from "actions/navigation";
import { doCancelAllResolvingUris } from "actions/content";
import FileListDownloaded from "./view";

View file

@ -7,7 +7,7 @@ import {
selectIsFetchingClaimListMine,
} from "selectors/claims";
import { doClaimRewardType } from "actions/rewards";
import { doNavigate } from "actions/app";
import { doNavigate } from "actions/navigation";
import { doCancelAllResolvingUris } from "actions/content";
import FileListPublished from "./view";

View file

@ -1,5 +1,5 @@
import React from "react";
import { doAuthNavigate } from "actions/app";
import { doAuthNavigate } from "actions/navigation";
import { connect } from "react-redux";
import { doFetchAccessToken } from "actions/user";
import { selectAccessToken, selectUser } from "selectors/user";

View file

@ -1,6 +1,6 @@
import React from "react";
import { connect } from "react-redux";
import { doNavigate, doHistoryBack } from "actions/app";
import { doNavigate, doHistoryBack } from "actions/navigation";
import { doClaimRewardType } from "actions/rewards";
import {
selectMyClaims,

View file

@ -5,7 +5,7 @@ import {
selectUnclaimedRewards,
} from "selectors/rewards";
import { selectUser } from "selectors/user";
import { doAuthNavigate, doNavigate } from "actions/app";
import { doAuthNavigate, doNavigate } from "actions/navigation";
import { doRewardList } from "actions/rewards";
import RewardsPage from "./view";

View file

@ -5,7 +5,7 @@ import {
selectSearchQuery,
selectCurrentSearchResults,
} from "selectors/search";
import { doNavigate } from "actions/app";
import { doNavigate } from "actions/navigation";
import SearchPage from "./view";
const select = state => ({

View file

@ -1,12 +1,6 @@
import * as types from "constants/action_types";
import * as modalTypes from "constants/modal_types";
const currentPath = () => {
const hash = document.location.hash;
if (hash !== "") return hash.replace(/^#/, "");
else return "/discover";
};
const { remote } = require("electron");
const application = remote.app;
const win = remote.BrowserWindow.getFocusedWindow();
@ -14,28 +8,18 @@ const win = remote.BrowserWindow.getFocusedWindow();
const reducers = {};
const defaultState = {
isLoaded: false,
isBackDisabled: true,
isForwardDisabled: true,
currentPath: currentPath(),
pathAfterAuth: "/discover",
platform: process.platform,
upgradeSkipped: sessionStorage.getItem("upgradeSkipped"),
daemonVersionMatched: null,
daemonReady: false,
hasSignature: false,
badgeNumber: 0,
history: { index: 0, stack: [] },
volume: sessionStorage.getItem("volume") || 1,
};
reducers[types.DAEMON_READY] = function(state, action) {
const { history } = state;
const { page } = action.data;
history.stack.push(page);
return Object.assign({}, state, {
daemonReady: true,
history,
});
};
@ -52,18 +36,6 @@ reducers[types.DAEMON_VERSION_MISMATCH] = function(state, action) {
});
};
reducers[types.CHANGE_PATH] = function(state, action) {
return Object.assign({}, state, {
currentPath: action.data.path,
});
};
reducers[types.CHANGE_AFTER_AUTH_PATH] = function(state, action) {
return Object.assign({}, state, {
pathAfterAuth: action.data.path,
});
};
reducers[types.UPGRADE_CANCELLED] = function(state, action) {
return Object.assign({}, state, {
downloadProgress: null,
@ -171,55 +143,6 @@ reducers[types.WINDOW_FOCUSED] = function(state, action) {
});
};
reducers[types.HISTORY_NAVIGATE] = (state, action) => {
let page = false;
let location = false;
// Get history from state
const { history } = state;
if (action.data.page) {
// Get page
page = action.data.page;
} else if (action.data.location) {
// Get new location
location = action.data.location;
}
// Add new location to stack
if (location) {
const lastItem = history.stack.length - 1;
// Check for duplicated
let is_duplicate = lastItem > -1
? history.stack[lastItem].location === location
: false;
if (!is_duplicate) {
// Create new page
page = {
index: history.stack.length,
location,
};
// Update index
history.index = history.stack.length;
// Add to stack
history.stack.push(page);
}
} else if (page) {
// Update index
history.index = page.index;
}
return Object.assign({}, state, {
history,
isBackDisabled: history.index === 0, // First page
isForwardDisabled: history.index === history.stack.length - 1, // Last page
});
};
reducers[types.VOLUME_CHANGED] = function(state, action) {
return Object.assign({}, state, {
volume: action.data.volume,

View file

@ -0,0 +1,85 @@
import * as types from "constants/action_types";
import { parseQueryParams } from "util/query_params";
const currentPath = () => {
const hash = document.location.hash;
if (hash !== "") return hash.replace(/^#/, "");
else return "/discover";
};
const reducers = {};
const defaultState = {
currentPath: currentPath(),
pathAfterAuth: "/discover",
index: 0,
stack: [],
};
reducers[types.DAEMON_READY] = function(state, action) {
const { currentPath } = state;
const params = parseQueryParams(currentPath.split("?")[1] || "");
return Object.assign({}, state, {
stack: [{ path: currentPath, scrollY: 0 }],
});
};
reducers[types.CHANGE_PATH] = function(state, action) {
return Object.assign({}, state, {
currentPath: action.data.path,
});
};
reducers[types.CHANGE_AFTER_AUTH_PATH] = function(state, action) {
return Object.assign({}, state, {
pathAfterAuth: action.data.path,
});
};
reducers[types.HISTORY_NAVIGATE] = (state, action) => {
const { stack, index } = state;
let newState = {};
const path = action.data.url,
previousIndex = index - 1;
// Check for duplicated
if (action.data.index >= 0) {
newState.index = action.data.index;
} else if (
previousIndex === -1 ||
!stack[previousIndex] ||
stack[previousIndex].path !== path
) {
newState.stack = [...stack.slice(0, index + 1), { path, scrollY: 0 }];
newState.index = newState.stack.length - 1;
}
history.replaceState(null, null, "#" + path); //this allows currentPath() to retain the URL on reload
return Object.assign({}, state, newState);
};
reducers[types.WINDOW_SCROLLED] = (state, action) => {
const { stack, index } = state;
const { scrollY } = action.data;
return Object.assign({}, state, {
stack: state.stack.map((stackItem, itemIndex) => {
if (itemIndex !== index) {
return stackItem;
}
return {
...stackItem,
scrollY: scrollY,
};
}),
});
};
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
return state;
}

View file

@ -1,88 +1,7 @@
import { createSelector } from "reselect";
import { parseQueryParams, toQueryString } from "util/query_params";
import * as settings from "constants/settings.js";
import lbryuri from "lbryuri";
export const _selectState = state => state.app || {};
export const selectIsLoaded = createSelector(
_selectState,
state => state.isLoaded
);
export const selectCurrentPath = createSelector(
_selectState,
state => state.currentPath
);
export const selectCurrentPage = createSelector(selectCurrentPath, 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 selectPageTitle = createSelector(
selectCurrentPage,
selectCurrentParams,
(page, params) => {
switch (page) {
case "settings":
return __("Settings");
case "report":
return __("Report");
case "wallet":
return __("Wallet");
case "send":
return __("Send Credits");
case "receive":
return __("Wallet Address");
case "backup":
return __("Backup Your Wallet");
case "rewards":
return __("Rewards");
case "invite":
return __("Invites");
case "start":
return __("Start");
case "publish":
return __("Publish");
case "help":
return __("Help");
case "developer":
return __("Developer");
case "search":
return params.query
? __("Search results for %s", params.query)
: __("Search");
case "show": {
const parts = [lbryuri.normalize(params.uri)];
// If the params has any keys other than "uri"
if (Object.keys(params).length > 1) {
parts.push(toQueryString(Object.assign({}, params, { uri: null })));
}
return parts.join("?");
}
case "downloaded":
return __("Downloads & Purchases");
case "published":
return __("Publishes");
case "discover":
return __("Home");
case false:
case null:
case "":
return "";
default:
return page[0].toUpperCase() + (page.length > 0 ? page.substr(1) : "");
}
}
);
export const selectPlatform = createSelector(
_selectState,
state => state.platform
@ -137,41 +56,6 @@ export const selectDownloadComplete = createSelector(
state => state.upgradeDownloadCompleted
);
export const selectHeaderLinks = createSelector(selectCurrentPage, page => {
// This contains intentional fall throughs
switch (page) {
case "wallet":
case "history":
case "send":
case "receive":
case "invite":
case "rewards":
case "backup":
return {
wallet: __("Overview"),
history: __("History"),
send: __("Send"),
receive: __("Receive"),
rewards: __("Rewards"),
invite: __("Invites"),
};
case "downloaded":
case "published":
return {
downloaded: __("Downloaded"),
published: __("Published"),
};
case "settings":
case "help":
return {
settings: __("Settings"),
help: __("Help"),
};
default:
return null;
}
});
export const selectUpgradeSkipped = createSelector(
_selectState,
state => state.upgradeSkipped
@ -222,41 +106,4 @@ export const selectCurrentLanguage = createSelector(
() => app.i18n.getLocale() || "en"
);
export const selectPathAfterAuth = createSelector(
_selectState,
state => state.pathAfterAuth
);
export const selectIsBackDisabled = createSelector(
_selectState,
state => state.isBackDisabled
);
export const selectIsForwardDisabled = createSelector(
_selectState,
state => state.isForwardDisabled
);
export const selectHistoryBack = createSelector(_selectState, state => {
const { history } = state;
const index = history.index - 1;
// Check if page exists
if (index > -1) {
// Get back history
return history.stack[index];
}
});
export const selectHistoryForward = createSelector(_selectState, state => {
const { history } = state;
const index = history.index + 1;
// Check if page exists
if (index <= history.stack.length) {
// Get forward history
return history.stack[index];
}
});
export const selectVolume = createSelector(_selectState, state => state.volume);

View file

@ -1,5 +1,5 @@
import { createSelector } from "reselect";
import { selectCurrentParams } from "selectors/app";
import { selectCurrentParams } from "selectors/navigation";
import lbryuri from "lbryuri";
const _selectState = state => state.claims || {};

View file

@ -1,5 +1,5 @@
import { createSelector } from "reselect";
import { selectCurrentParams } from "./app";
import { selectCurrentParams } from "selectors/navigation";
export const _selectState = state => state.costInfo || {};

View file

@ -0,0 +1,129 @@
import { createSelector } from "reselect";
import { parseQueryParams, toQueryString } from "util/query_params";
import * as settings from "constants/settings.js";
import lbryuri from "lbryuri";
export const _selectState = state => state.navigation || {};
export const selectCurrentPath = createSelector(
_selectState,
state => state.currentPath
);
export const selectCurrentPage = createSelector(selectCurrentPath, 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 selectHeaderLinks = createSelector(selectCurrentPage, page => {
// This contains intentional fall throughs
switch (page) {
case "wallet":
case "send":
case "receive":
case "rewards":
case "backup":
return {
wallet: __("Overview"),
send: __("Send"),
receive: __("Receive"),
rewards: __("Rewards"),
};
case "downloaded":
case "published":
return {
downloaded: __("Downloaded"),
published: __("Published"),
};
case "settings":
case "help":
return {
settings: __("Settings"),
help: __("Help"),
};
default:
return null;
}
});
export const selectPageTitle = createSelector(
selectCurrentPage,
selectCurrentParams,
(page, params) => {
switch (page) {
case "settings":
return __("Settings");
case "report":
return __("Report");
case "wallet":
return __("Wallet");
case "send":
return __("Send");
case "receive":
return __("Receive");
case "backup":
return __("Backup");
case "rewards":
return __("Rewards");
case "start":
return __("Start");
case "publish":
return __("Publish");
case "help":
return __("Help");
case "developer":
return __("Developer");
case "search":
return params.query
? __("Search results for %s", params.query)
: __("Search");
case "show": {
const parts = [lbryuri.normalize(params.uri)];
// If the params has any keys other than "uri"
if (Object.keys(params).length > 1) {
parts.push(toQueryString(Object.assign({}, params, { uri: null })));
}
return parts.join("?");
}
case "downloaded":
return __("Downloads & Purchases");
case "published":
return __("Publishes");
case "discover":
return __("Home");
default:
return "";
}
}
);
export const selectPathAfterAuth = createSelector(
_selectState,
state => state.pathAfterAuth
);
export const selectIsBackDisabled = createSelector(
_selectState,
state => state.index === 0
);
export const selectIsForwardDisabled = createSelector(
_selectState,
state => state.index === state.stack.length - 1
);
export const selectHistoryIndex = createSelector(
_selectState,
state => state.index
);
export const selectHistoryStack = createSelector(
_selectState,
state => state.stack
);

View file

@ -1,5 +1,5 @@
import { createSelector } from "reselect";
import { selectPageTitle, selectCurrentPage } from "selectors/app";
import { selectPageTitle, selectCurrentPage } from "selectors/navigation";
export const _selectState = state => state.search || {};

View file

@ -1,5 +1,4 @@
import { createSelector } from "reselect";
import { selectCurrentPage, selectDaemonReady } from "selectors/app";
export const _selectState = state => state.wallet || {};

View file

@ -5,6 +5,7 @@ import claimsReducer from "reducers/claims";
import contentReducer from "reducers/content";
import costInfoReducer from "reducers/cost_info";
import fileInfoReducer from "reducers/file_info";
import navigationReducer from "reducers/navigation";
import rewardsReducer from "reducers/rewards";
import searchReducer from "reducers/search";
import settingsReducer from "reducers/settings";
@ -13,8 +14,8 @@ import walletReducer from "reducers/wallet";
import { persistStore, autoRehydrate } from "redux-persist";
import createCompressor from "redux-persist-transform-compress";
import createFilter from "redux-persist-transform-filter";
import { REHYDRATE } from "redux-persist/constants";
import createActionBuffer from "redux-action-buffer";
//import { REHYDRATE } from "redux-persist/constants";
//import createActionBuffer from "redux-action-buffer";
const localForage = require("localforage");
const redux = require("redux");
@ -55,6 +56,7 @@ function enableBatching(reducer) {
const reducers = redux.combineReducers({
app: appReducer,
navigation: navigationReducer,
availability: availabilityReducer,
claims: claimsReducer,
fileInfo: fileInfoReducer,