Retain scrollY when navigating back using the History API #365
9 changed files with 42 additions and 63 deletions
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -191,8 +191,3 @@ export const selectBadgeNumber = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
state => state.badgeNumber
|
state => state.badgeNumber
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectNavigatingBack = createSelector(
|
|
||||||
_selectState,
|
|
||||||
state => state.navigatingBack
|
|
||||||
);
|
|
||||||
|
|
Loading…
Reference in a new issue