New routing setup #2395

Merged
neb-b merged 4 commits from dev into master 2019-03-30 05:25:18 +01:00
97 changed files with 762 additions and 1008 deletions
Showing only changes of commit fbbbe526c4 - Show all commits

View file

@ -59,6 +59,7 @@
"@hot-loader/react-dom": "16.8",
"@lbry/color": "^1.0.2",
"@lbry/components": "^2.3.3",
"@reach/router": "^1.2.1",
"@types/three": "^0.93.1",
"async-exit-hook": "^2.0.1",
"babel-eslint": "^10.0.1",
@ -106,8 +107,8 @@
"husky": "^0.14.3",
"json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#2a7e05940f892f104428eeb37bd1f178da811a09",
"lbryinc": "lbryio/lbryinc#437260f3dad2321e892388b68749ee6af01aff2b",
"lbry-redux": "lbryio/lbry-redux#86f1340f834d0f5cd5365492a8ff15d4b213a050",
"lbryinc": "lbryio/lbryinc#d9f9035113c8b9ab3b0ee7ffbd38f910086a665e",
"lint-staged": "^7.0.2",
"localforage": "^1.7.1",
"make-runnable": "^1.3.6",

View file

@ -35,9 +35,7 @@ export default appState => {
},
};
const rendererURL = isDev // ? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`
? `http://localhost:8080/index.dev.html`
: `file://${__dirname}/index.html`;
const rendererURL = isDev ? `http://localhost:8080` : `file://${__dirname}/index.html`;
let window = new BrowserWindow(windowConfiguration);

View file

@ -5,4 +5,8 @@ const port = 1337;
app.use(express.static(__dirname));
app.listen(port, () => console.log(`UI server listening at localhost://${port}`));
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, '/index.html'));
});
app.listen(port, () => console.log(`UI server listening at http://localhost:${port}`));

View file

@ -1,13 +1,6 @@
import { hot } from 'react-hot-loader/root';
import { connect } from 'react-redux';
import {
selectPageTitle,
selectHistoryIndex,
selectActiveHistoryEntry,
doUpdateBlockHeight,
doError,
} from 'lbry-redux';
import { doRecordScroll } from 'redux/actions/navigation';
import { doUpdateBlockHeight, doError } from 'lbry-redux';
import { doToggleEnhancedLayout } from 'redux/actions/app';
import { selectUser } from 'lbryinc';
import { selectThemePath } from 'redux/selectors/settings';
@ -15,17 +8,13 @@ import { selectEnhancedLayout } from 'redux/selectors/app';
import App from './view';
const select = state => ({
pageTitle: selectPageTitle(state),
user: selectUser(state),
currentStackIndex: selectHistoryIndex(state),
currentPageAttributes: selectActiveHistoryEntry(state),
theme: selectThemePath(state),
enhancedLayout: selectEnhancedLayout(state),
});
const perform = dispatch => ({
alertError: errorList => dispatch(doError(errorList)),
recordScroll: scrollPosition => dispatch(doRecordScroll(scrollPosition)),
updateBlockHeight: () => dispatch(doUpdateBlockHeight()),
toggleEnhancedLayout: () => dispatch(doToggleEnhancedLayout()),
});

View file

@ -14,9 +14,6 @@ const TWO_POINT_FIVE_MINUTES = 1000 * 60 * 2.5;
type Props = {
alertError: (string | {}) => void,
recordScroll: number => void,
currentStackIndex: number,
currentPageAttributes: { path: string, scrollY: number },
pageTitle: ?string,
theme: string,
updateBlockHeight: () => void,
@ -25,12 +22,6 @@ type Props = {
};
class App extends React.PureComponent<Props> {
constructor() {
super();
this.mainContent = undefined;
(this: any).scrollListener = this.scrollListener.bind(this);
}
componentWillMount() {
const { alertError, theme } = this.props;
@ -47,12 +38,6 @@ class App extends React.PureComponent<Props> {
componentDidMount() {
const { updateBlockHeight, toggleEnhancedLayout } = this.props;
const mainContent = document.getElementById('content');
this.mainContent = mainContent;
if (this.mainContent) {
this.mainContent.addEventListener('scroll', throttle(this.scrollListener, 750));
}
ReactModal.setAppElement('#window'); // fuck this
this.enhance = new EnhancedLayoutListener(() => toggleEnhancedLayout());
@ -63,18 +48,9 @@ class App extends React.PureComponent<Props> {
}, TWO_POINT_FIVE_MINUTES);
}
componentWillReceiveProps(props: Props) {
const { pageTitle } = props;
this.setTitleFromProps(pageTitle);
}
componentDidUpdate(prevProps: Props) {
const { currentStackIndex: prevStackIndex, theme: prevTheme } = prevProps;
const { currentStackIndex, currentPageAttributes, theme } = this.props;
if (this.mainContent && currentStackIndex !== prevStackIndex && currentPageAttributes) {
this.mainContent.scrollTop = currentPageAttributes.scrollY || 0;
}
const { theme: prevTheme } = prevProps;
const { theme } = this.props;
if (prevTheme !== theme) {
// $FlowFixMe
@ -83,26 +59,9 @@ class App extends React.PureComponent<Props> {
}
componentWillUnmount() {
if (this.mainContent) {
this.mainContent.removeEventListener('scroll', this.scrollListener);
}
this.enhance = null;
}
setTitleFromProps = (title: ?string) => {
window.document.title = title || 'LBRY';
};
scrollListener() {
const { recordScroll } = this.props;
if (this.mainContent) {
recordScroll(this.mainContent.scrollTop);
}
}
mainContent: ?HTMLElement;
enhance: ?any;
render() {

View file

@ -1,12 +1,3 @@
import { connect } from 'react-redux';
import { doNavigate } from 'redux/actions/navigation';
import Button from './view';
const perform = dispatch => ({
doNavigate: (path, params) => dispatch(doNavigate(path, params)),
});
export default connect(
null,
perform
)(Button);
export default Button;

View file

@ -2,6 +2,8 @@
import * as React from 'react';
import Icon from 'component/common/icon';
import classnames from 'classnames';
import { Link } from '@reach/router';
import { formatLbryUriForWeb } from 'util/uri';
type Props = {
onClick: ?(any) => any,
@ -13,9 +15,6 @@ type Props = {
disabled: ?boolean,
children: ?React.Node,
navigate: ?string,
// TODO: these (nav) should be a reusable type
doNavigate: (string, ?any) => void,
navigateParams: any,
className: ?string,
description: ?string,
type: string,
@ -24,7 +23,7 @@ type Props = {
iconColor?: string,
iconSize?: number,
constrict: ?boolean, // to shorten the button and ellipsis, only use for links
selected: ?boolean,
activeClass?: string,
};
class Button extends React.PureComponent<Props> {
@ -43,8 +42,6 @@ class Button extends React.PureComponent<Props> {
disabled,
children,
navigate,
navigateParams,
doNavigate,
className,
description,
button,
@ -53,7 +50,7 @@ class Button extends React.PureComponent<Props> {
iconColor,
iconSize,
constrict,
selected,
activeClass,
...otherProps
} = this.props;
@ -64,28 +61,19 @@ class Button extends React.PureComponent<Props> {
},
button
? {
'button--primary': button === 'primary',
'button--secondary': button === 'secondary',
'button--alt': button === 'alt',
'button--danger': button === 'danger',
'button--inverse': button === 'inverse',
'button--disabled': disabled,
'button--link': button === 'link',
'button--constrict': constrict,
'button--selected': selected,
}
'button--primary': button === 'primary',
'button--secondary': button === 'secondary',
'button--alt': button === 'alt',
'button--danger': button === 'danger',
'button--inverse': button === 'inverse',
'button--disabled': disabled,
'button--link': button === 'link',
'button--constrict': constrict,
}
: 'button--no-style',
className
);
const extendedOnClick =
!onClick && navigate
? event => {
event.stopPropagation();
doNavigate(navigate, navigateParams || {});
}
: onClick;
const content = (
<span className="button__content">
{icon && <Icon icon={icon} iconColor={iconColor} size={iconSize} />}
@ -95,16 +83,43 @@ class Button extends React.PureComponent<Props> {
</span>
);
return href ? (
<a className={combinedClassName} href={href} title={title}>
if (href) {
return (
<a href={href} className={combinedClassName}>
{content}
</a>
);
}
// Handle lbry:// uris passed in, or already formatted web urls
let path = navigate;
if (path) {
if (path.startsWith('lbry://')) {
path = formatLbryUriForWeb(path);
} else if (!path.startsWith('/')) {
// Force a leading slash so new paths aren't appended on to the current path
path = `/${path}`;
}
}
return path ? (
<Link
to={path}
title={title}
onClick={e => e.stopPropagation()}
getProps={({ isCurrent }) => ({
className:
isCurrent && activeClass ? `${combinedClassName} ${activeClass}` : combinedClassName,
})}
>
{content}
</a>
</Link>
) : (
<button
title={title}
aria-label={description || label || title}
className={combinedClassName}
onClick={extendedOnClick}
onClick={onClick}
disabled={disabled}
type={type}
{...otherProps}

View file

@ -1,11 +1,7 @@
import { connect } from 'react-redux';
import { doFetchClaimsByChannel } from 'redux/actions/content';
import { makeSelectCategoryListUris } from 'redux/selectors/content';
import {
makeSelectFetchingChannelClaims,
doResolveUris,
selectActiveHistoryEntry,
} from 'lbry-redux';
import { makeSelectFetchingChannelClaims, doResolveUris } from 'lbry-redux';
import { selectShowNsfw } from 'redux/selectors/settings';
import CategoryList from './view';
@ -13,7 +9,6 @@ const select = (state, props) => ({
urisInList: makeSelectCategoryListUris(props.uris, props.categoryLink)(state),
fetching: makeSelectFetchingChannelClaims(props.categoryLink)(state),
obscureNsfw: !selectShowNsfw(state),
currentPageAttributes: selectActiveHistoryEntry(state),
});
const perform = dispatch => ({

View file

@ -1,12 +1,13 @@
// @flow
import * as ICONS from 'constants/icons';
import React, { PureComponent, createRef } from 'react';
import { normalizeURI } from 'lbry-redux';
import { normalizeURI, parseURI } from 'lbry-redux';
import ToolTip from 'component/common/tooltip';
import FileCard from 'component/fileCard';
import Button from 'component/button';
import SubscribeButton from 'component/subscribeButton';
import throttle from 'util/throttle';
import { formatLbryUriForWeb } from 'util/uri';
type Props = {
category: string,
@ -44,7 +45,7 @@ class CategoryList extends PureComponent<Props, State> {
(this: any).handleScrollNext = this.handleScrollNext.bind(this);
(this: any).handleScrollPrevious = this.handleScrollPrevious.bind(this);
(this: any).handleArrowButtonsOnScroll = this.handleArrowButtonsOnScroll.bind(this);
(this: any).handleResolveOnScroll = this.handleResolveOnScroll.bind(this);
// (this: any).handleResolveOnScroll = this.handleResolveOnScroll.bind(this);
this.scrollWrapper = createRef();
}
@ -75,32 +76,34 @@ class CategoryList extends PureComponent<Props, State> {
}
}
componentDidUpdate(prevProps: Props) {
const { scrollY: previousScrollY } = prevProps.currentPageAttributes;
const { scrollY } = this.props.currentPageAttributes;
// The old lazy loading for home page relied on the navigation reducers copy of the scroll height
// Keeping it commented out for now to try and find a better way for better TTI on the homepage
// componentDidUpdate(prevProps: Props) {
// const {scrollY: previousScrollY} = prevProps.currentPageAttributes;
// const {scrollY} = this.props.currentPageAttributes;
if (scrollY > previousScrollY) {
this.handleResolveOnScroll();
}
}
// if(scrollY > previousScrollY) {
// this.handleResolveOnScroll();
// }
// }
handleResolveOnScroll() {
const {
urisInList,
resolveUris,
currentPageAttributes: { scrollY },
} = this.props;
// handleResolveOnScroll() {
// const {
// urisInList,
// resolveUris,
// currentPageAttributes: {scrollY},
// } = this.props;
const scrollWrapper = this.scrollWrapper.current;
if (!scrollWrapper) {
return;
}
// const scrollWrapper = this.scrollWrapper.current;
// if(!scrollWrapper) {
// return;
// }
const shouldResolve = window.innerHeight > scrollWrapper.offsetTop - scrollY;
if (shouldResolve && urisInList) {
resolveUris(urisInList);
}
}
// const shouldResolve = window.innerHeight > scrollWrapper.offsetTop - scrollY;
// if(shouldResolve && urisInList) {
// resolveUris(urisInList);
// }
// }
handleArrowButtonsOnScroll() {
// Determine if the arrow buttons should be disabled
@ -261,6 +264,11 @@ class CategoryList extends PureComponent<Props, State> {
const isCommunityTopBids = category.match(/^community/i);
const showScrollButtons = isCommunityTopBids ? !obscureNsfw : true;
let channelLink;
if (categoryLink) {
channelLink = formatLbryUriForWeb(categoryLink);
}
return (
<section className="media-group--row">
<header className="media-group__header">
@ -268,11 +276,7 @@ class CategoryList extends PureComponent<Props, State> {
{categoryLink ? (
<div className="channel-info__actions">
<div className="channel-info__actions__group">
<Button
label={category}
navigate="/show"
navigateParams={{ uri: categoryLink, page: 1 }}
/>
<Button label={category} navigate={channelLink} />
<SubscribeButton
button="alt"
showSnackBarOnSubscribe
@ -313,7 +317,7 @@ class CategoryList extends PureComponent<Props, State> {
{__(
'The community top bids section is only visible if you allow mature content in the app. You can change your content viewing preferences'
)}{' '}
<Button button="link" navigate="/settings" label={__('here')} />.
<Button button="link" navigate="/$/settings" label={__('here')} />.
</p>
) : (
<ul className="media-scrollhouse" ref={this.scrollWrapper}>

View file

@ -5,7 +5,6 @@ import {
makeSelectIsUriResolving,
makeSelectTotalItemsForChannel,
} from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import ChannelTile from './view';
const select = (state, props) => ({
@ -15,7 +14,6 @@ const select = (state, props) => ({
});
const perform = dispatch => ({
navigate: (path, params) => dispatch(doNavigate(path, params)),
resolveUri: uri => dispatch(doResolveUri(uri)),
});

View file

@ -5,6 +5,8 @@ import TruncatedText from 'component/common/truncated-text';
import classnames from 'classnames';
import SubscribeButton from 'component/subscribeButton';
import type { Claim } from 'types/claim';
import { navigate } from '@reach/router';
import { formatLbryUriForWeb } from 'util/uri';
type Props = {
uri: string,
@ -13,7 +15,6 @@ type Props = {
size: string,
claim: ?Claim,
resolveUri: string => void,
navigate: (string, ?{}) => void,
};
class ChannelTile extends React.PureComponent<Props> {
@ -36,7 +37,7 @@ class ChannelTile extends React.PureComponent<Props> {
}
render() {
const { claim, navigate, isResolvingUri, totalItems, uri, size } = this.props;
const { claim, isResolvingUri, totalItems, uri, size } = this.props;
let channelName;
let subscriptionUri;
@ -45,7 +46,7 @@ class ChannelTile extends React.PureComponent<Props> {
subscriptionUri = `lbry://${claim.permanent_url}`;
}
const onClick = () => navigate('/show', { uri, page: 1 });
const onClick = () => navigate(formatLbryUriForWeb(uri));
return (
<section

View file

@ -26,7 +26,7 @@ class FileExporter extends React.PureComponent<Props> {
constructor() {
super();
this.handleButtonClick = this.handleButtonClick.bind(this);
(this: any).handleButtonClick = this.handleButtonClick.bind(this);
}
handleFileCreation(filename: string, data: any) {

View file

@ -114,4 +114,5 @@ export const customIcons = {
[ICONS.UNSUBSCRIBE]: buildIcon(
<path d="M 12,5.67 10.94,4.61 C 5.7533356,-0.57666427 -2.0266644,7.2033357 3.16,12.39 l 1.06,1.06 7.78,7.78 7.78,-7.78 1.06,-1.06 c 2.149101,-2.148092 2.149101,-5.6319078 0,-7.78 -2.148092,-2.1491008 -5.631908,-2.1491008 -7.78,0 L 9.4481298,8.2303201 15.320603,9.2419066 11.772427,13.723825" />
),
[ICONS.LBRY]: <span>LBRY</span>, // Fix this
};

View file

@ -1,11 +1,9 @@
import { connect } from 'react-redux';
import { doOpenModal } from 'redux/actions/app';
import { doNavigate } from 'redux/actions/navigation';
import ExternalLink from './view';
const select = () => ({});
const perform = dispatch => ({
navigate: (path, params) => dispatch(doNavigate(path, params)),
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
});

View file

@ -4,6 +4,7 @@ import * as ICONS from 'constants/icons';
import * as React from 'react';
import { isURIValid } from 'lbry-redux';
import Button from 'component/button';
import { navigate } from '@reach/router';
type Props = {
href: string,
@ -45,14 +46,7 @@ class ExternalLink extends React.PureComponent<Props> {
// Return local link if protocol is lbry uri
if (protocol && protocol[0] === 'lbry:' && isURIValid(href)) {
element = (
<Button
button="link"
title={title || href}
label={children}
onClick={() => navigate('/show', { uri: href })}
/>
);
element = <Button button="link" title={title || href} label={children} navigate={href} />;
}
return element;

View file

@ -9,7 +9,6 @@ import {
makeSelectClaimIsPending,
} from 'lbry-redux';
import { selectRewardContentClaimIds } from 'lbryinc';
import { doNavigate } from 'redux/actions/navigation';
import { makeSelectContentPositionForUri } from 'redux/selectors/content';
import { selectShowNsfw } from 'redux/selectors/settings';
import { makeSelectIsSubscribed, makeSelectIsNew } from 'redux/selectors/subscriptions';
@ -31,7 +30,6 @@ const select = (state, props) => ({
});
const perform = dispatch => ({
navigate: (path, params) => dispatch(doNavigate(path, params)),
resolveUri: uri => dispatch(doResolveUri(uri)),
clearHistoryUri: uri => dispatch(doClearContentHistoryUri(uri)),
});

View file

@ -11,13 +11,14 @@ import classnames from 'classnames';
import FilePrice from 'component/filePrice';
import { openCopyLinkMenu } from 'util/context-menu';
import DateTime from 'component/dateTime';
import { navigate } from '@reach/router';
import { formatLbryUriForWeb } from 'util/uri';
type Props = {
uri: string,
claim: ?Claim,
fileInfo: ?{},
metadata: ?Metadata,
navigate: (string, ?{}) => void,
rewardedContentClaimIds: Array<string>,
obscureNsfw: boolean,
claimIsMine: boolean,
@ -61,7 +62,6 @@ class FileCard extends React.PureComponent<Props> {
claim,
fileInfo,
metadata,
navigate,
rewardedContentClaimIds,
obscureNsfw,
claimIsMine,
@ -108,11 +108,16 @@ class FileCard extends React.PureComponent<Props> {
}
};
const onClick = e => {
e.stopPropagation();
navigate(formatLbryUriForWeb(uri));
};
return (
<li
tabIndex="0"
role="button"
onClick={!pending ? () => navigate('/show', { uri }) : () => {}}
onClick={!pending && claim ? onClick : () => {}}
className={classnames('media-card', {
'card--link': !pending,
'media--pending': pending,

View file

@ -21,7 +21,6 @@ class FileListSearch extends React.PureComponent<Props> {
<React.Fragment>
<div className="search__results">
<section className="search__results-section">
<div className="search__results-title">{__('Search Results')}</div>
<HiddenNsfwClaims uris={uris} />
{!isSearching && uris && uris.length ? (
uris.map(uri =>

View file

@ -9,7 +9,6 @@ import {
} from 'lbry-redux';
import { selectRewardContentClaimIds } from 'lbryinc';
import { selectShowNsfw } from 'redux/selectors/settings';
import { doNavigate } from 'redux/actions/navigation';
import { doClearPublish, doUpdatePublishForm } from 'redux/actions/publish';
import { makeSelectIsSubscribed, makeSelectIsNew } from 'redux/selectors/subscriptions';
import FileTile from './view';
@ -28,7 +27,6 @@ const select = (state, props) => ({
const perform = dispatch => ({
clearPublish: () => dispatch(doClearPublish()),
navigate: (path, params) => dispatch(doNavigate(path, params)),
resolveUri: uri => dispatch(doResolveUri(uri)),
updatePublishForm: value => dispatch(doUpdatePublishForm(value)),
});

View file

@ -12,6 +12,8 @@ import FilePrice from 'component/filePrice';
import UriIndicator from 'component/uriIndicator';
import DateTime from 'component/dateTime';
import Yrbl from 'component/yrbl';
import { navigate } from '@reach/router';
import { formatLbryUriForWeb } from 'util/uri';
type Props = {
obscureNsfw: boolean,
@ -23,7 +25,6 @@ type Props = {
claim: ?Claim,
metadata: ?Metadata,
resolveUri: string => void,
navigate: (string, ?{}) => void,
clearPublish: () => void,
updatePublishForm: ({}) => void,
hideNoResult: boolean, // don't show the tile if there is no claim at this uri
@ -83,7 +84,6 @@ class FileTile extends React.PureComponent<Props> {
claim,
metadata,
isResolvingUri,
navigate,
obscureNsfw,
claimIsMine,
clearPublish,
@ -115,7 +115,7 @@ class FileTile extends React.PureComponent<Props> {
return displayHiddenMessage ? (
<span className="help">
{__('This file is hidden because it is marked NSFW. Update your')}{' '}
<Button button="link" navigate="/settings" label={__('content viewing preferences')} />{' '}
<Button button="link" navigate="/$/settings" label={__('content viewing preferences')} />{' '}
{__('to see it')}.
</span>
) : null;
@ -136,9 +136,9 @@ class FileTile extends React.PureComponent<Props> {
const wrapperProps = name
? {
onClick: () => navigate('/show', { uri }),
role: 'button',
}
onClick: () => navigate(formatLbryUriForWeb(uri)),
role: 'button',
}
: {};
return !name && hideNoResult ? null : (

View file

@ -3,7 +3,6 @@ import * as settings from 'constants/settings';
import { doChangeVolume } from 'redux/actions/app';
import { selectVolume } from 'redux/selectors/app';
import { doPlayUri, doSetPlayingUri, savePosition } from 'redux/actions/content';
import { doNavigate } from 'redux/actions/navigation';
import { doClaimEligiblePurchaseRewards, makeSelectCostInfoForUri } from 'lbryinc';
import {
makeSelectMetadataForUri,
@ -45,7 +44,6 @@ const perform = dispatch => ({
claimRewards: () => dispatch(doClaimEligiblePurchaseRewards()),
savePosition: (claimId, outpoint, position) =>
dispatch(savePosition(claimId, outpoint, position)),
navigate: (path, params) => dispatch(doNavigate(path, params)),
});
export default connect(

View file

@ -1,7 +1,6 @@
import { connect } from 'react-redux';
import { selectBalance, selectIsBackDisabled, selectIsForwardDisabled } from 'lbry-redux';
import { selectBalance } from 'lbry-redux';
import { formatCredits } from 'util/format-credits';
import { doNavigate, doHistoryBack, doHistoryForward } from 'redux/actions/navigation';
import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app';
import { doDownloadUpgradeRequested } from 'redux/actions/app';
import Header from './view';
@ -11,15 +10,10 @@ const select = state => ({
balance: selectBalance(state),
isUpgradeAvailable: selectIsUpgradeAvailable(state),
roundedBalance: formatCredits(selectBalance(state) || 0, 2),
isBackDisabled: selectIsBackDisabled(state),
isForwardDisabled: selectIsForwardDisabled(state),
});
const perform = dispatch => ({
downloadUpgradeRequested: () => dispatch(doDownloadUpgradeRequested()),
navigate: path => dispatch(doNavigate(path)),
back: () => dispatch(doHistoryBack()),
forward: () => dispatch(doHistoryForward()),
});
export default connect(

View file

@ -15,7 +15,6 @@ type Props = {
back: () => void,
forward: () => void,
downloadUpgradeRequested: any => void,
navigate: any => void,
};
const Header = (props: Props) => {
@ -24,7 +23,6 @@ const Header = (props: Props) => {
balance,
downloadUpgradeRequested,
isUpgradeAvailable,
navigate,
roundedBalance,
back,
isBackDisabled,
@ -38,6 +36,7 @@ const Header = (props: Props) => {
return (
<header className="header">
<div className="header__navigation">
{/* @if TARGET='app' */}
<Button
className="header__navigation-item header__navigation-item--wallet"
description={__('Your wallet')}
@ -48,7 +47,7 @@ const Header = (props: Props) => {
<LbcSymbol />
</React.Fragment>
}
onClick={() => navigate('/wallet')}
navigate="/$/wallet"
/>
<Button
@ -57,7 +56,6 @@ const Header = (props: Props) => {
disabled={isBackDisabled}
icon={ICONS.ARROW_LEFT}
iconSize={15}
onClick={back}
/>
<Button
@ -66,7 +64,6 @@ const Header = (props: Props) => {
disabled={isForwardDisabled}
icon={ICONS.ARROW_RIGHT}
iconSize={15}
onClick={forward}
/>
<Button
@ -74,8 +71,17 @@ const Header = (props: Props) => {
description={__('Home')}
icon={ICONS.HOME}
iconSize={15}
onClick={() => navigate('/discover')}
navigate="/"
/>
{/* @endif */}
{/* @if TARGET='web' */}
<Button
className="header__navigation-item header__navigation-item--lbry"
label={__('LBRY (logo)')}
iconRight={ICONS.LBRY}
navigate="/"
/>
{/* @endif */}
</div>
<WunderBar />
@ -94,7 +100,7 @@ const Header = (props: Props) => {
icon={ICONS.UPLOAD}
iconSize={24}
label={isUpgradeAvailable ? '' : __('Publish')}
onClick={() => navigate('/publish')}
navigate="/$/publish"
/>
{/* @if TARGET='app' */}

View file

@ -16,7 +16,7 @@ export default (props: Props) => {
<div className={className || 'help'}>
{numberOfNsfwClaims} {numberOfNsfwClaims > 1 ? __('files') : __('file')}{' '}
{__('hidden due to your')}{' '}
<Button button="link" navigate="/settings" label={__('content viewing preferences')} />.
<Button button="link" navigate="/$/settings" label={__('content viewing preferences')} />.
</div>
)
);

View file

@ -95,7 +95,7 @@ class InviteNew extends React.PureComponent<Props> {
/>
<p className="help">
{__('Earn')} <Button button="link" navigate="/rewards" label={__('rewards')} />{' '}
{__('Earn')} <Button button="link" navigate="/$/rewards" label={__('rewards')} />{' '}
{__('for inviting your friends.')} {__('Read our')}{' '}
<Button button="link" label={__('FAQ')} href="https://lbry.com/faq/referrals" />{' '}
{__('to learn more about referrals')}.

View file

@ -1,12 +0,0 @@
import { connect } from 'react-redux';
import { doNavigate } from 'redux/actions/navigation';
import NsfwOverlay from './view';
const perform = dispatch => ({
navigateSettings: () => dispatch(doNavigate('/settings')),
});
export default connect(
null,
perform
)(NsfwOverlay);

View file

@ -1,13 +0,0 @@
import React from 'react';
import Button from 'component/button';
const NsfwOverlay = () => (
<div className="card-overlay">
<p>
{__('This content is Not Safe For Work. To view adult content, please change your')}{' '}
<Button button="link" navigate="/settings" label={__('settings')} />.
</p>
</div>
);
export default NsfwOverlay;

View file

@ -1,33 +1,2 @@
import { connect } from 'react-redux';
import {
selectBalance,
selectPageTitle,
selectIsBackDisabled,
selectIsForwardDisabled,
} from 'lbry-redux';
import { doNavigate, doHistoryBack, doHistoryForward } from 'redux/actions/navigation';
import { doDownloadUpgrade } from 'redux/actions/app';
import { selectIsUpgradeAvailable, selectNavLinks } from 'redux/selectors/app';
import { formatCredits } from 'util/format-credits';
import Page from './view';
const select = state => ({
pageTitle: selectPageTitle(state),
navLinks: selectNavLinks(state),
isBackDisabled: selectIsBackDisabled(state),
isForwardDisabled: selectIsForwardDisabled(state),
isUpgradeAvailable: selectIsUpgradeAvailable(state),
balance: formatCredits(selectBalance(state) || 0, 2),
});
const perform = dispatch => ({
navigate: path => dispatch(doNavigate(path)),
back: () => dispatch(doHistoryBack()),
forward: () => dispatch(doHistoryForward()),
downloadUpgrade: () => dispatch(doDownloadUpgrade()),
});
export default connect(
select,
perform
)(Page);
export default Page;

View file

@ -91,11 +91,6 @@ class Page extends React.PureComponent<Props, State> {
'main--extra-padding': extraPadding,
})}
>
{pageTitle && (
<div className="page__header">
{pageTitle && <h1 className="page__title">{pageTitle}</h1>}
</div>
)}
{!loading && children}
{showLoader && (
<div className="page__empty">

View file

@ -1,6 +1,5 @@
import { connect } from 'react-redux';
import { makeSelectRewardByType, makeSelectIsRewardClaimPending, doClaimRewardType } from 'lbryinc';
import { doNavigate } from 'redux/actions/navigation';
import RewardLink from './view';
const select = (state, props) => ({
@ -10,7 +9,6 @@ const select = (state, props) => ({
const perform = dispatch => ({
claimReward: reward => dispatch(doClaimRewardType(reward.reward_type, { notifyError: true })),
navigate: path => dispatch(doNavigate(path)),
});
export default connect(

View file

@ -51,7 +51,7 @@ class RewardSummary extends React.Component<Props> {
<div className="card__actions">
<Button
button="primary"
navigate="/rewards"
navigate="/$/rewards"
label={hasRewards ? __('Claim Rewards') : __('View Rewards')}
/>
</div>

View file

@ -36,7 +36,7 @@ const RewardTile = (props: Props) => {
<Button button="primary" onClick={openRewardCodeModal} label={__('Enter Code')} />
)}
{reward.reward_type === rewards.TYPE_REFERRAL && (
<Button button="primary" navigate="/invite" label={__('Go To Invites')} />
<Button button="primary" navigate="/$/invite" label={__('Go To Invites')} />
)}
{reward.reward_type !== rewards.TYPE_REFERRAL &&
(claimed ? (

View file

@ -1,14 +1,2 @@
import { connect } from 'react-redux';
import { selectCurrentPage, selectCurrentParams, doToast } from 'lbry-redux';
import { doOpenModal } from 'redux/actions/app';
import Router from './view';
const select = state => ({
params: selectCurrentParams(state),
currentPage: selectCurrentPage(state),
});
export default connect(
select,
{ doOpenModal, doToast }
)(Router);
export default Router;

View file

@ -1,10 +1,10 @@
import * as PAGES from 'constants/pages';
import React from 'react';
import { Router } from '@reach/router';
import SettingsPage from 'page/settings';
import HelpPage from 'page/help';
import ReportPage from 'page/report';
import WalletPage from 'page/wallet';
import GetCreditsPage from 'page/getCredits';
import SendReceivePage from 'page/sendCredits';
import ShowPage from 'page/show';
import PublishPage from 'page/publish';
import DiscoverPage from 'page/discover';
@ -12,7 +12,6 @@ import RewardsPage from 'page/rewards';
import FileListDownloaded from 'page/fileListDownloaded';
import FileListPublished from 'page/fileListPublished';
import TransactionHistoryPage from 'page/transactionHistory';
import ChannelPage from 'page/channel';
import AuthPage from 'page/auth';
import InvitePage from 'page/invite';
import BackupPage from 'page/backup';
@ -20,41 +19,28 @@ import SubscriptionsPage from 'page/subscriptions';
import SearchPage from 'page/search';
import UserHistoryPage from 'page/userHistory';
const route = (props, page, routesMap) => {
const component = routesMap[page];
if (!component) {
props.doToast({
message: __('Invalid page requested'),
});
}
return component || routesMap.discover;
};
export default function AppRouter(props) {
return (
<Router>
<DiscoverPage path="/" />
<ShowPage path="/:claimName/:claimId" />
<ShowPage path="/:claimName" />
const Router = props => {
const { currentPage, params } = props;
return route(props, currentPage, {
auth: <AuthPage params={params} />,
backup: <BackupPage params={params} />,
channel: <ChannelPage params={params} />,
discover: <DiscoverPage params={params} />,
downloaded: <FileListDownloaded params={params} />,
help: <HelpPage params={params} />,
history: <TransactionHistoryPage params={params} />,
invite: <InvitePage params={params} />,
publish: <PublishPage params={params} />,
published: <FileListPublished params={params} />,
getcredits: <GetCreditsPage params={params} />,
report: <ReportPage params={params} />,
rewards: <RewardsPage params={params} />,
send: <SendReceivePage params={params} />,
settings: <SettingsPage params={params} />,
show: <ShowPage {...params} />,
wallet: <WalletPage params={params} />,
subscriptions: <SubscriptionsPage params={params} />,
search: <SearchPage {...params} />,
user_history: <UserHistoryPage {...params} />,
});
};
export default Router;
<AuthPage path={`$/${PAGES.AUTH}`} />
<BackupPage path={`$/${PAGES.BACKUP}`} />
<InvitePage path={`$/${PAGES.INVITE}`} />
<FileListDownloaded path={`$/${PAGES.DOWNLOADED}`} />
<FileListPublished path={`$/${PAGES.PUBLISHED}`} />
<HelpPage path={`$/${PAGES.HELP}`} />
<PublishPage path={`$/${PAGES.PUBLISH}`} />
<ReportPage path={`$/${PAGES.REPORT}`} />
<RewardsPage path={`$/${PAGES.REWARDS}`} />
<SearchPage path={`$/${PAGES.SEARCH}`} />
<SettingsPage path={`$/${PAGES.SETTINGS}`} />
<SubscriptionsPage path={`$/${PAGES.SUBSCRIPTIONS}`} />
<TransactionHistoryPage path={`$/${PAGES.HISTORY}`} />
<UserHistoryPage path={`$/${PAGES.USER_HISTORY}`} />
<WalletPage path={`$/${PAGES.WALLET}`} />
</Router>
);
}

View file

@ -8,7 +8,7 @@ import Button from 'component/button';
const ExpandableOptions = posed.div({
hide: { height: 0, opacity: 0 },
show: { height: 280, opacity: 1 },
show: { height: 300, opacity: 1 },
});
type Props = {
@ -34,11 +34,11 @@ const SearchOptions = (props: Props) => {
const resultCount = options[SEARCH_OPTIONS.RESULT_COUNT];
return (
<div className="card card--section search__options-wrapper">
<div className="search__options-wrapper">
<div className="card--space-between">
<Button
button="alt"
label={__('ADVANCED SEARCH')}
label={__('FILTER')}
iconRight={expanded ? ICONS.UP : ICONS.DOWN}
onClick={toggleSearchExpanded}
/>

View file

@ -1,10 +1,8 @@
import { connect } from 'react-redux';
import { selectNavLinks } from 'redux/selectors/app';
import { selectUnreadAmount } from 'redux/selectors/subscriptions';
import SideBar from './view';
const select = state => ({
navLinks: selectNavLinks(state),
unreadSubscriptionTotal: selectUnreadAmount(state),
});

View file

@ -1,4 +1,6 @@
// @flow
neb-b commented 2019-03-29 15:28:09 +01:00 (Migrated from github.com)
Review

This is the other big file. No longer bringing in the nav links from redux. Will add the tutorial back in a future PR.

This is the other big file. No longer bringing in the nav links from redux. Will add the tutorial back in a future PR.
import * as PAGES from 'constants/pages';
import * as ICONS from 'constants/icons';
import * as React from 'react';
import Button from 'component/button';
import classnames from 'classnames';
@ -14,86 +16,96 @@ type SideBarLink = {
};
type Props = {
navLinks: {
primary: Array<SideBarLink>,
secondary: Array<SideBarLink>,
},
unreadSubscriptionTotal: number,
};
class SideBar extends React.PureComponent<Props> {
renderNavLink(navLink: SideBarLink) {
const { label, path, active, subLinks = [], icon, guide } = navLink;
render() {
const { unreadSubscriptionTotal } = this.props;
const buildLink = (path, label, icon) => ({
navigate: path ? `$/${path}` : '/',
label,
icon,
});
const inner = (
<li
className={classnames('navigation__link', {
'navigation__link--active': active,
'navigation__link--guide': guide,
})}
key={label}
>
<Button icon={icon} label={label} navigate={path} />
{
// The sublinks should be animated on open close
// Removing it because the current implementation with CSSTransitionGroup
// was really slow and looked pretty bad. Possible fix is upgrading to v2
// Not sure if that has better performance
}
{!!subLinks.length && active && (
<ul key="0" className="navigation__link-items">
{subLinks.map(({ active: subLinkActive, label: subLabel, path: subPath }) => (
<li
className={classnames('navigation__link-item', {
'navigation__link-item--active': subLinkActive,
})}
key={subPath}
>
{subPath ? <Button label={subLabel} navigate={subPath} /> : <span>{subLabel}</span>}
</li>
))}
</ul>
)}
const renderLink = (linkProps, index) => (
<li key={index}>
<Button
{...linkProps}
className="navigation__link"
activeClass="navigation__link--active"
/>
</li>
);
return guide ? (
<Tooltip key={guide} alwaysVisible direction="right" body={guide}>
{inner}
</Tooltip>
) : (
inner
);
}
render() {
const { navLinks, unreadSubscriptionTotal } = this.props;
return (
<nav className="navigation">
<div className="navigation__links">
{navLinks.primary.map(({ label, path, active, icon }) => (
<Button
icon={icon}
className={classnames('navigation__link', {
'navigation__link--active': active,
})}
key={path}
label={
path === '/subscriptions' && unreadSubscriptionTotal
? `${label} (${unreadSubscriptionTotal})`
: label
}
navigate={path}
/>
))}
<ul className="navigation__links">
{[
{
...buildLink(null, __('Home'), ICONS.HOME),
},
{
...buildLink(
PAGES.SUBSCRIPTIONS,
`${__('Subscriptions')} ${
unreadSubscriptionTotal > 0 ? '(' + unreadSubscriptionTotal + ')' : ''
}`,
ICONS.SUBSCRIPTION
),
},
].map(renderLink)}
</ul>
<div className="navigation__link navigation__link--title">My LBRY</div>
<div className="navigation__links">
<ul>
<li className="navigation__link navigation__link--title">Account</li>
{navLinks.secondary.map(this.renderNavLink)}
{[
{
...buildLink(PAGES.DOWNLOADED, 'Downloads', ICONS.LOCAL),
},
{
...buildLink(PAGES.PUBLISHED, 'Publishes', ICONS.PUBLISHED),
},
{
...buildLink(PAGES.USER_HISTORY, 'History', ICONS.HISTORY),
},
{
...buildLink(PAGES.INVITE, 'Invite', ICONS.INVITE),
},
{
...buildLink(PAGES.REWARDS, 'Rewards', ICONS.FEATURED),
},
].map(renderLink)}
</ul>
</div>
<div className="navigation__link navigation__link--title">Wallet</div>
<ul className="navigation__links">
{[
{
...buildLink(PAGES.WALLET, 'Overview', ICONS.WALLET),
},
{
...buildLink(PAGES.HISTORY, 'Transactions', ICONS.TRANSACTIONS),
},
{
...buildLink(PAGES.BACKUP, 'Backup', ICONS.BACKUP),
},
].map(renderLink)}
</ul>
<ul className="navigation__links navigation__links--bottom">
{[
{
...buildLink(PAGES.SETTINGS, 'Settings', ICONS.SETTINGS),
},
{
...buildLink(PAGES.HELP, 'Help', ICONS.HELP),
},
].map(renderLink)}
</ul>
</nav>
);
}

View file

@ -1,6 +1,5 @@
import { connect } from 'react-redux';
import { selectClaimedRewardsByTransactionId } from 'lbryinc';
import { doNavigate } from 'redux/actions/navigation';
import { doOpenModal } from 'redux/actions/app';
import {
selectAllMyClaimsByOutpoint,
@ -16,7 +15,6 @@ const select = state => ({
});
const perform = dispatch => ({
navigate: (path, params) => dispatch(doNavigate(path, params)),
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
setTransactionFilter: filterSetting => dispatch(doSetTransactionListFilter(filterSetting)),
});

View file

@ -1,5 +1,6 @@
// @flow
import type { Transaction } from 'types/transaction';
import * as TXN_TYPES from 'constants/transaction_types';
import * as ICONS from 'constants/icons';
import React from 'react';
import ButtonTransaction from 'component/common/transaction-link';
@ -7,7 +8,7 @@ import CreditAmount from 'component/common/credit-amount';
import DateTime from 'component/dateTime';
import Button from 'component/button';
import { buildURI } from 'lbry-redux';
import * as txnTypes from 'constants/transaction_types';
import { formatLbryUriForWeb } from 'util/uri';
type Props = {
transaction: Transaction,
@ -26,7 +27,7 @@ class TransactionListItem extends React.PureComponent<Props> {
}
getLink(type: string) {
if (type === txnTypes.TIP) {
if (type === TXN_TYPES.TIP) {
return <Button icon={ICONS.UNLOCK} onClick={this.abandonClaim} title={__('Unlock Tip')} />;
}
return <Button icon={ICONS.DELETE} onClick={this.abandonClaim} title={__('Abandon Claim')} />;
@ -68,12 +69,7 @@ class TransactionListItem extends React.PureComponent<Props> {
<td className="table__item--actionable">
{reward && <span>{reward.reward_title}</span>}
{name && claimId && (
<Button
constrict
button="link"
navigate="/show"
navigateParams={{ uri: buildURI({ claimName: name, claimId }) }}
>
<Button constrict button="link" navigate={buildURI({ claimName: name, claimId })}>
{name}
</Button>
)}

View file

@ -35,7 +35,7 @@ class TransactionListRecent extends React.PureComponent<Props> {
<p className="card__subtitle">
{__('To view all of your transactions, navigate to the')}{' '}
<Button button="link" navigate="/history" label={__('transactions page')} />.
<Button button="link" navigate="/$/history" label={__('transactions page')} />.
</p>
</header>
@ -56,7 +56,7 @@ class TransactionListRecent extends React.PureComponent<Props> {
<div className="card__actions">
<Button
button="primary"
navigate="/history"
navigate="/$/history"
label={__('Full History')}
icon={icons.HISTORY}
/>

View file

@ -57,12 +57,7 @@ class UriIndicator extends React.PureComponent<Props> {
}
return (
<Button
noPadding
className="button--uri-indicator"
navigate="/show"
navigateParams={{ uri: channelLink, page: 1 }}
>
<Button noPadding className="button--uri-indicator" navigate={channelLink}>
{inner}
</Button>
);

View file

@ -5,7 +5,6 @@ import {
doUserCheckEmailVerified,
selectUser,
} from 'lbryinc';
import { doNavigate } from 'redux/actions/navigation';
import UserEmailVerify from './view';
const select = state => ({
@ -16,7 +15,6 @@ const select = state => ({
const perform = dispatch => ({
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)),
checkEmailVerified: () => dispatch(doUserCheckEmailVerified()),
navigate: path => dispatch(doNavigate(path)),
});
export default connect(

View file

@ -1,22 +1,15 @@
import { connect } from 'react-redux';
import { selectHistoryPageCount, makeSelectHistoryForPage } from 'redux/selectors/content';
import { doNavigate } from 'redux/actions/navigation';
import { selectCurrentParams, makeSelectCurrentParam } from 'lbry-redux';
import { doClearContentHistoryUri } from 'redux/actions/content';
import UserHistory from './view';
const select = state => {
const paramPage = Number(makeSelectCurrentParam('page')(state) || 0);
return {
pageCount: selectHistoryPageCount(state),
page: paramPage,
params: selectCurrentParams(state),
history: makeSelectHistoryForPage(paramPage)(state),
};
};
const select = state => ({
pageCount: selectHistoryPageCount(state),
// history: makeSelectHistoryForPage(paramPage)(state),
});
const perform = dispatch => ({
navigate: (path, params) => dispatch(doNavigate(path, params)),
clearHistoryUri: uri => dispatch(doClearContentHistoryUri(uri)),
});

View file

@ -14,7 +14,6 @@ type Props = {
history: Array<HistoryItem>,
page: number,
pageCount: number,
navigate: (string, {}) => void,
clearHistoryUri: string => void,
params: { page: number },
};
@ -54,7 +53,7 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
changePage(pageNumber: number) {
const { params } = this.props;
const newParams = { ...params, page: pageNumber };
this.props.navigate('/user_history', newParams);
// this.props.navigate('/user_history', newParams);
}
paginate(e: SyntheticKeyboardEvent<*>) {
@ -94,7 +93,7 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
}
render() {
const { history, page, pageCount } = this.props;
const { history = [], page, pageCount } = this.props;
const { itemsSelected } = this.state;
const allSelected = Object.keys(itemsSelected).length === history.length;
const selectHandler = allSelected ? this.unselectAll : this.selectAll;
@ -175,7 +174,7 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
<div className="card__content">
<div className="card__actions card__actions--center">
<Button button="primary" navigate="/discover" label={__('Explore new content')} />
<Button button="primary" navigate="/" label={__('Explore new content')} />
</div>
</div>
</section>

View file

@ -51,8 +51,7 @@ class UserHistoryItem extends React.PureComponent<Props> {
constrict
button="link"
label={name ? `lbry://${name}` : `lbry://...`}
navigate="/show"
navigateParams={{ uri }}
navigate={uri}
/>
</div>
);

View file

@ -1,7 +1,6 @@
import * as MODALS from 'constants/modal_types';
import { connect } from 'react-redux';
import { doOpenModal } from 'redux/actions/app';
import { doNavigate } from 'redux/actions/navigation';
import {
doUserIdentityVerify,
rewards,
@ -22,7 +21,6 @@ const select = state => {
};
const perform = dispatch => ({
navigate: uri => dispatch(doNavigate(uri)),
verifyUserIdentity: token => dispatch(doUserIdentityVerify(token)),
verifyPhone: () => dispatch(doOpenModal(MODALS.PHONE_COLLECTION)),
});

View file

@ -8,7 +8,6 @@ import { Lbryio } from 'lbryinc';
type Props = {
errorMessage: ?string,
isPending: boolean,
navigate: string => void,
verifyUserIdentity: string => void,
verifyPhone: () => void,
};
@ -25,7 +24,7 @@ class UserVerify extends React.PureComponent<Props> {
}
render() {
const { errorMessage, isPending, navigate, verifyPhone } = this.props;
const { errorMessage, isPending, verifyPhone } = this.props;
return (
<React.Fragment>
<section className="card card--section">
@ -137,11 +136,7 @@ class UserVerify extends React.PureComponent<Props> {
<div className="card__content">
<div className="card__actions">
<Button
onClick={() => navigate('/discover')}
button="primary"
label={__('Skip Rewards')}
/>
<Button navigate="/" button="primary" label={__('Skip Rewards')} />
</div>
</div>
</section>

View file

@ -1,44 +1,38 @@
import { connect } from 'react-redux';
import {
selectSearchState as selectSearch,
selectWunderBarAddress,
selectSearchSuggestions,
doUpdateSearchQuery,
doFocusSearchInput,
doBlurSearchInput,
doUpdateSearchQuery,
doSearch,
doToast,
selectSearchValue,
selectSearchSuggestions,
selectSearchBarFocused,
parseURI,
} from 'lbry-redux';
import analytics from 'analytics';
import { doNavigate } from 'redux/actions/navigation';
import Wunderbar from './view';
import { navigate } from '@reach/router';
import { formatLbryUriForWeb } from 'util/uri';
const select = state => {
const { isActive, searchQuery, ...searchState } = selectSearch(state);
const address = selectWunderBarAddress(state);
// if we are on the file/channel page
// use the address in the history stack
const wunderbarValue = isActive ? searchQuery : searchQuery || address;
return {
...searchState,
wunderbarValue,
suggestions: selectSearchSuggestions(state),
};
};
const select = state => ({
suggestions: selectSearchSuggestions(state),
searchQuery: selectSearchValue(state),
isFocused: selectSearchBarFocused(state),
});
const perform = dispatch => ({
onSearch: query => {
dispatch(doSearch(query));
dispatch(doNavigate(`/search`, { query }));
navigate(`/$/search?q=${query}`);
analytics.apiLogSearch();
},
onSubmit: (uri, extraParams) => dispatch(doNavigate('/show', { uri, ...extraParams })),
onSubmit: uri => {
const path = formatLbryUriForWeb(uri);
navigate(path);
dispatch(doUpdateSearchQuery(''));
},
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),
doFocus: () => dispatch(doFocusSearchInput()),
doBlur: () => dispatch(doBlurSearchInput()),
doShowSnackBar: props => dispatch(doToast(props)),
});
export default connect(

View file

@ -1,19 +1,22 @@
// @flow
import * as PAGES from 'constants/pages';
import * as ICONS from 'constants/icons';
import React from 'react';
import classnames from 'classnames';
import { normalizeURI, SEARCH_TYPES, isURIValid } from 'lbry-redux';
import { normalizeURI, SEARCH_TYPES, isURIValid, buildURI } from 'lbry-redux';
import Icon from 'component/common/icon';
import { parseQueryParams } from 'util/query-params';
import Autocomplete from './internal/autocomplete';
import { Location, navigate } from '@reach/router';
const L_KEY_CODE = 76;
const ESC_KEY_CODE = 27;
type Props = {
searchQuery: ?string,
updateSearchQuery: string => void,
onSearch: string => void,
onSubmit: (string, {}) => void,
onSubmit: string => void,
wunderbarValue: ?string,
suggestions: Array<string>,
doFocus: () => void,
@ -22,10 +25,18 @@ type Props = {
doShowSnackBar: ({}) => void,
};
class WunderBar extends React.PureComponent<Props> {
type State = {
query: ?string,
};
class WunderBar extends React.PureComponent<Props, State> {
constructor() {
super();
this.state = {
query: null,
};
(this: any).handleSubmit = this.handleSubmit.bind(this);
(this: any).handleChange = this.handleChange.bind(this);
(this: any).handleKeyDown = this.handleKeyDown.bind(this);
@ -61,6 +72,7 @@ class WunderBar extends React.PureComponent<Props> {
return;
}
// @if TARGET='app'
const shouldFocus =
process.platform === 'darwin'
? keyCode === L_KEY_CODE && metaKey
@ -70,30 +82,20 @@ class WunderBar extends React.PureComponent<Props> {
this.input.focus();
doFocus();
}
// @endif
}
}
handleChange(e: SyntheticInputEvent<*>) {
const { updateSearchQuery } = this.props;
const { value } = e.target;
const { updateSearchQuery } = this.props;
updateSearchQuery(value);
}
handleSubmit(value: string, suggestion?: { value: string, type: string }) {
const { onSubmit, onSearch, doShowSnackBar } = this.props;
const query = value.trim();
const getParams = () => {
const parts = query.split('?');
let extraParams = {};
if (parts.length > 0) {
extraParams = parseQueryParams(parts.join(''));
}
return extraParams;
};
const showSnackError = () => {
doShowSnackBar({
message: __('Invalid LBRY URL entered. Only A-Z, a-z, 0-9, and "-" allowed.'),
@ -105,23 +107,20 @@ class WunderBar extends React.PureComponent<Props> {
if (suggestion.type === 'search') {
onSearch(query);
} else if (isURIValid(query)) {
const params = getParams();
const uri = normalizeURI(query);
onSubmit(uri, params);
onSubmit(uri);
} else {
showSnackError();
}
return;
}
// Currently no suggestion is highlighted. The user may have started
// typing, then lost focus and came back later on the same page
try {
if (isURIValid(query)) {
const uri = normalizeURI(query);
const params = getParams();
onSubmit(uri, params);
onSubmit(uri);
} else {
showSnackError();
}
@ -133,7 +132,7 @@ class WunderBar extends React.PureComponent<Props> {
input: ?HTMLInputElement;
render() {
const { wunderbarValue, suggestions, doFocus, doBlur } = this.props;
const { suggestions, doFocus, doBlur, searchQuery } = this.props;
return (
<div className="wunderbar">
@ -141,7 +140,7 @@ class WunderBar extends React.PureComponent<Props> {
<Autocomplete
autoHighlight
wrapperStyle={{ flex: 1, position: 'relative' }}
value={wunderbarValue || ''}
value={searchQuery}
items={suggestions}
getItemValue={item => item.value}
onChange={this.handleChange}

View file

@ -28,7 +28,14 @@ export default class extends React.PureComponent<Props> {
return (
<div className="yrbl-wrap">
<img alt="Friendly gerbil" className={classnames('yrbl', className)} src={image} />
<img
alt="Friendly gerbil"
className={classnames('yrbl', className)}
src={
// If we don't use a leading `/` @reach/router will try to append the src url to the existing web url
`/${image}`
}
/>
{title && subtitle && (
<div className="card__content">
<h2 className="card__title">{title}</h2>

View file

@ -48,3 +48,6 @@ export const UP = 'ChevronUp';
export const DOWN = 'ChevronDown';
export const SECURE = 'Lock';
export const MENU = 'Menu';
export const BACKUP = 'Database';
export const TRANSACTIONS = 'FileText';
export const LBRY = '';

View file

@ -1,8 +1,8 @@
import ErrorBoundary from 'component/errorBoundary';
import App from 'component/app';
import SnackBar from 'component/snackBar';
import SplashScreen from 'component/splash';
// @if TARGET='app'
import SplashScreen from 'component/splash';
import moment from 'moment';
import { ipcRenderer, remote, shell } from 'electron';
import * as ACTIONS from 'constants/action_types';
@ -19,7 +19,6 @@ import {
doHideModal,
} from 'redux/actions/app';
import { Lbry, doToast, isURIValid, setSearchApi } from 'lbry-redux';
import { doNavigate, doHistoryBack, doHistoryForward } from 'redux/actions/navigation';
import { doDownloadLanguages, doUpdateIsNightAsync } from 'redux/actions/settings';
import { doAuthenticate, Lbryio, rewards, doBlackListedOutpointsSubscribe } from 'lbryinc';
import 'scss/all.scss';
@ -46,11 +45,11 @@ if (process.env.SEARCH_API_URL) {
// @if TARGET='app'
ipcRenderer.on('navigate-backward', () => {
app.store.dispatch(doHistoryBack());
// app.store.dispatch(doHistoryBack());
});
ipcRenderer.on('navigate-forward', () => {
app.store.dispatch(doHistoryForward());
// app.store.dispatch(doHistoryForward());
});
// @endif
@ -132,9 +131,9 @@ ipcRenderer.on('open-uri-requested', (event, uri, newSession) => {
app.store.dispatch(doConditionalAuthNavigate(newSession));
} else if (uri.startsWith(APPPAGEURL)) {
const navpage = uri.replace(APPPAGEURL, '').toLowerCase();
app.store.dispatch(doNavigate(`/${navpage}`));
// app.store.dispatch(doNavigate(`/${navpage}`));
} else if (isURIValid(uri)) {
app.store.dispatch(doNavigate('/show', { uri }));
// app.store.dispatch(doNavigate('/show', { uri }));
} else {
app.store.dispatch(
doToast({
@ -147,7 +146,7 @@ ipcRenderer.on('open-uri-requested', (event, uri, newSession) => {
ipcRenderer.on('open-menu', (event, uri) => {
if (uri && uri.startsWith('/help')) {
app.store.dispatch(doNavigate('/help'));
// app.store.dispatch(doNavigate('/help'));
}
});
@ -233,9 +232,11 @@ const init = () => {
app.store.dispatch(doBlackListedOutpointsSubscribe());
function onDaemonReady() {
// @if TARGET='app'
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(doDaemonReady());
// @endif
// app.store.dispatch(doDaemonReady());
ReactDOM.render(
<Provider store={store}>
<ErrorBoundary>
@ -245,11 +246,9 @@ const init = () => {
</Provider>,
document.getElementById('app')
);
// @if TARGET='web'
window.sessionStorage.removeItem('loaded');
// @endif
}
// @if TARGET='app'
if (window.sessionStorage.getItem('loaded') === 'y') {
onDaemonReady();
} else {
@ -263,6 +262,10 @@ const init = () => {
document.getElementById('app')
);
}
// @endif
// @if TARGET='web'
onDaemonReady();
// @endif
};
init();

View file

@ -1,11 +1,11 @@
import { connect } from 'react-redux';
import { doNavigate } from 'redux/actions/navigation';
import { doSetClientSetting } from 'redux/actions/settings';
import { selectUserIsRewardApproved, selectUnclaimedRewardValue } from 'lbryinc';
import { selectBalance } from 'lbry-redux';
import { doHideModal } from 'redux/actions/app';
import * as settings from 'constants/settings';
import ModalCreditIntro from './view';
import { navigate } from '@reach/router';
const select = state => ({
currentBalance: selectBalance(state),
@ -15,8 +15,8 @@ const select = state => ({
const perform = dispatch => () => ({
addBalance: () => {
navigate('/$/getcredits');
dispatch(doSetClientSetting(settings.CREDIT_REQUIRED_ACKNOWLEDGED, true));
dispatch(doNavigate('/getcredits'));
dispatch(doHideModal());
},
closeModal: () => {

View file

@ -1,6 +1,7 @@
// @flow
import React from 'react';
import { Modal } from 'modal/modal';
import { formatLbryUriForWeb } from 'util/uri';
// @if TARGET='app'
import { shell } from 'electron';
// @endif
@ -13,10 +14,16 @@ type Props = {
class ModalOpenExternalLink extends React.PureComponent<Props> {
openExternalLink() {
const { uri, closeModal } = this.props;
// @if TARGET='app'
const { openExternal } = shell;
if (uri) {
openExternal(uri);
}
// @endif
// @if TARGET='web'
window.open(uri);
// @endif
closeModal();
}

View file

@ -1,7 +1,6 @@
import { connect } from 'react-redux';
import { doHideModal } from 'redux/actions/app';
import { selectPhoneToVerify, selectUser } from 'lbryinc';
import { doNavigate } from 'redux/actions/navigation';
import ModalPhoneCollection from './view';
const select = state => ({
@ -12,7 +11,6 @@ const select = state => ({
const perform = dispatch => () => ({
closeModal: () => {
dispatch(doHideModal());
dispatch(doNavigate('/rewards'));
},
});

View file

@ -2,12 +2,10 @@ import { connect } from 'react-redux';
import { doHideModal } from 'redux/actions/app';
import ModalSendTip from './view';
import { doClearPublish } from 'redux/actions/publish';
import { doNavigate } from 'redux/actions/navigation';
const perform = dispatch => ({
closeModal: () => dispatch(doHideModal()),
clearPublish: () => dispatch(doClearPublish()),
navigate: (path, params) => dispatch(doNavigate(path, params)),
});
export default connect(

View file

@ -1,20 +1,15 @@
import { connect } from 'react-redux';
import * as settings from 'constants/settings';
import { selectBalance, selectCurrentPage, selectError, doToast } from 'lbry-redux';
import { selectBalance, makeSelectCostInfoForUri, selectError, doToast } from 'lbry-redux';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import {
selectUser,
selectUserIsVerificationCandidate,
selectCostForCurrentPageUri,
} from 'lbryinc';
import { selectUser, selectUserIsVerificationCandidate } from 'lbryinc';
import { selectModal } from 'redux/selectors/app';
import { doOpenModal } from 'redux/actions/app';
import ModalRouter from './view';
const select = state => ({
const select = (state, props) => ({
balance: selectBalance(state),
showPageCost: selectCostForCurrentPageUri(state),
page: selectCurrentPage(state),
showPageCost: makeSelectCostInfoForUri(props.uri)(state),
isVerificationCandidate: selectUserIsVerificationCandidate(state),
isCreditIntroAcknowledged: makeSelectClientSetting(settings.CREDIT_REQUIRED_ACKNOWLEDGED)(state),
isEmailCollectionAcknowledged: makeSelectClientSetting(settings.EMAIL_COLLECTION_ACKNOWLEDGED)(

View file

@ -113,6 +113,7 @@ class ModalRouter extends React.PureComponent<Props, State> {
isPaidShowPage(props: Props) {
const { page, showPageCost } = props;
// Fix me
return page === 'show' && showPageCost > 0;
}

View file

@ -1,6 +1,5 @@
import { selectPathAfterAuth } from 'lbry-redux';
import { connect } from 'react-redux';
import { doNavigate } from 'redux/actions/navigation';
import {
selectAuthenticationIsPending,
selectEmailToVerify,
@ -22,11 +21,7 @@ const select = state => ({
isVerificationCandidate: selectUserIsVerificationCandidate(state),
});
const perform = dispatch => ({
navigate: path => dispatch(doNavigate(path)),
});
export default connect(
select,
perform
null
)(AuthPage);

View file

@ -6,6 +6,7 @@ import UserEmailNew from 'component/userEmailNew';
import UserEmailVerify from 'component/userEmailVerify';
import UserVerify from 'component/userVerify';
import Page from 'component/page';
import { navigate } from '@reach/router';
type Props = {
isPending: boolean,
@ -16,7 +17,6 @@ type Props = {
is_reward_approved: boolean,
is_identity_verified: boolean,
},
navigate: (string, ?{}) => void,
};
class AuthPage extends React.PureComponent<Props> {
@ -29,14 +29,14 @@ class AuthPage extends React.PureComponent<Props> {
}
navigateIfAuthenticated = (props: Props) => {
const { isPending, user, pathAfterAuth, navigate } = props;
const { isPending, user } = props;
if (
!isPending &&
user &&
user.has_verified_email &&
(user.is_reward_approved || user.is_identity_verified)
) {
navigate(pathAfterAuth);
navigate('/');
}
};
@ -56,7 +56,6 @@ class AuthPage extends React.PureComponent<Props> {
}
render() {
const { navigate } = this.props;
const [innerContent, useTemplate] = this.renderMain();
return (
@ -69,11 +68,7 @@ class AuthPage extends React.PureComponent<Props> {
{`${__(
'This information is disclosed only to LBRY, Inc. and not to the LBRY network. It is only required to earn LBRY rewards and may be used to sync usage data across devices.'
)} `}
<Button
button="link"
onClick={() => navigate('/discover')}
label={__('Return home.')}
/>
<Button button="link" navigate="/" label={__('Return home.')} />
</p>
</section>
) : (

View file

@ -3,30 +3,24 @@ import { doFetchClaimsByChannel } from 'redux/actions/content';
import { PAGE_SIZE } from 'constants/claim';
import {
makeSelectClaimForUri,
makeSelectClaimsInChannelForCurrentPage,
makeSelectClaimsInChannelForCurrentPageState,
makeSelectFetchingChannelClaims,
makeSelectCurrentParam,
makeSelectClaimIsMine,
makeSelectTotalPagesForChannel,
selectCurrentParams,
} from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import { doOpenModal } from 'redux/actions/app';
import ChannelPage from './view';
const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
claimsInChannel: makeSelectClaimsInChannelForCurrentPage(props.uri)(state),
claimsInChannel: makeSelectClaimsInChannelForCurrentPageState(props.uri)(state),
fetching: makeSelectFetchingChannelClaims(props.uri)(state),
page: makeSelectCurrentParam('page')(state),
params: selectCurrentParams(state),
totalPages: makeSelectTotalPagesForChannel(props.uri, PAGE_SIZE)(state),
channelIsMine: makeSelectClaimIsMine(props.uri)(state),
});
const perform = dispatch => ({
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
navigate: (path, params) => dispatch(doNavigate(path, params)),
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
});

View file

@ -1,8 +1,9 @@
// @flow
import type { Claim } from 'types/claim';
import type { UrlLocation } from 'types/location';
import * as icons from 'constants/icons';
import * as MODALS from 'constants/modal_types';
import React from 'react';
import React, { useEffect } from 'react';
import BusyIndicator from 'component/common/busy-indicator';
import { FormField, Form } from 'component/common/form';
import ReactPaginate from 'react-paginate';
@ -11,10 +12,10 @@ import Page from 'component/page';
import FileList from 'component/fileList';
import HiddenNsfwClaims from 'component/hiddenNsfwClaims';
import Button from 'component/button';
import { navigate } from '@reach/router';
type Props = {
uri: string,
page: number,
totalPages: number,
fetching: boolean,
params: { page: number },
@ -24,32 +25,42 @@ type Props = {
fetchClaims: (string, number) => void,
navigate: (string, {}) => void,
openModal: (id: string, { uri: string }) => void,
location: UrlLocation,
};
class ChannelPage extends React.PureComponent<Props> {
componentDidMount() {
const { uri, page, fetchClaims } = this.props;
fetchClaims(uri, page || 1);
}
function ChannelPage(props: Props) {
const {
uri,
fetching,
claimsInChannel,
claim,
totalPages,
channelIsMine,
openModal,
fetchClaims,
location,
} = props;
componentDidUpdate(prevProps: Props) {
const { page, fetchClaims, uri } = this.props;
const { name, permanent_url: permanentUrl } = claim;
const { search } = location;
const urlParams = new URLSearchParams(search);
const page = Number(urlParams.get('page')) || 1;
if (prevProps.page && prevProps.page && page !== prevProps.page) {
fetchClaims(uri, page);
useEffect(() => {
// Fetch new claims if the channel or page number changes
fetchClaims(uri, page);
}, [uri, page]);
const changePage = (pageNumber: number) => {
if (!page && pageNumber === 1) {
return;
}
}
changePage(pageNumber: number) {
const { params } = this.props;
const newParams = Object.assign({}, params, { page: pageNumber });
navigate(`?page=${pageNumber}`);
};
this.props.navigate('/show', newParams);
}
paginate(e: SyntheticKeyboardEvent<*>, totalPages: number) {
// Change page if enter was pressed, and the given page is between
// the first and the last.
const paginate = (e: SyntheticKeyboardEvent<*>) => {
// Change page if enter was pressed, and the given page is between the first and the last page
const pageFromInput = Number(e.currentTarget.value);
if (
@ -59,90 +70,77 @@ class ChannelPage extends React.PureComponent<Props> {
pageFromInput > 0 &&
pageFromInput <= totalPages
) {
this.changePage(pageFromInput);
changePage(pageFromInput);
}
}
};
render() {
const {
uri,
fetching,
claimsInChannel,
claim,
page,
totalPages,
channelIsMine,
openModal,
} = this.props;
const { name, permanent_url: permanentUrl } = claim;
const currentPage = parseInt((page || 1) - 1, 10);
const contentList =
claimsInChannel && claimsInChannel.length ? (
<FileList sortByHeight hideFilter fileInfos={claimsInChannel} />
) : (
!fetching && <span className="empty">{__('No content found.')}</span>
);
return (
<Page notContained>
<header className="channel-info">
<h1 className="media__title media__title--large">
{name}
{fetching && <BusyIndicator />}
</h1>
<span>{`lbry://${permanentUrl}`}</span>
return (
<Page notContained>
<header className="channel-info">
<h1 className="media__title media__title--large">
{name}
{fetching && <BusyIndicator />}
</h1>
<div className="channel-info__actions__group">
<SubscribeButton uri={`lbry://${permanentUrl}`} channelName={name} />
<Button
button="alt"
icon={icons.SHARE}
label={__('Share Channel')}
onClick={() =>
openModal(MODALS.SOCIAL_SHARE, { uri, speechShareable: true, isChannel: true })
}
/>
</div>
</header>
<div className="channel-info__actions__group">
<SubscribeButton uri={`lbry://${permanentUrl}`} channelName={name} />
<Button
button="alt"
icon={icons.SHARE}
label={__('Share Channel')}
onClick={() =>
openModal(MODALS.SOCIAL_SHARE, { uri, speechShareable: true, isChannel: true })
}
/>
</div>
</header>
<section className="media-group--list">{contentList}</section>
{(!fetching || (claimsInChannel && claimsInChannel.length)) && totalPages > 1 && (
<Form>
<fieldset-group class="fieldset-group--smushed fieldgroup--paginate">
<fieldset-section>
<ReactPaginate
pageCount={totalPages}
pageRangeDisplayed={2}
previousLabel=""
nextLabel=""
activeClassName="pagination__item--selected"
pageClassName="pagination__item"
previousClassName="pagination__item pagination__item--previous"
nextClassName="pagination__item pagination__item--next"
breakClassName="pagination__item pagination__item--break"
marginPagesDisplayed={2}
onPageChange={e => this.changePage(e.selected + 1)}
forcePage={currentPage}
initialPage={currentPage}
containerClassName="pagination"
/>
</fieldset-section>
<FormField
className="paginate-channel"
onKeyUp={e => this.paginate(e, totalPages)}
label={__('Go to page:')}
type="text"
name="paginate-file"
/>
</fieldset-group>
</Form>
<section className="media-group--list">
{claimsInChannel && claimsInChannel.length ? (
<FileList sortByHeight hideFilter fileInfos={claimsInChannel} />
) : (
!fetching && <span className="empty">{__('No content found.')}</span>
)}
</section>
{!channelIsMine && <HiddenNsfwClaims className="card__content help" uri={uri} />}
</Page>
);
}
{(!fetching || (claimsInChannel && claimsInChannel.length)) && totalPages > 1 && (
<Form>
<fieldset-group class="fieldset-group--smushed fieldgroup--paginate">
<fieldset-section>
<ReactPaginate
pageCount={totalPages}
pageRangeDisplayed={2}
previousLabel=""
nextLabel=""
activeClassName="pagination__item--selected"
pageClassName="pagination__item"
previousClassName="pagination__item pagination__item--previous"
nextClassName="pagination__item pagination__item--next"
breakClassName="pagination__item pagination__item--break"
marginPagesDisplayed={2}
onPageChange={e => changePage(e.selected + 1)}
forcePage={page - 1}
initialPage={page - 1}
disableInitialCallback
containerClassName="pagination"
/>
</fieldset-section>
<FormField
className="paginate-channel"
onKeyUp={e => paginate(e)}
label={__('Go to page:')}
type="text"
name="paginate-file"
/>
</fieldset-group>
</Form>
)}
{!channelIsMine && <HiddenNsfwClaims className="card__content help" uri={uri} />}
</Page>
);
}
export default ChannelPage;

View file

@ -1,6 +1,5 @@
import { connect } from 'react-redux';
import * as settings from 'constants/settings';
import { doNavigate } from 'redux/actions/navigation';
import { selectRewardContentClaimIds, selectPlayingUri } from 'redux/selectors/content';
import { doRemoveUnreadSubscription } from 'redux/actions/subscriptions';
import { doSetClientSetting } from 'redux/actions/settings';
@ -15,10 +14,10 @@ import {
makeSelectChannelForClaimUri,
} from 'lbry-redux';
import {
makeSelectCostInfoForUri,
doFetchCostInfoForUri,
doFetchViewCount,
makeSelectViewCountForUri,
makeSelectCostInfoForUri,
doFetchCostInfoForUri,
} from 'lbryinc';
import { selectShowNsfw, makeSelectClientSetting } from 'redux/selectors/settings';
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
@ -43,7 +42,6 @@ const select = (state, props) => ({
});
const perform = dispatch => ({
navigate: (path, params) => dispatch(doNavigate(path, params)),
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)),
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),

View file

@ -39,7 +39,6 @@ type Props = {
channelUri: string,
viewCount: number,
prepareEdit: ({}, string) => void,
navigate: (string, ?{}) => void,
openModal: (id: string, { uri: string }) => void,
markSubscriptionRead: (string, string) => void,
fetchViewCount: string => void,
@ -132,7 +131,6 @@ class FilePage extends React.Component<Props> {
openModal,
claimIsMine,
prepareEdit,
navigate,
costInfo,
fileInfo,
channelUri,
@ -173,6 +171,7 @@ class FilePage extends React.Component<Props> {
return (
<Page notContained className="main--file-page">
<div className="grid-area--content">
<h1 className="media__uri">{uri}</h1>
{showFile && <FileViewer className="content__embedded" uri={uri} mediaType={mediaType} />}
{!showFile &&
(thumbnail ? (
@ -231,9 +230,9 @@ class FilePage extends React.Component<Props> {
button="primary"
icon={icons.EDIT}
label={__('Edit')}
navigate="/$/publish"
onClick={() => {
prepareEdit(claim, editUri);
navigate('/publish');
}}
/>
)}

View file

@ -5,7 +5,6 @@ import {
selectIsFetchingFileList,
selectFileListDownloadedSort,
} from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import FileListDownloaded from './view';
const select = state => ({
@ -15,11 +14,7 @@ const select = state => ({
sortBy: selectFileListDownloadedSort(state),
});
const perform = dispatch => ({
navigate: path => dispatch(doNavigate(path)),
});
export default connect(
select,
perform
null
)(FileListDownloaded);

View file

@ -8,13 +8,12 @@ import { PAGES } from 'lbry-redux';
type Props = {
fetching: boolean,
fileInfos: {},
navigate: (string, ?{}) => void,
sortBy: string,
};
class FileListDownloaded extends React.PureComponent<Props> {
render() {
const { fetching, fileInfos, navigate, sortBy } = this.props;
const { fetching, fileInfos, sortBy } = this.props;
const hasDownloads = fileInfos && Object.values(fileInfos).length > 0;
return (
@ -32,11 +31,7 @@ class FileListDownloaded extends React.PureComponent<Props> {
<div className="card__content">
<div className="card__actions card__actions--center">
<Button
button="primary"
onClick={() => navigate('/discover')}
label={__('Explore new content')}
/>
<Button button="primary" navigate="/" label={__('Explore new content')} />
</div>
</div>
</section>

View file

@ -4,7 +4,6 @@ import {
selectFileListPublishedSort,
selectMyClaimsWithoutChannels,
} from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import { doCheckPendingPublishes } from 'redux/actions/publish';
import FileListPublished from './view';
@ -15,7 +14,6 @@ const select = state => ({
});
const perform = dispatch => ({
navigate: path => dispatch(doNavigate(path)),
checkPendingPublishes: () => dispatch(doCheckPendingPublishes()),
});

View file

@ -9,7 +9,6 @@ import { PAGES } from 'lbry-redux';
type Props = {
claims: Array<Claim>,
checkPendingPublishes: () => void,
navigate: (string, ?{}) => void,
fetching: boolean,
sortBy: string,
};
@ -21,7 +20,7 @@ class FileListPublished extends React.PureComponent<Props> {
}
render() {
const { fetching, claims, navigate, sortBy } = this.props;
const { fetching, claims, sortBy } = this.props;
return (
<Page notContained loading={fetching}>
{claims && claims.length ? (
@ -45,7 +44,7 @@ class FileListPublished extends React.PureComponent<Props> {
<div className="card__actions card__actions--center">
<Button
button="primary"
onClick={() => navigate('/publish')}
navigate="/$/publish"
label={__('Publish something new')}
/>
</div>

View file

@ -200,7 +200,7 @@ class HelpPage extends React.PureComponent<Props, State> {
<div className="card__content">
<div className="card__actions">
<Button
navigate="/report"
navigate="/$/report"
label={__('Submit a Bug Report/Feature Request')}
icon={icons.REPORT}
button="primary"

View file

@ -1,6 +1,5 @@
import { connect } from 'react-redux';
import { doResolveUri, selectBalance } from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import {
selectPublishFormValues,
selectIsStillEditing,
@ -35,7 +34,6 @@ const perform = dispatch => ({
clearPublish: () => dispatch(doClearPublish()),
resolveUri: uri => dispatch(doResolveUri(uri)),
publish: params => dispatch(doPublish(params)),
navigate: path => dispatch(doNavigate(path)),
prepareEdit: (claim, uri) => dispatch(doPrepareEdit(claim, uri)),
resetThumbnailStatus: () => dispatch(doResetThumbnailStatus()),
});

View file

@ -6,7 +6,7 @@ import {
selectUser,
doRewardList,
} from 'lbryinc';
import { doAuthNavigate, doNavigate } from 'redux/actions/navigation';
import { doAuthNavigate } from 'redux/actions/navigation';
import { selectDaemonSettings } from 'redux/selectors/settings';
import RewardsPage from './view';
@ -20,7 +20,6 @@ const select = state => ({
const perform = dispatch => ({
fetchRewards: () => dispatch(doRewardList()),
navigate: path => dispatch(doNavigate(path)),
doAuth: () => {
dispatch(doAuthNavigate('/rewards'));
},

View file

@ -11,7 +11,6 @@ import { rewards as REWARD_TYPES } from 'lbryinc';
type Props = {
doAuth: () => void,
navigate: string => void,
fetching: boolean,
rewards: Array<Reward>,
claimed: Array<Reward>,
@ -28,7 +27,7 @@ type Props = {
class RewardsPage extends PureComponent<Props> {
renderPageHeader() {
const { doAuth, navigate, user, daemonSettings } = this.props;
const { doAuth, user, daemonSettings } = this.props;
if (user && !user.is_reward_approved && daemonSettings && daemonSettings.share_usage_data) {
if (!user.primary_email || !user.has_verified_email || !user.is_identity_verified) {
@ -68,7 +67,7 @@ class RewardsPage extends PureComponent<Props> {
)}`}
</p>
<p>
<Button onClick={() => navigate('/discover')} button="primary" label="Return Home" />
<Button navigate="/" button="primary" label="Return Home" />
</p>
</section>
);
@ -93,7 +92,7 @@ class RewardsPage extends PureComponent<Props> {
}
renderUnclaimedRewards() {
const { fetching, rewards, user, daemonSettings, navigate, claimed } = this.props;
const { fetching, rewards, user, daemonSettings, claimed } = this.props;
if (daemonSettings && !daemonSettings.share_usage_data) {
return (
@ -104,7 +103,7 @@ class RewardsPage extends PureComponent<Props> {
{__(
'Rewards are currently disabled for your account. Turn on diagnostic data sharing, in'
)}{' '}
<Button button="link" onClick={() => navigate('/settings')} label="Settings" />
<Button button="link" navigate="/$/settings" label="Settings" />
{__(', in order to re-enable them.')}
</p>
</header>
@ -132,8 +131,8 @@ class RewardsPage extends PureComponent<Props> {
<p className="card__content">
{claimed && claimed.length
? __(
"You have claimed all available rewards! We're regularly adding more so be sure to check back later."
)
"You have claimed all available rewards! We're regularly adding more so be sure to check back later."
)
: __('There are no rewards available at this time, please check back later.')}
</p>
</section>

View file

@ -1,17 +1,14 @@
import { connect } from 'react-redux';
import { selectIsSearching, makeSelectCurrentParam, doUpdateSearchQuery } from 'lbry-redux';
import { doNavigate } from 'redux/actions/navigation';
import { doSearch, selectIsSearching } from 'lbry-redux';
import SearchPage from './view';
const select = state => ({
isSearching: selectIsSearching(state),
query: makeSelectCurrentParam('query')(state),
});
const perform = dispatch => ({
navigate: path => dispatch(doNavigate(path)),
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),
});
const perform = {
doSearch,
};
export default connect(
select,

View file

@ -1,6 +1,7 @@
// @flow
import type { UrlLocation } from 'types/location';
import * as ICONS from 'constants/icons';
import * as React from 'react';
import React, { useEffect, Fragment } from 'react';
import { isURIValid, normalizeURI, parseURI } from 'lbry-redux';
import FileTile from 'component/fileTile';
import ChannelTile from 'component/channelTile';
@ -9,55 +10,88 @@ import Page from 'component/page';
import ToolTip from 'component/common/tooltip';
import Icon from 'component/common/icon';
import SearchOptions from 'component/searchOptions';
import { Location } from '@reach/router';
type Props = {
query: ?string,
};
type Props = { doSearch: string => void, location: UrlLocation };
class SearchPage extends React.PureComponent<Props> {
render() {
const { query } = this.props;
const isValid = isURIValid(query);
export default function SearchPage(props: Props) {
const {
doSearch,
location: { search },
} = props;
let uri;
let isChannel;
if (isValid) {
uri = normalizeURI(query);
({ isChannel } = parseURI(uri));
const urlParams = new URLSearchParams(location.search);
const urlQuery = urlParams.get('q');
useEffect(() => {
if (urlQuery) {
doSearch(urlQuery);
}
}, [urlQuery]);
return (
<Page noPadding>
<section className="search">
{query && isValid && (
<header className="search__header">
<h1 className="search__title">
{`lbry://${query}`}
<ToolTip
icon
body={__('This is the resolution of a LBRY URL and not controlled by LBRY Inc.')}
>
<Icon icon={ICONS.HELP} />
</ToolTip>
</h1>
{isChannel ? (
<ChannelTile size="large" isSearchResult uri={uri} />
) : (
<FileTile size="large" isSearchResult displayHiddenMessage uri={uri} />
return (
<Page noPadding>
<Location>
{(locationProps: {
location: {
search: string,
},
}) => {
const { location } = locationProps;
const urlParams = new URLSearchParams(location.search);
const query = urlParams.get('q');
const isValid = isURIValid(query);
let uri;
let isChannel;
let label;
if (isValid) {
uri = normalizeURI(query);
({ isChannel } = parseURI(uri));
// label = (
// <Fragment>
// {`lbry://${query}`}
// <ToolTip
// icon
// body={__('This is the resolution of a LBRY URL and not controlled by LBRY Inc.')}
// >
// <Icon icon={ICONS.HELP} />
// </ToolTip>
// </Fragment>
// );
}
return (
<section className="search">
{query && (
<Fragment>
<header className="search__header">
{isValid && (
<Fragment>
<SearchOptions />
<h1 className="media__uri">{uri}</h1>
{isChannel ? (
<ChannelTile size="large" isSearchResult uri={uri} />
) : (
<FileTile size="large" isSearchResult displayHiddenMessage uri={uri} />
)}
</Fragment>
)}
</header>
<div className="search__results-wrapper">
<FileListSearch query={query} />
<div className="card__content help">
{__('These search results are provided by LBRY, Inc.')}
</div>
</div>
</Fragment>
)}
</header>
)}
<div className="search__results-wrapper">
<SearchOptions />
<FileListSearch query={query} />
<div className="help">{__('These search results are provided by LBRY, Inc.')}</div>
</div>
</section>
</Page>
);
}
</section>
);
}}
</Location>
</Page>
);
}
export default SearchPage;

View file

@ -5,16 +5,23 @@ import {
makeSelectClaimForUri,
makeSelectIsUriResolving,
makeSelectTotalPagesForChannel,
buildURI,
} from 'lbry-redux';
import { selectBlackListedOutpoints } from 'lbryinc';
import ShowPage from './view';
const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
blackListedOutpoints: selectBlackListedOutpoints(state),
totalPages: makeSelectTotalPagesForChannel(props.uri, PAGE_SIZE)(state),
});
const select = (state, props) => {
// claimName and claimId come from the url `lbry.tv/{claimName}/{claimId}"
console.log('props', props);
const uri = buildURI({ contentName: props.claimName, claimId: props.claimId });
return {
claim: makeSelectClaimForUri(uri)(state),
isResolvingUri: makeSelectIsUriResolving(uri)(state),
blackListedOutpoints: selectBlackListedOutpoints(state),
totalPages: makeSelectTotalPagesForChannel(uri, PAGE_SIZE)(state),
uri: uri,
};
};
const perform = dispatch => ({
resolveUri: uri => dispatch(doResolveUri(uri)),

View file

@ -1,4 +1,5 @@
// @flow
import type { UrlLocation } from 'types/location';
import React from 'react';
import BusyIndicator from 'component/common/busy-indicator';
import ChannelPage from 'page/channel';
@ -13,6 +14,7 @@ type Props = {
uri: string,
claim: Claim,
totalPages: number,
location: UrlLocation,
blackListedOutpoints: Array<{
txid: string,
nout: number,
@ -38,8 +40,7 @@ class ShowPage extends React.PureComponent<Props> {
}
render() {
const { claim, isResolvingUri, uri, blackListedOutpoints } = this.props;
const { claim, isResolvingUri, uri, blackListedOutpoints, location } = this.props;
let innerContent = '';
if (!claim || (claim && !claim.name)) {
@ -58,7 +59,7 @@ class ShowPage extends React.PureComponent<Props> {
</Page>
);
} else if (claim.name.length && claim.name[0] === '@') {
innerContent = <ChannelPage uri={uri} />;
innerContent = <ChannelPage uri={uri} location={location} />;
} else if (claim && blackListedOutpoints) {
let isClaimBlackListed = false;
@ -89,7 +90,7 @@ class ShowPage extends React.PureComponent<Props> {
</Page>
);
} else {
innerContent = <FilePage uri={uri} />;
innerContent = <FilePage uri={uri} location={location} />;
}
}

View file

@ -13,6 +13,7 @@ import SuggestedSubscriptions from 'component/subscribeSuggested';
import MarkAsRead from 'component/subscribeMarkAsRead';
import Tooltip from 'component/common/tooltip';
import Yrbl from 'component/yrbl';
import { formatLbryUriForWeb } from 'util/uri';
type Props = {
viewMode: ViewMode,
@ -109,12 +110,7 @@ export default (props: Props) => {
return (
<span>
<h2 className="card__title card__title--flex">
<Button
button="link"
navigate="/show"
navigateParams={{ uri: channel, page: 1 }}
label={claimName}
/>
<Button button="link" navigate={channel} label={claimName} />
<MarkAsRead channel={channel} />
</h2>

View file

@ -6,6 +6,7 @@ import TransactionListRecent from 'component/transactionListRecent';
import WalletAddress from 'component/walletAddress';
import Page from 'component/page';
import UnsupportedOnWeb from 'component/common/unsupported-on-web';
import WalletSend from 'component/walletSend';
const WalletPage = () => (
<Page>
@ -15,6 +16,7 @@ const WalletPage = () => (
<WalletBalance />
<RewardSummary />
</div>
<WalletSend />
<WalletAddress />
<TransactionListRecent />
</div>

View file

@ -7,7 +7,7 @@ import * as MODALS from 'constants/modal_types';
import { ipcRenderer } from 'electron';
// @endif
import { doOpenModal } from 'redux/actions/app';
import { doNavigate } from 'redux/actions/navigation';
import { navigate } from '@reach/router';
import { setSubscriptionLatest, doUpdateUnreadSubscriptions } from 'redux/actions/subscriptions';
import { makeSelectUnreadByChannel } from 'redux/selectors/subscriptions';
import {
@ -24,10 +24,11 @@ import {
parseURI,
creditsToString,
doError,
makeSelectCostInfoForUri,
} from 'lbry-redux';
import { makeSelectCostInfoForUri } from 'lbryinc';
import { makeSelectClientSetting, selectosNotificationsEnabled } from 'redux/selectors/settings';
import analytics from 'analytics';
import { formatLbryUriForWeb } from 'util/uri';
const DOWNLOAD_POLL_INTERVAL = 250;
@ -82,11 +83,7 @@ export function doUpdateLoadStatus(uri: string, outpoint: string) {
silent: false,
});
notif.onclick = () => {
dispatch(
doNavigate('/show', {
uri,
})
);
navigate(formatLbryUriForWeb(uri));
};
}

View file

@ -10,7 +10,6 @@ import {
selectFileInfosByOutpoint,
} from 'lbry-redux';
import { doHideModal } from 'redux/actions/app';
import { doHistoryBack } from 'redux/actions/navigation';
export function doOpenFileInFolder(path) {
return () => {
@ -63,7 +62,6 @@ export function doDeleteFileAndGoBack(fileInfo, deleteFromComputer, abandonClaim
return dispatch => {
const actions = [];
actions.push(doHideModal());
actions.push(doHistoryBack());
actions.push(doDeleteFile(fileInfo, deleteFromComputer, abandonClaim));
dispatch(batchActions(...actions));
};

View file

@ -1,74 +0,0 @@
import { ACTIONS, selectHistoryIndex, selectHistoryStack } from 'lbry-redux';
import { toQueryString } from 'util/query-params';
import analytics from 'analytics';
export function doNavigate(path, params = {}, options = {}) {
return dispatch => {
if (!path) {
return;
}
// ensure uri always has "lbry://" prefix
const navigationParams = params;
if (path === '/show') {
if (navigationParams.uri && !navigationParams.uri.startsWith('lbry://')) {
navigationParams.uri = `lbry://${navigationParams.uri}`;
}
}
let url = path;
if (params && Object.values(params).length) {
url += `?${toQueryString(params)}`;
}
analytics.track('NAVIGATION', { destination: url });
const { scrollY } = options;
dispatch({
type: ACTIONS.HISTORY_NAVIGATE,
data: { url, index: options.index, scrollY },
});
};
}
export function doAuthNavigate(pathAfterAuth = null, params = {}) {
return dispatch => {
if (pathAfterAuth) {
dispatch({
type: ACTIONS.CHANGE_AFTER_AUTH_PATH,
data: {
path: `${pathAfterAuth}?${toQueryString(params)}`,
},
});
}
dispatch(doNavigate('/auth'));
};
}
export function doHistoryTraverse(dispatch, state, modifier) {
const stack = selectHistoryStack(state);
const index = selectHistoryIndex(state) + modifier;
if (index >= 0 && index < stack.length) {
const historyItem = stack[index];
dispatch(doNavigate(historyItem.path, {}, { scrollY: historyItem.scrollY, index }));
}
}
export function doHistoryBack() {
return (dispatch, getState) => doHistoryTraverse(dispatch, getState(), -1);
}
export function doHistoryForward() {
return (dispatch, getState) => doHistoryTraverse(dispatch, getState(), 1);
}
export function doRecordScroll(scroll) {
return dispatch => {
dispatch({
type: ACTIONS.WINDOW_SCROLLED,
data: { scrollY: scroll },
});
};
}

View file

@ -21,8 +21,9 @@ import {
} from 'lbry-redux';
import { doOpenModal } from 'redux/actions/app';
import { selectosNotificationsEnabled } from 'redux/selectors/settings';
import { doNavigate } from 'redux/actions/navigation';
import { navigate } from '@reach/router';
import analytics from 'analytics';
import { formatLbryUriForWeb } from 'util/uri';
// @if TARGET='app'
import fs from 'fs';
import path from 'path';
@ -127,12 +128,12 @@ export const doUploadThumbnail = (filePath: string, nsfw: boolean) => (dispatch:
.then(json =>
json.success
? dispatch({
type: ACTIONS.UPDATE_PUBLISH_FORM,
data: {
uploadThumbnailStatus: THUMBNAIL_STATUSES.COMPLETE,
thumbnail: `${json.data.url}${fileExt}`,
},
})
type: ACTIONS.UPDATE_PUBLISH_FORM,
data: {
uploadThumbnailStatus: THUMBNAIL_STATUSES.COMPLETE,
thumbnail: `${json.data.url}${fileExt}`,
},
})
: uploadError(json.message)
)
.catch(err => uploadError(err.message));
@ -327,11 +328,7 @@ export const doCheckPendingPublishes = () => (dispatch: Dispatch, getState: GetS
silent: false,
});
notif.onclick = () => {
dispatch(
doNavigate('/show', {
uri: claim.permanent_url,
})
);
navigate(formatLbryUriForWeb(claim.permanent_url));
};
}
}

View file

@ -100,21 +100,6 @@ export const selectDaemonVersionMatched = createSelector(
state => state.daemonVersionMatched
);
export const selectSnackBar = createSelector(
selectState,
state => state.snackBar || {}
);
export const selectSnackBarSnacks = createSelector(
selectSnackBar,
snackBar => snackBar.snacks || []
);
export const selectBadgeNumber = createSelector(
selectState,
state => state.badgeNumber
);
export const selectCurrentLanguage = createSelector(
selectState,
() => i18n.getLocale() || 'en'
@ -130,147 +115,6 @@ export const selectUpgradeTimer = createSelector(
state => state.checkUpgradeTimer
);
export const selectNavLinks = createSelector(
selectCurrentPage,
selectHistoryStack,
makeSelectClientSetting(SETTINGS.FIRST_RUN_COMPLETED),
makeSelectClientSetting(SETTINGS.INVITE_ACKNOWLEDGED),
(currentPage, historyStack, firstRunCompleted, inviteAcknowledged) => {
// Determine if any links should show a tooltip for a guided tour
// It will only show one at a time, in the order they are set.
const guidedTourItem = [
// @if TARGET='app'
{
page: PAGES.INVITE,
hasBeenCompleted: inviteAcknowledged,
guide: 'Check this out!',
},
// @endif
// Add more items below for tooltip guides that will happen after a user has completed the invite guide
].filter(({ hasBeenCompleted }) => !hasBeenCompleted)[0];
const isWalletPage = page =>
page === PAGES.WALLET ||
page === PAGES.SEND ||
page === PAGES.GET_CREDITS ||
page === PAGES.HISTORY ||
page === PAGES.BACKUP;
const isCurrentlyWalletPage = isWalletPage(currentPage);
const previousStack = historyStack.slice().reverse();
const getPreviousSubLinkPath = checkIfValidPage => {
for (let i = 0; i < previousStack.length; i += 1) {
const currentStackItem = previousStack[i];
// Trim off the "/" from the path
const pageInStack = currentStackItem.path.slice(1);
if (checkIfValidPage(pageInStack)) {
return currentStackItem.path;
}
}
return undefined;
};
// Gets the last active sublink in a section
const getActiveSublink = category => {
if (category === PAGES.WALLET) {
const previousPath = getPreviousSubLinkPath(isWalletPage);
return previousPath || `/${PAGES.WALLET}`;
}
return undefined;
};
// Is this path the first unacknowledged item in the guided tour list
const getGuideIfNecessary = page => {
if (!firstRunCompleted) {
return null;
}
return guidedTourItem && guidedTourItem.page === page ? guidedTourItem.guide : null;
};
const buildLink = (label, page) => ({
label,
path: `/${page}`,
active: currentPage === page,
guide: getGuideIfNecessary(page),
});
const walletSubLinks = [
{
...buildLink('Overview', PAGES.WALLET),
},
{
...buildLink('Send & Receive', PAGES.SEND),
},
{
...buildLink('Transactions', PAGES.HISTORY),
},
{
...buildLink('Get Credits', PAGES.GET_CREDITS),
},
{
...buildLink('Backup', PAGES.BACKUP),
},
];
const navLinks = {
primary: [
{
...buildLink('Explore', PAGES.DISCOVER),
icon: ICONS.HOME,
},
{
...buildLink('Subscriptions', PAGES.SUBSCRIPTIONS),
icon: ICONS.SUBSCRIPTION,
},
],
secondary: [
{
label: 'Wallet',
icon: ICONS.WALLET,
subLinks: walletSubLinks,
path: isCurrentlyWalletPage ? `/${PAGES.WALLET}` : getActiveSublink(PAGES.WALLET),
active: isWalletPage(currentPage),
},
{
...buildLink('Invite', PAGES.INVITE),
icon: ICONS.INVITE,
},
{
...buildLink('Rewards', PAGES.REWARDS),
// This probably shouldn't use the "FEATURED" icon, but not sure what else to use
icon: ICONS.FEATURED,
},
{
...buildLink('Downloads', PAGES.DOWNLOADED),
icon: ICONS.LOCAL,
},
{
...buildLink('Publishes', PAGES.PUBLISHED),
icon: ICONS.PUBLISHED,
},
{
...buildLink('History', PAGES.USER_HISTORY),
icon: ICONS.HISTORY,
},
{
...buildLink('Settings', PAGES.SETTINGS),
icon: ICONS.SETTINGS,
},
{
...buildLink('Help', PAGES.HELP),
icon: ICONS.HELP,
},
],
};
return navLinks;
}
);
export const selectModal = createSelector(
selectState,
state => {

View file

@ -3,7 +3,7 @@ import { createSelector } from 'reselect';
import {
makeSelectClaimForUri,
selectClaimsByUri,
makeSelectClaimsInChannelForCurrentPage,
makeSelectClaimsInChannelForCurrentPageState,
} from 'lbry-redux';
import { HISTORY_ITEMS_PER_PAGE } from 'constants/content';
@ -68,7 +68,7 @@ export const makeSelectHistoryForUri = (uri: string) =>
export const makeSelectCategoryListUris = (uris: ?Array<string>, channel: string) =>
createSelector(
makeSelectClaimsInChannelForCurrentPage(channel),
makeSelectClaimsInChannelForCurrentPageState(channel),
channelClaims => {
if (uris) return uris;

View file

@ -2,6 +2,7 @@
.button {
border-radius: 0;
font-size: 1em;
svg {
stroke-width: 1.9;

View file

@ -27,7 +27,12 @@
// at smaller screen widths
@media (min-width: 601px) {
// @if TARGET='app'
width: 250px;
// @endif
// @if TARGET='web'
width: 170px;
// @endif
}
@media (max-width: 600px) {
@ -53,6 +58,21 @@
&:disabled {
opacity: 0.3;
}
.button__content {
line-height: var(--header-height);
overflow: hidden;
text-align: center;
text-overflow: ellipsis;
width: 100%;
}
}
.header__navigation-item--lbry {
flex: 1;
font-weight: 800;
font-size: 1.2rem;
padding-left: var(--spacing-vertical-large);
}
.header__navigation-item--back {
@ -149,12 +169,4 @@
html[data-mode='dark'] & {
border-color: $lbry-gray-5;
}
.button__content {
line-height: var(--header-height);
overflow: hidden;
text-align: center;
text-overflow: ellipsis;
width: 100%;
}
}

View file

@ -139,6 +139,12 @@
margin-right: var(--spacing-vertical-small);
}
.media__uri {
font-size: 1.5rem;
padding-bottom: 5px;
opacity: 0.6;
}
// M E D I A
// A C T I O N S
@ -508,21 +514,19 @@
font-size: 2rem;
}
@media (max-width: 600px) {
font-size: 1.75rem;
}
.channel-info__actions__group {
button:first-of-type {
font-size: 2rem;
.button {
&:first-child {
font-size: 2rem;
.button__label {
margin: 0;
.button__label {
margin: 0;
}
}
}
button:last-of-type {
font-weight: normal;
&:last-child {
font-weight: normal;
}
}
}
}

View file

@ -52,6 +52,11 @@
position: relative;
}
.navigation__links--bottom {
position: absolute;
bottom: var(--spacing-vertical-large);
}
.navigation__link {
display: block;
line-height: 1.75;
@ -102,7 +107,7 @@
.navigation__link--title {
margin-bottom: var(--spacing-vertical-small);
padding-top: var(--spacing-vertical-medium);
padding-top: var(--spacing-vertical-large);
color: $lbry-gray-5;
cursor: default;

View file

@ -53,6 +53,7 @@
.search__options-wrapper {
font-size: 1.25em;
padding-bottom: var(--spacing-vertical-large);
}
.search__options {

View file

@ -8,7 +8,7 @@
}
.yrbl {
height: 300px;
height: 20rem;
margin-right: var(--spacing-vertical-large);
}

18
src/ui/types/location.js Normal file
View file

@ -0,0 +1,18 @@
// @flow
export type UrlLocation = {
search: string,
hash: string,
host: string,
hostname: string,
href: string,
key: string,
origin: string,
pathname: string,
port: string,
protocol: string,
reload: () => void,
replace: string => void,
search: string,
state: {},
};

13
src/ui/util/uri.js Normal file
View file

@ -0,0 +1,13 @@
// @flow
import { parseURI } from 'lbry-redux';
export const formatLbryUriForWeb = (uri: string) => {
const { claimName, claimId } = parseURI(uri);
let webUrl = `/${claimName}`;
if (claimId) {
webUrl += `/${claimId}`;
}
return webUrl;
};

View file

@ -2,6 +2,7 @@
<html>
<head>
<meta charset="utf-8" />
<title>lbry.tv</title>
</head>
<body>

View file

@ -7,6 +7,6 @@
<body>
<div id="app"></div>
<script type="text/javascript" src="ui.js"></script>
<script type="text/javascript" src="/ui.js"></script>
</body>
</html>

View file

@ -38,7 +38,7 @@ let baseConfig = {
use: {
loader: 'file-loader',
options: {
outputPath: 'ui/img',
outputPath: '$/static/img',
name: '[name].[ext]',
},
},
@ -48,7 +48,7 @@ let baseConfig = {
use: {
loader: 'file-loader',
options: {
outputPath: 'ui/font',
outputPath: '$/static/font',
},
},
},

View file

@ -4,9 +4,13 @@ const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.config.js');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { DefinePlugin } = require('webpack');
const { getIfUtils, removeEmpty } = require('webpack-config-utils');
const STATIC_ROOT = path.resolve(__dirname, 'static/');
const DIST_ROOT = path.resolve(__dirname, 'dist/');
const NODE_ENV = process.env.NODE_ENV || 'development';
const { ifProduction } = getIfUtils(NODE_ENV);
let mainConfig = {
target: 'electron-main',
@ -40,7 +44,11 @@ let mainConfig = {
{
from: `${STATIC_ROOT}/`,
to: `${DIST_ROOT}/electron/static/`,
ignore: ['font/**/*'],
ignore: ['font/**/*', 'index.dev.html', 'index.html'],
},
{
from: ifProduction(`${STATIC_ROOT}/index.html`, `${STATIC_ROOT}/index.dev.html`),
to: `${DIST_ROOT}/electron/static/index.html`,
},
]),
],

103
yarn.lock
View file

@ -850,6 +850,17 @@
resolved "https://registry.yarnpkg.com/@posthtml/esm/-/esm-1.0.0.tgz#09bcb28a02438dcee22ad1970ca1d85a000ae0cf"
integrity sha512-dEVG+ITnvqKGa4v040tP+n8LOKOqr94qjLva7bE5pnfm2KHJwsKz69J4KMxgWLznbpBJzy8vQfCayEk3vLZnZQ==
"@reach/router@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@reach/router/-/router-1.2.1.tgz#34ae3541a5ac44fa7796e5506a5d7274a162be4e"
integrity sha512-kTaX08X4g27tzIFQGRukaHmNbtMYDS3LEWIS8+l6OayGIw6Oyo1HIF/JzeuR2FoF9z6oV+x/wJSVSq4v8tcUGQ==
dependencies:
create-react-context "^0.2.1"
invariant "^2.2.3"
prop-types "^15.6.1"
react-lifecycles-compat "^3.0.4"
warning "^3.0.0"
"@samverschueren/stream-to-observable@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
@ -1340,6 +1351,11 @@ array-unique@^0.3.2:
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
asap@~2.0.3:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
asn1.js@^4.0.0:
version "4.10.1"
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
@ -2584,6 +2600,11 @@ copy-webpack-plugin@^4.6.0:
p-limit "^1.0.0"
serialize-javascript "^1.4.0"
core-js@^1.0.0:
version "1.2.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7:
version "2.6.5"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895"
@ -2651,6 +2672,14 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
create-react-context@^0.2.1:
version "0.2.3"
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.3.tgz#9ec140a6914a22ef04b8b09b7771de89567cb6f3"
integrity sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==
dependencies:
fbjs "^0.8.0"
gud "^1.0.0"
crocket@^0.9.11:
version "0.9.11"
resolved "https://registry.yarnpkg.com/crocket/-/crocket-0.9.11.tgz#288fca11ef0d3dd239b62c488265f30c8edfb0c5"
@ -3589,6 +3618,13 @@ encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
encoding@^0.1.11:
version "0.1.12"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=
dependencies:
iconv-lite "~0.4.13"
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
@ -4239,6 +4275,19 @@ faye-websocket@~0.11.1:
dependencies:
websocket-driver ">=0.5.1"
fbjs@^0.8.0:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
dependencies:
core-js "^1.0.0"
isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
fd-slicer@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
@ -4854,6 +4903,11 @@ graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
gud@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==
handle-thing@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754"
@ -5204,7 +5258,7 @@ iconv-lite@0.4.23:
dependencies:
safer-buffer ">= 2.1.2 < 3"
iconv-lite@^0.4.24, iconv-lite@^0.4.4:
iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@ -5386,7 +5440,7 @@ interpret@^1.0.0, interpret@^1.1.0:
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296"
integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==
invariant@^2.2.2, invariant@^2.2.4:
invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
@ -5734,7 +5788,7 @@ is-retry-allowed@^1.0.0:
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
integrity sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=
is-stream@^1.0.0, is-stream@^1.1.0:
is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
@ -5829,6 +5883,14 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
isomorphic-fetch@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=
dependencies:
node-fetch "^1.0.1"
whatwg-fetch ">=0.10.0"
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@ -6066,17 +6128,17 @@ lazy-val@^1.0.3, lazy-val@^1.0.4:
tar-stream "^1.6.2"
zstd-codec "^0.1.1"
lbry-redux@lbryio/lbry-redux#2a7e05940f892f104428eeb37bd1f178da811a09:
lbry-redux@lbryio/lbry-redux#86f1340f834d0f5cd5365492a8ff15d4b213a050:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/2a7e05940f892f104428eeb37bd1f178da811a09"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/86f1340f834d0f5cd5365492a8ff15d4b213a050"
dependencies:
proxy-polyfill "0.1.6"
reselect "^3.0.0"
uuid "^3.3.2"
lbryinc@lbryio/lbryinc#437260f3dad2321e892388b68749ee6af01aff2b:
lbryinc@lbryio/lbryinc#d9f9035113c8b9ab3b0ee7ffbd38f910086a665e:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/437260f3dad2321e892388b68749ee6af01aff2b"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/d9f9035113c8b9ab3b0ee7ffbd38f910086a665e"
dependencies:
bluebird "^3.5.1"
reselect "^3.0.0"
@ -7006,6 +7068,14 @@ node-emoji@^1.8.1:
dependencies:
lodash.toarray "^4.4.0"
node-fetch@^1.0.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
dependencies:
encoding "^0.1.11"
is-stream "^1.0.1"
node-fetch@^2.1.1, node-fetch@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5"
@ -8253,6 +8323,13 @@ promise-inflight@^1.0.1:
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
promise@^7.1.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
dependencies:
asap "~2.0.3"
prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
@ -9350,7 +9427,7 @@ set-value@^2.0.0:
is-plain-object "^2.0.3"
split-string "^3.0.1"
setimmediate@^1.0.4, setimmediate@~1.0.4:
setimmediate@^1.0.4, setimmediate@^1.0.5, setimmediate@~1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
@ -10342,6 +10419,11 @@ typo-js@*:
resolved "https://registry.yarnpkg.com/typo-js/-/typo-js-1.0.3.tgz#54d8ebc7949f1a7810908b6002c6841526c99d5a"
integrity sha1-VNjrx5SfGngQkItgAsaEFSbJnVo=
ua-parser-js@^0.7.18:
version "0.7.19"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
integrity sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==
uglify-js@3.4.x:
version "3.4.9"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
@ -10997,6 +11079,11 @@ websocket-extensions@>=0.1.1:
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==
whatwg-fetch@>=0.10.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==
whet.extend@~0.9.9:
version "0.9.9"
resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"