Retain scrollY when navigating back using the History API #365

Merged
6ea86b96 merged 2 commits from scroll-back into master 2017-07-19 17:10:54 +02:00
9 changed files with 42 additions and 63 deletions

View file

@ -33,11 +33,11 @@ export function doNavigate(path, params = {}) {
const state = getState(); const state = getState();
const pageTitle = selectPageTitle(state); const pageTitle = selectPageTitle(state);
dispatch(doHistoryPush(params, pageTitle, url)); dispatch(doHistoryPush({ params }, pageTitle, url));
}; };
} }
export function doChangePath(path) { export function doChangePath(path, options = {}) {
return function(dispatch, getState) { return function(dispatch, getState) {
dispatch({ dispatch({
type: types.CHANGE_PATH, type: types.CHANGE_PATH,
@ -48,8 +48,12 @@ export function doChangePath(path) {
const state = getState(); const state = getState();
const pageTitle = selectPageTitle(state); const pageTitle = selectPageTitle(state);
const scrollY = options.scrollY;
window.document.title = pageTitle; window.document.title = pageTitle;
window.scrollTo(0, 0);
if (scrollY) window.scrollTo(0, scrollY);
else window.scrollTo(0, 0);
const currentPage = selectCurrentPage(state); const currentPage = selectCurrentPage(state);
if (currentPage === "search") { if (currentPage === "search") {
@ -64,24 +68,29 @@ export function doHistoryBack() {
if (!history.state) return; if (!history.state) return;
history.back(); history.back();
dispatch({
type: types.HISTORY_BACK,
});
}; };
} }
export function doHistoryBackCompleted() { export function doHistoryPush(currentState, title, relativeUrl) {
return function(dispatch, getState) {
dispatch({
type: types.HISTORY_BACK_COMPLETED,
});
};
}
export function doHistoryPush(params, title, relativeUrl) {
return function(dispatch, getState) { return function(dispatch, getState) {
title += " - LBRY"; title += " - LBRY";
history.pushState(params, title, `#${relativeUrl}`); history.pushState(currentState, title, `#${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}`
);
}; };
} }
@ -230,6 +239,7 @@ export function doAlertError(errorList) {
export function doDaemonReady() { export function doDaemonReady() {
return function(dispatch, getState) { return function(dispatch, getState) {
history.replaceState({}, document.title, `#/discover`);
dispatch(doAuthenticate()); dispatch(doAuthenticate());
dispatch({ dispatch({
type: types.DAEMON_READY, type: types.DAEMON_READY,

View file

@ -2,7 +2,11 @@ import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { selectCurrentModal } from "selectors/app"; import { selectCurrentModal } from "selectors/app";
import { doCheckUpgradeAvailable, doAlertError } from "actions/app"; import {
doCheckUpgradeAvailable,
doAlertError,
doRecordScroll,
} from "actions/app";
import { doUpdateBalance } from "actions/wallet"; import { doUpdateBalance } from "actions/wallet";
import App from "./view"; import App from "./view";
@ -14,6 +18,7 @@ const perform = dispatch => ({
alertError: errorList => dispatch(doAlertError(errorList)), alertError: errorList => dispatch(doAlertError(errorList)),
checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()), checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()),
updateBalance: balance => dispatch(doUpdateBalance(balance)), updateBalance: balance => dispatch(doUpdateBalance(balance)),
recordScroll: scrollPosition => dispatch(doRecordScroll(scrollPosition)),
}); });
export default connect(select, perform)(App); export default connect(select, perform)(App);

View file

@ -21,6 +21,14 @@ class App extends React.PureComponent {
lbry.balanceSubscribe(balance => { lbry.balanceSubscribe(balance => {
this.props.updateBalance(balance); this.props.updateBalance(balance);
}); });
this.scrollListener = () => this.props.recordScroll(window.scrollY);
window.addEventListener("scroll", this.scrollListener);
}
componentWillUnmount() {
window.removeEventListener("scroll", this.scrollListener);
} }
render() { render() {

View file

@ -2,7 +2,6 @@ export const CHANGE_PATH = "CHANGE_PATH";
export const OPEN_MODAL = "OPEN_MODAL"; export const OPEN_MODAL = "OPEN_MODAL";
export const CLOSE_MODAL = "CLOSE_MODAL"; export const CLOSE_MODAL = "CLOSE_MODAL";
export const HISTORY_BACK = "HISTORY_BACK"; export const HISTORY_BACK = "HISTORY_BACK";
export const HISTORY_BACK_COMPLETED = "HISTORY_BACK_COMPLETED";
export const SHOW_SNACKBAR = "SHOW_SNACKBAR"; export const SHOW_SNACKBAR = "SHOW_SNACKBAR";
export const REMOVE_SNACKBAR_SNACK = "REMOVE_SNACKBAR_SNACK"; export const REMOVE_SNACKBAR_SNACK = "REMOVE_SNACKBAR_SNACK";
export const WINDOW_FOCUSED = "WINDOW_FOCUSED"; export const WINDOW_FOCUSED = "WINDOW_FOCUSED";

View file

@ -37,10 +37,10 @@ window.addEventListener("popstate", (event, param) => {
if (hash !== "") { if (hash !== "") {
const url = hash.split("#")[1]; const url = hash.split("#")[1];
const params = event.state; const { params, scrollY } = event.state || {};
const queryString = toQueryString(params); const queryString = toQueryString(params);
app.store.dispatch(doChangePath(`${url}?${queryString}`)); app.store.dispatch(doChangePath(`${url}?${queryString}`, { scrollY }));
} else { } else {
app.store.dispatch(doChangePath("/discover")); app.store.dispatch(doChangePath("/discover"));
} }

View file

@ -1,24 +1,20 @@
import React from "react"; import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { doHistoryBackCompleted } from "actions/app";
import { doFetchFeaturedUris, doCancelAllResolvingUris } from "actions/content"; import { doFetchFeaturedUris, doCancelAllResolvingUris } from "actions/content";
import { import {
selectFeaturedUris, selectFeaturedUris,
selectFetchingFeaturedUris, selectFetchingFeaturedUris,
} from "selectors/content"; } from "selectors/content";
import { selectNavigatingBack } from "selectors/app";
import DiscoverPage from "./view"; import DiscoverPage from "./view";
const select = state => ({ const select = state => ({
featuredUris: selectFeaturedUris(state), featuredUris: selectFeaturedUris(state),
fetchingFeaturedUris: selectFetchingFeaturedUris(state), fetchingFeaturedUris: selectFetchingFeaturedUris(state),
isNavigatingBack: selectNavigatingBack(state),
}); });
const perform = dispatch => ({ const perform = dispatch => ({
fetchFeaturedUris: () => dispatch(doFetchFeaturedUris()), fetchFeaturedUris: () => dispatch(doFetchFeaturedUris()),
cancelResolvingUris: () => dispatch(doCancelAllResolvingUris()), cancelResolvingUris: () => dispatch(doCancelAllResolvingUris()),
finishedNavigatingBack: () => dispatch(doHistoryBackCompleted()),
}); });
export default connect(select, perform)(DiscoverPage); export default connect(select, perform)(DiscoverPage);

View file

@ -1,5 +1,4 @@
import React from "react"; import React from "react";
import lbry from "lbry.js";
import lbryio from "lbryio.js"; import lbryio from "lbryio.js";
import lbryuri from "lbryuri"; import lbryuri from "lbryuri";
import FileCard from "component/fileCard"; import FileCard from "component/fileCard";
@ -38,31 +37,10 @@ const FeaturedCategory = props => {
class DiscoverPage extends React.PureComponent { class DiscoverPage extends React.PureComponent {
componentWillMount() { componentWillMount() {
this.props.fetchFeaturedUris(); this.props.fetchFeaturedUris();
this.scrollListener = this.handleScroll.bind(this);
}
componentDidMount() {
if (this.props.isNavigatingBack) {
const scrollY = parseInt(lbry.getClientSetting("prefs_scrolly"));
if (!isNaN(scrollY)) {
const restoreScrollPosition = () => {
window.scrollTo(0, scrollY);
};
setTimeout(restoreScrollPosition, 100);
}
this.props.finishedNavigatingBack();
}
window.addEventListener("scroll", this.scrollListener);
}
handleScroll() {
lbry.setClientSetting("prefs_scrolly", window.scrollY);
} }
componentWillUnmount() { componentWillUnmount() {
this.props.cancelResolvingUris(); this.props.cancelResolvingUris();
window.removeEventListener("scroll", this.scrollListener);
} }
render() { render() {

View file

@ -141,18 +141,6 @@ reducers[types.WINDOW_FOCUSED] = function(state, action) {
}); });
}; };
reducers[types.HISTORY_BACK] = function(state, action) {
return Object.assign({}, state, {
navigatingBack: true,
});
};
reducers[types.HISTORY_BACK_COMPLETED] = function(state, action) {
return Object.assign({}, state, {
navigatingBack: false,
});
};
export default function reducer(state = defaultState, action) { export default function reducer(state = defaultState, action) {
const handler = reducers[action.type]; const handler = reducers[action.type];
if (handler) return handler(state, action); if (handler) return handler(state, action);

View file

@ -191,8 +191,3 @@ export const selectBadgeNumber = createSelector(
_selectState, _selectState,
state => state.badgeNumber state => state.badgeNumber
); );
export const selectNavigatingBack = createSelector(
_selectState,
state => state.navigatingBack
);