Right Click to Navigate History #3547
10 changed files with 208 additions and 48 deletions
|
@ -915,12 +915,7 @@
|
|||
"Support %amount% LBC": "Support %amount% LBC",
|
||||
"You deposited %amount% LBC as a support!": "You deposited %amount% LBC as a support!",
|
||||
"LBRY Link": "LBRY Link",
|
||||
"Publish to %uri%": "Publish to %uri%",
|
||||
"Your wallet": "Your wallet",
|
||||
"Publish a file, or create a channel": "Publish a file, or create a channel",
|
||||
"Your account": "Your account",
|
||||
"Channel profile picture": "Channel profile picture",
|
||||
"refreshing the app": "refreshing the app",
|
||||
"Follower": "Follower",
|
||||
"%repost_channel_link% reposted": "%repost_channel_link% reposted"
|
||||
"Your account": "Your account"
|
||||
}
|
|
@ -11,6 +11,7 @@ import WunderBar from 'component/wunderbar';
|
|||
import Icon from 'component/common/icon';
|
||||
import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button';
|
||||
import Tooltip from 'component/common/tooltip';
|
||||
import NavigationButton from 'component/navigationButton';
|
||||
// @if TARGET='app'
|
||||
import { IS_MAC } from 'component/app/view';
|
||||
// @endif
|
||||
|
@ -18,7 +19,15 @@ import { IS_MAC } from 'component/app/view';
|
|||
type Props = {
|
||||
balance: string,
|
||||
roundedBalance: number,
|
||||
history: { push: string => void, goBack: () => void, goForward: () => void, location: { pathname: string } },
|
||||
history: {
|
||||
entities: {}[],
|
||||
goBack: () => void,
|
||||
goForward: () => void,
|
||||
index: number,
|
||||
length: number,
|
||||
location: { pathname: string },
|
||||
push: string => void,
|
||||
},
|
||||
currentTheme: string,
|
||||
automaticDarkModeEnabled: boolean,
|
||||
setClientSetting: (string, boolean | string) => void,
|
||||
|
@ -105,21 +114,8 @@ const Header = (props: Props) => {
|
|||
{/* @if TARGET='app' */}
|
||||
{!authHeader && (
|
||||
<div className="header__navigation-arrows">
|
||||
<Button
|
||||
className="header__navigation-item header__navigation-item--back"
|
||||
description={__('Navigate back')}
|
||||
onClick={() => history.goBack()}
|
||||
icon={ICONS.ARROW_LEFT}
|
||||
iconSize={18}
|
||||
/>
|
||||
|
||||
<Button
|
||||
className="header__navigation-item header__navigation-item--forward"
|
||||
description={__('Navigate forward')}
|
||||
onClick={() => history.goForward()}
|
||||
icon={ICONS.ARROW_RIGHT}
|
||||
iconSize={18}
|
||||
/>
|
||||
<NavigationButton isBackward history={history} />
|
||||
<NavigationButton isBackward={false} history={history} />
|
||||
</div>
|
||||
)}
|
||||
{/* @endif */}
|
||||
|
|
3
ui/component/navigationButton/index.js
Normal file
3
ui/component/navigationButton/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import NavigationButton from './view';
|
||||
|
||||
export default NavigationButton;
|
91
ui/component/navigationButton/view.jsx
Normal file
91
ui/component/navigationButton/view.jsx
Normal file
|
@ -0,0 +1,91 @@
|
|||
// @flow
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import Button from 'component/button';
|
||||
|
||||
// the maximum length of history to show per button
|
||||
const MAX_HISTORY_SIZE = 12;
|
||||
|
||||
type Props = {
|
||||
isBackward: boolean,
|
||||
history: {
|
||||
entries: Array<{ key: string, title: string, pathname: string }>,
|
||||
go: number => void,
|
||||
goBack: () => void,
|
||||
goForward: () => void,
|
||||
index: number,
|
||||
length: number,
|
||||
location: { pathname: string },
|
||||
push: string => void,
|
||||
},
|
||||
};
|
||||
|
||||
// determines which slice of entries should make up the back or forward button drop-downs (isBackward vs !isBackward respectively)
|
||||
const sliceEntries = (currentIndex, entries, historyLength, isBackward, maxSize) => {
|
||||
let l = isBackward ? 0 : currentIndex + 1;
|
||||
let r = isBackward ? currentIndex : historyLength;
|
||||
const exceedsMax = maxSize < r - l;
|
||||
if (!exceedsMax) {
|
||||
return entries.slice(l, r);
|
||||
} else if (isBackward) {
|
||||
l = r - maxSize;
|
||||
} else {
|
||||
r = l + maxSize;
|
||||
}
|
||||
return entries.slice(l, r);
|
||||
};
|
||||
|
||||
const NavigationButton = (props: Props) => {
|
||||
const { isBackward, history } = props;
|
||||
const { entries, go } = history;
|
||||
const currentIndex = history.index;
|
||||
const historyLength = history.length;
|
||||
const [showHistory, setShowHistory] = useState(false);
|
||||
|
||||
// creates an <li> intended for the button's <ul>
|
||||
const makeItem = useCallback(
|
||||
(entry: { pathname: string, title: string, key: string }, index: number) => {
|
||||
// difference between the current index and the index of the entry
|
||||
const backwardDif = index - (currentIndex < MAX_HISTORY_SIZE ? currentIndex : MAX_HISTORY_SIZE);
|
||||
const forwardDif = index + 1;
|
||||
return (
|
||||
<li
|
||||
className="header__navigation-button"
|
||||
role="link"
|
||||
key={entry.key}
|
||||
onMouseDown={() => {
|
||||
setShowHistory(false);
|
||||
go(isBackward ? backwardDif : forwardDif);
|
||||
}}
|
||||
>
|
||||
<span>{entry.title}</span>
|
||||
<span className="header__navigation-button-help">{entry.pathname === '/' ? __('Home') : entry.pathname}</span>
|
||||
</li>
|
||||
);
|
||||
},
|
||||
[currentIndex, isBackward, go]
|
||||
);
|
||||
const slicedEntries = sliceEntries(currentIndex, entries, historyLength, isBackward, MAX_HISTORY_SIZE);
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
PR has been updated to disable button if no historical entries. PR has been updated to disable button if no historical entries.
Awesome! Awesome!
|
||||
className={`header__navigation-item header__navigation-item--${isBackward ? 'back' : 'forward'}`}
|
||||
description={isBackward ? __('Navigate back') : __('Navigate forward')}
|
||||
onBlur={() => setShowHistory(false)}
|
||||
onClick={() => (isBackward ? history.goBack() : history.goForward())}
|
||||
onContextMenu={e => {
|
||||
setShowHistory(!showHistory);
|
||||
// the following three lines prevent the regular context menu (right click menu) from appearing
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}}
|
||||
icon={isBackward ? ICONS.ARROW_LEFT : ICONS.ARROW_RIGHT}
|
||||
iconSize={18}
|
||||
disabled={slicedEntries.length === 0}
|
||||
/>
|
||||
{showHistory && <ul className={'header__navigation-dropdown'}>{slicedEntries.map(makeItem)}</ul>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default NavigationButton;
|
|
@ -2,10 +2,31 @@ import { connect } from 'react-redux';
|
|||
import { selectUserVerifiedEmail } from 'lbryinc';
|
||||
import { selectScrollStartingPosition } from 'redux/selectors/app';
|
||||
import Router from './view';
|
||||
import { normalizeURI, makeSelectTitleForUri } from 'lbry-redux';
|
||||
|
||||
const select = state => ({
|
||||
const select = state => {
|
||||
const { pathname, hash } = state.router.location;
|
||||
const urlPath = pathname + hash;
|
||||
// Remove the leading "/" added by the browser
|
||||
const path = urlPath.slice(1).replace(/:/g, '#');
|
||||
|
||||
let uri;
|
||||
try {
|
||||
uri = normalizeURI(path);
|
||||
} catch (e) {
|
||||
const match = path.match(/[#/:]/);
|
||||
|
||||
if (!path.startsWith('$/') && match && match.index) {
|
||||
uri = `lbry://${path.slice(0, match.index)}`;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
uri,
|
||||
title: makeSelectTitleForUri(uri)(state),
|
||||
currentScroll: selectScrollStartingPosition(state),
|
||||
isAuthenticated: selectUserVerifiedEmail(state),
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(select)(Router);
|
||||
|
|
|
@ -29,6 +29,8 @@ import SignInPage from 'page/signIn';
|
|||
import SignInVerifyPage from 'page/signInVerify';
|
||||
import ChannelsPage from 'page/channels';
|
||||
import EmbedWrapperPage from 'page/embedWrapper';
|
||||
import { parseURI } from 'lbry-redux';
|
||||
import { SITE_TITLE } from 'config';
|
||||
|
||||
// Tell the browser we are handling scroll restoration
|
||||
if ('scrollRestoration' in history) {
|
||||
|
@ -61,6 +63,19 @@ type Props = {
|
|||
currentScroll: number,
|
||||
location: { pathname: string, search: string },
|
||||
isAuthenticated: boolean,
|
||||
history: {
|
||||
entries: { title: string }[],
|
||||
goBack: () => void,
|
||||
goForward: () => void,
|
||||
index: number,
|
||||
length: number,
|
||||
location: { pathname: string },
|
||||
push: string => void,
|
||||
state: {},
|
||||
replaceState: ({}, string, string) => void,
|
||||
},
|
||||
uri: string,
|
||||
title: string,
|
||||
};
|
||||
|
||||
function AppRouter(props: Props) {
|
||||
|
@ -68,7 +83,37 @@ function AppRouter(props: Props) {
|
|||
currentScroll,
|
||||
location: { pathname },
|
||||
isAuthenticated,
|
||||
history,
|
||||
uri,
|
||||
title,
|
||||
} = props;
|
||||
const { entries } = history;
|
||||
const entryIndex = history.index;
|
||||
|
||||
useEffect(() => {
|
||||
if (uri) {
|
||||
const { channelName, streamName } = parseURI(uri);
|
||||
|
||||
if (typeof title !== 'undefined' && title !== '') {
|
||||
document.title = title;
|
||||
} else if (streamName) {
|
||||
document.title = streamName;
|
||||
} else if (channelName) {
|
||||
Why did you move this effect out of the show component? Why did you move this effect out of the show component?
I want this to fire on every page and not just the pages that render using the ShowPage component. I want this to fire on every page and not just the pages that render using the ShowPage component.
|
||||
document.title = channelName;
|
||||
} else {
|
||||
document.title = IS_WEB ? SITE_TITLE : 'LBRY';
|
||||
}
|
||||
} else {
|
||||
document.title = IS_WEB ? SITE_TITLE : 'LBRY';
|
||||
}
|
||||
|
||||
// @if TARGET='app'
|
||||
entries[entryIndex].title = document.title;
|
||||
// @endif
|
||||
return () => {
|
||||
document.title = IS_WEB ? SITE_TITLE : 'LBRY';
|
||||
};
|
||||
}, [entries, entryIndex, title, uri]);
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, currentScroll);
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
// @flow
|
||||
import React, { useEffect } from 'react';
|
||||
import { parseURI } from 'lbry-redux';
|
||||
import BusyIndicator from 'component/common/busy-indicator';
|
||||
import ChannelPage from 'page/channel';
|
||||
import FilePage from 'page/file';
|
||||
import Page from 'component/page';
|
||||
import Button from 'component/button';
|
||||
import { SITE_TITLE } from 'config';
|
||||
import Card from 'component/common/card';
|
||||
|
||||
type Props = {
|
||||
|
@ -24,8 +22,7 @@ type Props = {
|
|||
};
|
||||
|
||||
function ShowPage(props: Props) {
|
||||
const { isResolvingUri, resolveUri, uri, claim, blackListedOutpoints, location, title, claimIsMine } = props;
|
||||
const { channelName, streamName } = parseURI(uri);
|
||||
const { isResolvingUri, resolveUri, uri, claim, blackListedOutpoints, location, claimIsMine } = props;
|
||||
const signingChannel = claim && claim.signing_channel;
|
||||
const canonicalUrl = claim && claim.canonical_url;
|
||||
const claimExists = claim !== null && claim !== undefined;
|
||||
|
@ -46,22 +43,6 @@ function ShowPage(props: Props) {
|
|||
}
|
||||
}, [resolveUri, isResolvingUri, canonicalUrl, uri, claimExists, haventFetchedYet]);
|
||||
|
||||
useEffect(() => {
|
||||
if (title) {
|
||||
document.title = title;
|
||||
} else if (streamName) {
|
||||
document.title = streamName;
|
||||
} else if (channelName) {
|
||||
document.title = channelName;
|
||||
} else {
|
||||
document.title = IS_WEB ? SITE_TITLE : 'LBRY';
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.title = IS_WEB ? SITE_TITLE : 'LBRY';
|
||||
};
|
||||
}, [title, channelName, streamName]);
|
||||
|
||||
let innerContent = '';
|
||||
|
||||
if (!claim || (claim && !claim.name)) {
|
||||
|
|
|
@ -147,3 +147,29 @@
|
|||
.header__navigation-item--balance {
|
||||
margin: 0 var(--spacing-medium);
|
||||
}
|
||||
|
||||
.header__navigation-dropdown {
|
||||
@extend .menu__list--header;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
list-style-type: none;
|
||||
background-color: var(--color-header-background);
|
||||
}
|
||||
|
||||
.header__navigation-button {
|
||||
margin: 0;
|
||||
padding: var(--spacing-miniscule) var(--spacing-medium);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: var(--color-menu-background--active);
|
||||
}
|
||||
}
|
||||
|
||||
.header__navigation-button-help {
|
||||
@extend .help;
|
||||
margin-top: 0;
|
||||
margin-left: var(--spacing-small);
|
||||
}
|
||||
|
|
|
@ -55,6 +55,8 @@
|
|||
margin-left: calc(var(--spacing-medium) * -1);
|
||||
box-shadow: var(--card-box-shadow);
|
||||
animation: menu-animate-in var(--animation-duration) var(--animation-style);
|
||||
border-bottom-left-radius: var(--border-radius);
|
||||
border-bottom-right-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.menu__link {
|
||||
|
|
|
@ -7,7 +7,7 @@ import { createFilter, createBlacklistFilter } from 'redux-persist-transform-fil
|
|||
import localForage from 'localforage';
|
||||
import { createStore, applyMiddleware, compose } from 'redux';
|
||||
import thunk from 'redux-thunk';
|
||||
import { createHashHistory, createBrowserHistory } from 'history';
|
||||
import { createMemoryHistory, createBrowserHistory } from 'history';
|
||||
import { routerMiddleware } from 'connected-react-router';
|
||||
import createRootReducer from './reducers';
|
||||
import { buildSharedStateMiddleware, ACTIONS as LBRY_REDUX_ACTIONS } from 'lbry-redux';
|
||||
|
@ -96,7 +96,7 @@ const persistOptions = {
|
|||
|
||||
let history;
|
||||
// @if TARGET='app'
|
||||
history = createHashHistory();
|
||||
history = createMemoryHistory();
|
||||
// @endif
|
||||
// @if TARGET='web'
|
||||
history = createBrowserHistory();
|
||||
|
|
Loading…
Reference in a new issue
Are you able to determine if we can go back here or not? If so, it would be great to disable the back button and give it some disabled style so it's easier to tell that you can't