Fix everything I broke (I hope) - Use react-router #2408

Merged
neb-b merged 1 commit from routing-fixes into master 2019-04-04 23:14:05 +02:00
39 changed files with 352 additions and 350 deletions

View file

@ -59,7 +59,6 @@
"@hot-loader/react-dom": "16.8", "@hot-loader/react-dom": "16.8",
"@lbry/color": "^1.0.2", "@lbry/color": "^1.0.2",
"@lbry/components": "^2.3.3", "@lbry/components": "^2.3.3",
"@reach/router": "^1.2.1",
"@types/three": "^0.93.1", "@types/three": "^0.93.1",
"async-exit-hook": "^2.0.1", "async-exit-hook": "^2.0.1",
"babel-eslint": "^10.0.1", "babel-eslint": "^10.0.1",
@ -70,6 +69,7 @@
"chalk": "^2.4.2", "chalk": "^2.4.2",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"codemirror": "^5.39.2", "codemirror": "^5.39.2",
"connected-react-router": "^6.3.2",
"copy-webpack-plugin": "^4.6.0", "copy-webpack-plugin": "^4.6.0",
"country-data": "^0.0.31", "country-data": "^0.0.31",
"cross-env": "^5.2.0", "cross-env": "^5.2.0",
@ -104,6 +104,7 @@
"flow-typed": "^2.3.0", "flow-typed": "^2.3.0",
"formik": "^0.10.4", "formik": "^0.10.4",
"hast-util-sanitize": "^1.1.2", "hast-util-sanitize": "^1.1.2",
"history": "^4.9.0",
"husky": "^0.14.3", "husky": "^0.14.3",
"json-loader": "^0.5.4", "json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git", "lbry-format": "https://github.com/lbryio/lbry-format.git",
@ -134,7 +135,8 @@
"react-modal": "^3.1.7", "react-modal": "^3.1.7",
"react-paginate": "^5.2.1", "react-paginate": "^5.2.1",
"react-pose": "^4.0.5", "react-pose": "^4.0.5",
"react-redux": "^5.0.3", "react-redux": "^6.0.1",
"react-router-dom": "^5.0.0",
"react-simplemde-editor": "^4.0.0", "react-simplemde-editor": "^4.0.0",
"react-toggle": "^4.0.2", "react-toggle": "^4.0.2",
"react-virtualized": "^9.21.0", "react-virtualized": "^9.21.0",

View file

@ -1,7 +1,6 @@
// @flow // @flow
import { Lbryio } from 'lbryinc'; import { Lbryio } from 'lbryinc';
import ReactGA from 'react-ga'; import ReactGA from 'react-ga';
import { globalHistory } from '@reach/router';
neb-b commented 2019-04-04 22:41:39 +02:00 (Migrated from github.com)
Review

Will add back analytics stuff in another PR. They were busted anyway. Just want to get this in.

Will add back analytics stuff in another PR. They were busted anyway. Just want to get this in.
type Analytics = { type Analytics = {
pageView: string => void, pageView: string => void,
@ -15,7 +14,7 @@ let analyticsEnabled: boolean = true;
const analytics: Analytics = { const analytics: Analytics = {
pageView: path => { pageView: path => {
if (analyticsEnabled) { if (analyticsEnabled) {
ReactGA.pageview(path); // ReactGA.pageview(path);
} }
}, },
setUser: user => { setUser: user => {
@ -72,19 +71,19 @@ const analytics: Analytics = {
// Initialize google analytics // Initialize google analytics
// Set `debug: true` for debug info // Set `debug: true` for debug info
ReactGA.initialize('UA-60403362-12', { // ReactGA.initialize('UA-60403362-12', {
gaOptions: { name: IS_WEB ? 'web' : 'desktop' }, // gaOptions: { name: IS_WEB ? 'web' : 'desktop' },
testMode: process.env.NODE_ENV !== 'production', // testMode: process.env.NODE_ENV !== 'production',
}); // });
// Manually call the first page view // Manually call the first page view
// Reach Router doesn't include this on `history.listen` // Reach Router doesn't include this on `history.listen`
analytics.pageView(window.location.pathname + window.location.search); // analytics.pageView(window.location.pathname + window.location.search);
// Listen for url changes and report // Listen for url changes and report
// This will include search queries/filter options // This will include search queries/filter options
globalHistory.listen(({ location }) => // globalHistory.listen(({ location }) =>
analytics.pageView(window.location.pathname + window.location.search) // analytics.pageView(window.location.pathname + window.location.search)
); // );
export default analytics; export default analytics;

View file

@ -1,4 +1,4 @@
import store from 'store'; import { store } from 'store';
const env = process.env.NODE_ENV || 'production'; const env = process.env.NODE_ENV || 'production';

View file

@ -3,7 +3,6 @@ import React from 'react';
import Router from 'component/router/index'; import Router from 'component/router/index';
import ModalRouter from 'modal/modalRouter'; import ModalRouter from 'modal/modalRouter';
import ReactModal from 'react-modal'; import ReactModal from 'react-modal';
import throttle from 'util/throttle';
import SideBar from 'component/sideBar'; import SideBar from 'component/sideBar';
import Header from 'component/header'; import Header from 'component/header';
import { openContextMenu } from 'util/context-menu'; import { openContextMenu } from 'util/context-menu';

View file

@ -2,7 +2,7 @@
import * as React from 'react'; import * as React from 'react';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
import classnames from 'classnames'; import classnames from 'classnames';
import { Link } from '@reach/router'; import { NavLink } from 'react-router-dom';
import { formatLbryUriForWeb } from 'util/uri'; import { formatLbryUriForWeb } from 'util/uri';
import { OutboundLink } from 'react-ga'; import { OutboundLink } from 'react-ga';
@ -109,19 +109,16 @@ class Button extends React.PureComponent<Props> {
} }
return path ? ( return path ? (
<Link <NavLink
exact
to={path} to={path}
title={title} title={title}
onClick={e => e.stopPropagation()} onClick={e => e.stopPropagation()}
getProps={({ isCurrent, isPartiallyCurrent }) => ({ className={combinedClassName}
className: activeClassName={activeClass}
(path === '/' ? isCurrent : isPartiallyCurrent) && activeClass
? `${combinedClassName} ${activeClass}`
: combinedClassName,
})}
> >
{content} {content}
</Link> </NavLink>
) : ( ) : (
<button <button
title={title} title={title}

View file

@ -5,7 +5,7 @@ import TruncatedText from 'component/common/truncated-text';
import classnames from 'classnames'; import classnames from 'classnames';
import SubscribeButton from 'component/subscribeButton'; import SubscribeButton from 'component/subscribeButton';
import type { Claim } from 'types/claim'; import type { Claim } from 'types/claim';
import { navigate } from '@reach/router'; import { withRouter } from 'react-router-dom';
import { formatLbryUriForWeb } from 'util/uri'; import { formatLbryUriForWeb } from 'util/uri';
type Props = { type Props = {
@ -15,6 +15,7 @@ type Props = {
size: string, size: string,
claim: ?Claim, claim: ?Claim,
resolveUri: string => void, resolveUri: string => void,
history: { push: string => void },
}; };
class ChannelTile extends React.PureComponent<Props> { class ChannelTile extends React.PureComponent<Props> {
@ -37,7 +38,7 @@ class ChannelTile extends React.PureComponent<Props> {
} }
render() { render() {
const { claim, isResolvingUri, totalItems, uri, size } = this.props; const { claim, isResolvingUri, totalItems, uri, size, history } = this.props;
let channelName; let channelName;
let subscriptionUri; let subscriptionUri;
@ -46,7 +47,7 @@ class ChannelTile extends React.PureComponent<Props> {
subscriptionUri = `lbry://${claim.permanent_url}`; subscriptionUri = `lbry://${claim.permanent_url}`;
} }
const onClick = () => navigate(formatLbryUriForWeb(uri)); const onClick = () => history.push(formatLbryUriForWeb(uri));
return ( return (
<section <section
@ -86,4 +87,4 @@ class ChannelTile extends React.PureComponent<Props> {
} }
} }
export default ChannelTile; export default withRouter(ChannelTile);

View file

@ -4,13 +4,11 @@ import * as ICONS from 'constants/icons';
import * as React from 'react'; import * as React from 'react';
import { isURIValid } from 'lbry-redux'; import { isURIValid } from 'lbry-redux';
import Button from 'component/button'; import Button from 'component/button';
import { navigate } from '@reach/router';
type Props = { type Props = {
href: string, href: string,
title?: string, title?: string,
children: React.Node, children: React.Node,
navigate: (string, ?{}) => void,
openModal: (id: string, { uri: string }) => void, openModal: (id: string, { uri: string }) => void,
}; };
@ -21,7 +19,7 @@ class ExternalLink extends React.PureComponent<Props> {
}; };
createLink() { createLink() {
const { href, title, children, openModal, navigate } = this.props; const { href, title, children, openModal } = this.props;
// Regex for url protocol // Regex for url protocol
const protocolRegex = new RegExp('^(https?|lbry)+:', 'i'); const protocolRegex = new RegExp('^(https?|lbry)+:', 'i');

View file

@ -11,7 +11,7 @@ import classnames from 'classnames';
import FilePrice from 'component/filePrice'; import FilePrice from 'component/filePrice';
import { openCopyLinkMenu } from 'util/context-menu'; import { openCopyLinkMenu } from 'util/context-menu';
import DateTime from 'component/dateTime'; import DateTime from 'component/dateTime';
import { navigate } from '@reach/router'; import { withRouter } from 'react-router-dom';
import { formatLbryUriForWeb } from 'util/uri'; import { formatLbryUriForWeb } from 'util/uri';
type Props = { type Props = {
@ -29,6 +29,7 @@ type Props = {
isNew: boolean, isNew: boolean,
placeholder: boolean, placeholder: boolean,
preventResolve: boolean, preventResolve: boolean,
history: { push: string => void },
}; };
class FileCard extends React.PureComponent<Props> { class FileCard extends React.PureComponent<Props> {
@ -70,6 +71,7 @@ class FileCard extends React.PureComponent<Props> {
isNew, isNew,
isResolvingUri, isResolvingUri,
placeholder, placeholder,
history,
} = this.props; } = this.props;
const abandoned = !isResolvingUri && !claim && !pending && !placeholder; const abandoned = !isResolvingUri && !claim && !pending && !placeholder;
@ -110,7 +112,7 @@ class FileCard extends React.PureComponent<Props> {
const onClick = e => { const onClick = e => {
e.stopPropagation(); e.stopPropagation();
navigate(formatLbryUriForWeb(uri)); history.push(formatLbryUriForWeb(uri));
}; };
return ( return (
@ -147,4 +149,4 @@ class FileCard extends React.PureComponent<Props> {
} }
} }
export default FileCard; export default withRouter(FileCard);

View file

@ -12,7 +12,7 @@ import FilePrice from 'component/filePrice';
import UriIndicator from 'component/uriIndicator'; import UriIndicator from 'component/uriIndicator';
import DateTime from 'component/dateTime'; import DateTime from 'component/dateTime';
import Yrbl from 'component/yrbl'; import Yrbl from 'component/yrbl';
import { navigate } from '@reach/router'; import { withRouter } from 'react-router-dom';
import { formatLbryUriForWeb } from 'util/uri'; import { formatLbryUriForWeb } from 'util/uri';
type Props = { type Props = {
@ -32,6 +32,7 @@ type Props = {
size: string, size: string,
isSubscribed: boolean, isSubscribed: boolean,
isNew: boolean, isNew: boolean,
history: { push: string => void },
}; };
class FileTile extends React.PureComponent<Props> { class FileTile extends React.PureComponent<Props> {
@ -91,6 +92,7 @@ class FileTile extends React.PureComponent<Props> {
hideNoResult, hideNoResult,
displayHiddenMessage, displayHiddenMessage,
size, size,
history,
} = this.props; } = this.props;
if (!claim && isResolvingUri) { if (!claim && isResolvingUri) {
@ -136,7 +138,7 @@ class FileTile extends React.PureComponent<Props> {
const wrapperProps = name const wrapperProps = name
? { ? {
onClick: () => navigate(formatLbryUriForWeb(uri)), onClick: () => history.push(formatLbryUriForWeb(uri)),
role: 'button', role: 'button',
} }
: {}; : {};
@ -209,7 +211,7 @@ class FileTile extends React.PureComponent<Props> {
clearPublish(); // to remove any existing publish data clearPublish(); // to remove any existing publish data
updatePublishForm({ name: claimName }); // to populate the name updatePublishForm({ name: claimName }); // to populate the name
navigate('/publish'); history.push('/publish');
}} }}
/> />
} }
@ -221,4 +223,4 @@ class FileTile extends React.PureComponent<Props> {
} }
} }
export default FileTile; export default withRouter(FileTile);

View file

@ -88,7 +88,7 @@ const Header = (props: Props) => {
{roundedBalance} <LbcSymbol /> {roundedBalance} <LbcSymbol />
</React.Fragment> </React.Fragment>
} }
navigate="/$/wallet" navigate="/$/account"
/> />
<Button <Button

View file

@ -11,7 +11,7 @@ const select = (state, props) => {
return { return {
page, page,
pageCount: selectHistoryPageCount(state), pageCount: selectHistoryPageCount(state),
history: makeSelectHistoryForPage(page)(state), historyItems: makeSelectHistoryForPage(page)(state),
}; };
}; };

View file

@ -4,7 +4,7 @@ import Button from 'component/button';
import { Form, FormField } from 'component/common/form'; import { Form, FormField } from 'component/common/form';
import ReactPaginate from 'react-paginate'; import ReactPaginate from 'react-paginate';
import NavigationHistoryItem from 'component/navigationHistoryItem'; import NavigationHistoryItem from 'component/navigationHistoryItem';
import { navigate } from '@reach/router'; import { withRouter } from 'react-router-dom';
type HistoryItem = { type HistoryItem = {
uri: string, uri: string,
@ -12,11 +12,12 @@ type HistoryItem = {
}; };
type Props = { type Props = {
history: Array<HistoryItem>, historyItems: Array<HistoryItem>,
page: number, page: number,
pageCount: number, pageCount: number,
clearHistoryUri: string => void, clearHistoryUri: string => void,
params: { page: number }, params: { page: number },
history: { push: string => void },
}; };
type State = { type State = {
@ -52,7 +53,8 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
} }
changePage(pageNumber: number) { changePage(pageNumber: number) {
navigate(`?page=${pageNumber}`); const { history } = this.props;
history.push(`?page=${pageNumber}`);
} }
paginate(e: SyntheticKeyboardEvent<*>) { paginate(e: SyntheticKeyboardEvent<*>) {
@ -69,9 +71,9 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
} }
selectAll() { selectAll() {
const { history } = this.props; const { historyItems } = this.props;
const newSelectedState = {}; const newSelectedState = {};
history.forEach(({ uri }) => (newSelectedState[uri] = true)); historyItems.forEach(({ uri }) => (newSelectedState[uri] = true));
this.setState({ itemsSelected: newSelectedState }); this.setState({ itemsSelected: newSelectedState });
} }
@ -92,7 +94,7 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
} }
render() { render() {
const { history = [], page, pageCount } = this.props; const { historyItems = [], page, pageCount } = this.props;
const { itemsSelected } = this.state; const { itemsSelected } = this.state;
const allSelected = Object.keys(itemsSelected).length === history.length; const allSelected = Object.keys(itemsSelected).length === history.length;
const selectHandler = allSelected ? this.unselectAll : this.selectAll; const selectHandler = allSelected ? this.unselectAll : this.selectAll;
@ -113,9 +115,9 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
onClick={selectHandler} onClick={selectHandler}
/> />
</div> </div>
{!!history.length && ( {!!historyItems.length && (
<section className="card__content item-list"> <section className="card__content item-list">
{history.map(item => ( {historyItems.map(item => (
<NavigationHistoryItem <NavigationHistoryItem
key={item.uri} key={item.uri}
uri={item.uri} uri={item.uri}
@ -182,4 +184,4 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
); );
} }
} }
export default UserHistoryPage; export default withRouter(UserHistoryPage);

View file

@ -5,7 +5,7 @@ import moment from 'moment';
import classnames from 'classnames'; import classnames from 'classnames';
import Button from 'component/button'; import Button from 'component/button';
import { FormField } from 'component/common/form'; import { FormField } from 'component/common/form';
import { navigate } from '@reach/router'; import { withRouter } from 'react-router-dom';
import { formatLbryUriForWeb } from 'util/uri'; import { formatLbryUriForWeb } from 'util/uri';
type Props = { type Props = {
@ -16,6 +16,7 @@ type Props = {
onSelect?: () => void, onSelect?: () => void,
resolveUri: string => void, resolveUri: string => void,
slim: boolean, slim: boolean,
history: { push: string => void },
}; };
class NavigationHistoryItem extends React.PureComponent<Props> { class NavigationHistoryItem extends React.PureComponent<Props> {
@ -32,7 +33,7 @@ class NavigationHistoryItem extends React.PureComponent<Props> {
} }
render() { render() {
const { lastViewed, selected, onSelect, claim, uri, slim } = this.props; const { lastViewed, selected, onSelect, claim, uri, slim, history } = this.props;
let name; let name;
let title; let title;
@ -45,7 +46,7 @@ class NavigationHistoryItem extends React.PureComponent<Props> {
const onClick = const onClick =
onSelect || onSelect ||
function() { function() {
navigate(navigatePath); history.push(navigatePath);
}; };
return ( return (
@ -65,4 +66,4 @@ class NavigationHistoryItem extends React.PureComponent<Props> {
} }
} }
export default NavigationHistoryItem; export default withRouter(NavigationHistoryItem);

View file

@ -4,7 +4,6 @@ import Button from 'component/button';
import { Form, FormField } from 'component/common/form'; import { Form, FormField } from 'component/common/form';
import ReactPaginate from 'react-paginate'; import ReactPaginate from 'react-paginate';
import NavigationHistoryItem from 'component/navigationHistoryItem'; import NavigationHistoryItem from 'component/navigationHistoryItem';
import { navigate } from '@reach/router';
type HistoryItem = { type HistoryItem = {
uri: string, uri: string,

View file

@ -1,6 +1,6 @@
import * as PAGES from 'constants/pages'; import * as PAGES from 'constants/pages';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { Router } from '@reach/router'; import { Route, Switch } from 'react-router-dom';
import SettingsPage from 'page/settings'; import SettingsPage from 'page/settings';
import HelpPage from 'page/help'; import HelpPage from 'page/help';
import ReportPage from 'page/report'; import ReportPage from 'page/report';
@ -21,49 +21,32 @@ import UserHistoryPage from 'page/userHistory';
import SendCreditsPage from 'page/sendCredits'; import SendCreditsPage from 'page/sendCredits';
import NavigationHistory from 'page/navigationHistory'; import NavigationHistory from 'page/navigationHistory';
const ScrollHandler = props => {
const { key } = props.location;
useEffect(() => {
// This shouldn't scroll to top when you click "back"
// Might take some more work but fixes scroll position being stuck on navigation for now
const main = document.querySelector('main');
if (main) {
main.scrollIntoView();
}
}, [key]);
return props.children;
};
export default function AppRouter() { export default function AppRouter() {
return ( return (
<Router> <Switch>
<ScrollHandler path="/"> <Route path="/" exact component={DiscoverPage} />
<DiscoverPage path="/" /> <Route path={`/$/${PAGES.AUTH}`} exact component={AuthPage} />
<ShowPage path="/:claimName/:claimId" /> <Route path={`/$/${PAGES.BACKUP}`} exact component={BackupPage} />
<ShowPage path="/:claimName" /> <Route path={`/$/${PAGES.INVITE}`} exact component={InvitePage} />
{/* <ShowPage path="/" uri="five" /> */} <Route path={`/$/${PAGES.DOWNLOADED}`} exact component={FileListDownloaded} />
<Route path={`/$/${PAGES.PUBLISHED}`} exact component={FileListPublished} />
neb-b commented 2019-04-04 22:43:51 +02:00 (Migrated from github.com)
Review

Will add this back later, it will be a little different with react-router

Will add this back later, it will be a little different with react-router
<Route path={`/$/${PAGES.HELP}`} exact component={HelpPage} />
<Route path={`/$/${PAGES.PUBLISH}`} exact component={PublishPage} />
<Route path={`/$/${PAGES.REPORT}`} exact component={ReportPage} />
<Route path={`/$/${PAGES.REWARDS}`} exact component={RewardsPage} />
<Route path={`/$/${PAGES.SEARCH}`} exact component={SearchPage} />
<Route path={`/$/${PAGES.SETTINGS}`} exact component={SettingsPage} />
<Route path={`/$/${PAGES.SUBSCRIPTIONS}`} exact component={SubscriptionsPage} />
<Route path={`/$/${PAGES.TRANSACTIONS}`} exact component={TransactionHistoryPage} />
<Route path={`/$/${PAGES.HISTORY}`} exact component={UserHistoryPage} />
<Route path={`/$/${PAGES.ACCOUNT}`} exact component={AccountPage} />
<Route path={`/$/${PAGES.SEND}`} exact component={SendCreditsPage} />
<Route path={`/$/${PAGES.HISTORY}`} exact component={UserHistoryPage} />
<Route path={`/$/${PAGES.HISTORY}/all`} exact component={NavigationHistory} />
<AuthPage path={`$/${PAGES.AUTH}`} /> {/* Below need to go at the end to make sure we don't match any of our pages first */}
<BackupPage path={`$/${PAGES.BACKUP}`} /> <Route path="/:claimName/:claimId" component={ShowPage} />
<InvitePage path={`$/${PAGES.INVITE}`} /> <Route path="/:claimName" component={ShowPage} />
<FileListDownloaded path={`$/${PAGES.DOWNLOADED}`} /> </Switch>
<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.TRANSACTIONS}`} />
<UserHistoryPage path={`$/${PAGES.HISTORY}`} />
<AccountPage path={`$/${PAGES.ACCOUNT}`} />
<SendCreditsPage path={`$/${PAGES.SEND}`} />
<UserHistoryPage path={`$/${PAGES.HISTORY}`} />
<NavigationHistory path={`$/${PAGES.HISTORY}/all`} />
</ScrollHandler>
</Router>
); );
} }

View file

@ -11,7 +11,7 @@ import {
} from 'lbry-redux'; } from 'lbry-redux';
import analytics from 'analytics'; import analytics from 'analytics';
import Wunderbar from './view'; import Wunderbar from './view';
import { navigate } from '@reach/router'; import { withRouter } from 'react-router-dom';
import { formatLbryUriForWeb } from 'util/uri'; import { formatLbryUriForWeb } from 'util/uri';
const select = state => ({ const select = state => ({
@ -20,14 +20,14 @@ const select = state => ({
isFocused: selectSearchBarFocused(state), isFocused: selectSearchBarFocused(state),
}); });
const perform = dispatch => ({ const perform = (dispatch, ownProps) => ({
onSearch: query => { onSearch: query => {
navigate(`/$/search?q=${query}`); ownProps.history.push({ pathname: `/$/search`, search: `?q=${query}` });
analytics.apiLogSearch(); analytics.apiLogSearch();
}, },
onSubmit: uri => { onSubmit: uri => {
const path = formatLbryUriForWeb(uri); const path = formatLbryUriForWeb(uri);
navigate(path); ownProps.history.push(path);
dispatch(doUpdateSearchQuery('')); dispatch(doUpdateSearchQuery(''));
}, },
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)), updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),
@ -35,7 +35,9 @@ const perform = dispatch => ({
doBlur: () => dispatch(doBlurSearchInput()), doBlur: () => dispatch(doBlurSearchInput()),
}); });
export default connect( export default withRouter(
select, connect(
perform select,
)(Wunderbar); perform
)(Wunderbar)
);

View file

@ -7,7 +7,6 @@ import { normalizeURI, SEARCH_TYPES, isURIValid, buildURI } from 'lbry-redux';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
import { parseQueryParams } from 'util/query-params'; import { parseQueryParams } from 'util/query-params';
import Autocomplete from './internal/autocomplete'; import Autocomplete from './internal/autocomplete';
import { Location, navigate } from '@reach/router';
const L_KEY_CODE = 76; const L_KEY_CODE = 76;
const ESC_KEY_CODE = 27; const ESC_KEY_CODE = 27;

View file

@ -22,11 +22,12 @@ import { Lbry, doToast, isURIValid, setSearchApi } from 'lbry-redux';
import { doDownloadLanguages, doUpdateIsNightAsync } from 'redux/actions/settings'; import { doDownloadLanguages, doUpdateIsNightAsync } from 'redux/actions/settings';
import { doAuthenticate, Lbryio, rewards, doBlackListedOutpointsSubscribe } from 'lbryinc'; import { doAuthenticate, Lbryio, rewards, doBlackListedOutpointsSubscribe } from 'lbryinc';
import 'scss/all.scss'; import 'scss/all.scss';
import store from 'store'; import { store, history } from 'store';
import pjson from 'package.json'; import pjson from 'package.json';
import app from './app'; import app from './app';
import analytics from './analytics'; import analytics from './analytics';
import doLogWarningConsoleMessage from './logWarningConsoleMessage'; import doLogWarningConsoleMessage from './logWarningConsoleMessage';
import { ConnectedRouter } from 'connected-react-router';
const APPPAGEURL = 'lbry://?'; const APPPAGEURL = 'lbry://?';
@ -219,10 +220,12 @@ const init = () => {
app.store.dispatch(doDaemonReady()); app.store.dispatch(doDaemonReady());
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <Provider store={store}>
<ErrorBoundary> <ConnectedRouter history={history}>
<App /> <ErrorBoundary>
<SnackBar /> <App />
</ErrorBoundary> <SnackBar />
</ErrorBoundary>
</ConnectedRouter>
</Provider>, </Provider>,
document.getElementById('app') document.getElementById('app')
); );

View file

@ -1,7 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { doHideModal } from 'redux/actions/app'; import { doHideModal } from 'redux/actions/app';
import { selectPhoneToVerify, selectUser } from 'lbryinc'; import { selectPhoneToVerify, selectUser } from 'lbryinc';
import { navigate } from '@reach/router';
import ModalPhoneCollection from './view'; import ModalPhoneCollection from './view';
const select = state => ({ const select = state => ({
@ -10,10 +9,7 @@ const select = state => ({
}); });
const perform = dispatch => () => ({ const perform = dispatch => () => ({
closeModal: () => { closeModal: () => dispatch(doHideModal()),
dispatch(doHideModal());
navigate('/$/rewards');
},
}); });
export default connect( export default connect(

View file

@ -4,6 +4,7 @@ import { Modal } from 'modal/modal';
import Button from 'component/button'; import Button from 'component/button';
import UserPhoneNew from 'component/userPhoneNew'; import UserPhoneNew from 'component/userPhoneNew';
import UserPhoneVerify from 'component/userPhoneVerify'; import UserPhoneVerify from 'component/userPhoneVerify';
import { withRouter } from 'react-router-dom';
type Props = { type Props = {
phone: ?number, phone: ?number,
@ -11,6 +12,7 @@ type Props = {
phone_number: ?number, phone_number: ?number,
}, },
closeModal: () => void, closeModal: () => void,
history: { push: string => void },
}; };
class ModalPhoneCollection extends React.PureComponent<Props> { class ModalPhoneCollection extends React.PureComponent<Props> {
@ -24,7 +26,7 @@ class ModalPhoneCollection extends React.PureComponent<Props> {
} }
renderInner() { renderInner() {
const { closeModal, phone, user } = this.props; const { closeModal, phone, user, history } = this.props;
const cancelButton = <Button button="link" onClick={closeModal} label={__('Not Now')} />; const cancelButton = <Button button="link" onClick={closeModal} label={__('Not Now')} />;
@ -33,6 +35,8 @@ class ModalPhoneCollection extends React.PureComponent<Props> {
} else if (!user.phone_number) { } else if (!user.phone_number) {
return <UserPhoneVerify cancelButton={cancelButton} />; return <UserPhoneVerify cancelButton={cancelButton} />;
} }
history.push('/$/rewards');
return closeModal(); return closeModal();
} }
@ -52,4 +56,4 @@ class ModalPhoneCollection extends React.PureComponent<Props> {
} }
} }
export default ModalPhoneCollection; export default withRouter(ModalPhoneCollection);

View file

@ -1,12 +1,11 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { doHideModal } from 'redux/actions/app'; import { doHideModal } from 'redux/actions/app';
import { doAuthNavigate } from 'redux/actions/navigation';
import ModalRewardApprovalRequired from './view'; import ModalRewardApprovalRequired from './view';
const perform = dispatch => ({ const perform = dispatch => ({
doAuth: () => { doAuth: () => {
dispatch(doHideModal()); dispatch(doHideModal());
dispatch(doAuthNavigate()); // dispatch(doAuthNavigate());
}, },
closeModal: () => dispatch(doHideModal()), closeModal: () => dispatch(doHideModal()),
}); });

View file

@ -7,13 +7,13 @@ import UserEmailNew from 'component/userEmailNew';
import UserEmailVerify from 'component/userEmailVerify'; import UserEmailVerify from 'component/userEmailVerify';
import UserVerify from 'component/userVerify'; import UserVerify from 'component/userVerify';
import Page from 'component/page'; import Page from 'component/page';
import { navigate } from '@reach/router';
type Props = { type Props = {
isPending: boolean, isPending: boolean,
email: string, email: string,
pathAfterAuth: string, pathAfterAuth: string,
location: UrlLocation, location: UrlLocation,
history: { push: string => void },
user: ?{ user: ?{
has_verified_email: boolean, has_verified_email: boolean,
is_reward_approved: boolean, is_reward_approved: boolean,
@ -31,7 +31,7 @@ class AuthPage extends React.PureComponent<Props> {
} }
navigateIfAuthenticated = (props: Props) => { navigateIfAuthenticated = (props: Props) => {
const { isPending, user, location } = props; const { isPending, user, location, history } = props;
if ( if (
!isPending && !isPending &&
user && user &&
@ -42,7 +42,7 @@ class AuthPage extends React.PureComponent<Props> {
const urlParams = new URLSearchParams(search); const urlParams = new URLSearchParams(search);
const redirectTo = urlParams.get('redirect'); const redirectTo = urlParams.get('redirect');
const path = redirectTo ? `/$/${redirectTo}` : '/'; const path = redirectTo ? `/$/${redirectTo}` : '/';
navigate(path); history.push(path);
} }
}; };

View file

@ -12,7 +12,7 @@ import Page from 'component/page';
import FileList from 'component/fileList'; import FileList from 'component/fileList';
import HiddenNsfwClaims from 'component/hiddenNsfwClaims'; import HiddenNsfwClaims from 'component/hiddenNsfwClaims';
import Button from 'component/button'; import Button from 'component/button';
import { navigate } from '@reach/router'; import { withRouter } from 'react-router-dom';
type Props = { type Props = {
uri: string, uri: string,
@ -23,7 +23,7 @@ type Props = {
claimsInChannel: Array<Claim>, claimsInChannel: Array<Claim>,
channelIsMine: boolean, channelIsMine: boolean,
fetchClaims: (string, number) => void, fetchClaims: (string, number) => void,
navigate: (string, {}) => void, history: { push: string => void },
openModal: (id: string, { uri: string }) => void, openModal: (id: string, { uri: string }) => void,
location: UrlLocation, location: UrlLocation,
}; };
@ -39,6 +39,7 @@ function ChannelPage(props: Props) {
openModal, openModal,
fetchClaims, fetchClaims,
location, location,
history,
} = props; } = props;
const { name, permanent_url: permanentUrl } = claim; const { name, permanent_url: permanentUrl } = claim;
@ -56,7 +57,7 @@ function ChannelPage(props: Props) {
return; return;
} }
navigate(`?page=${pageNumber}`); history.push(`?page=${pageNumber}`);
}; };
const paginate = (e: SyntheticKeyboardEvent<*>) => { const paginate = (e: SyntheticKeyboardEvent<*>) => {
@ -143,4 +144,4 @@ function ChannelPage(props: Props) {
); );
} }
export default ChannelPage; export default withRouter(ChannelPage);

View file

@ -1,5 +1,4 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { doAuthNavigate } from 'redux/actions/navigation';
import { doFetchAccessToken, selectAccessToken, selectUser } from 'lbryinc'; import { doFetchAccessToken, selectAccessToken, selectUser } from 'lbryinc';
import { selectDaemonSettings } from 'redux/selectors/settings'; import { selectDaemonSettings } from 'redux/selectors/settings';
import HelpPage from './view'; import HelpPage from './view';
@ -11,7 +10,7 @@ const select = state => ({
}); });
const perform = dispatch => ({ const perform = dispatch => ({
doAuth: () => dispatch(doAuthNavigate('/help')), // doAuth: () => dispatch(doAuthNavigate('/help')),
fetchAccessToken: () => dispatch(doFetchAccessToken()), fetchAccessToken: () => dispatch(doFetchAccessToken()),
}); });

View file

@ -1,3 +1,3 @@
import UserHistoryPage from './view'; import NavigationHistoryPage from './view';
export default UserHistoryPage; export default NavigationHistoryPage;

View file

@ -6,7 +6,6 @@ import {
selectUser, selectUser,
doRewardList, doRewardList,
} from 'lbryinc'; } from 'lbryinc';
import { navigate } from '@reach/router';
import { selectDaemonSettings } from 'redux/selectors/settings'; import { selectDaemonSettings } from 'redux/selectors/settings';
import RewardsPage from './view'; import RewardsPage from './view';
@ -20,9 +19,6 @@ const select = state => ({
const perform = dispatch => ({ const perform = dispatch => ({
fetchRewards: () => dispatch(doRewardList()), fetchRewards: () => dispatch(doRewardList()),
doAuth: () => {
navigate('/$/auth?redirect=rewards');
},
}); });
export default connect( export default connect(

View file

@ -27,7 +27,7 @@ type Props = {
class RewardsPage extends PureComponent<Props> { class RewardsPage extends PureComponent<Props> {
renderPageHeader() { renderPageHeader() {
const { doAuth, user, daemonSettings } = this.props; const { user, daemonSettings } = this.props;
if (user && !user.is_reward_approved && daemonSettings && daemonSettings.share_usage_data) { if (user && !user.is_reward_approved && daemonSettings && daemonSettings.share_usage_data) {
if (!user.primary_email || !user.has_verified_email || !user.is_identity_verified) { if (!user.primary_email || !user.has_verified_email || !user.is_identity_verified) {
@ -42,7 +42,7 @@ class RewardsPage extends PureComponent<Props> {
</header> </header>
<div className="card__content"> <div className="card__content">
<Button onClick={doAuth} button="primary" label="Prove Humanity" /> <Button navigate="/$/auth?redirect=rewards" button="primary" label="Prove Humanity" />
</div> </div>
</section> </section>
); );

View file

@ -10,7 +10,6 @@ import Page from 'component/page';
import ToolTip from 'component/common/tooltip'; import ToolTip from 'component/common/tooltip';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
import SearchOptions from 'component/searchOptions'; import SearchOptions from 'component/searchOptions';
import { Location } from '@reach/router';
type Props = { doSearch: string => void, location: UrlLocation }; type Props = { doSearch: string => void, location: UrlLocation };
@ -19,9 +18,17 @@ export default function SearchPage(props: Props) {
doSearch, doSearch,
location: { search }, location: { search },
} = props; } = props;
const urlParams = new URLSearchParams(search);
const urlParams = new URLSearchParams(location.search);
const urlQuery = urlParams.get('q'); const urlQuery = urlParams.get('q');
const isValid = isURIValid(urlQuery);
let uri;
let isChannel;
let label;
if (isValid) {
uri = normalizeURI(urlQuery);
({ isChannel } = parseURI(uri));
}
useEffect(() => { useEffect(() => {
if (urlQuery) { if (urlQuery) {
@ -31,67 +38,33 @@ export default function SearchPage(props: Props) {
return ( return (
<Page noPadding> <Page noPadding>
<Location> <section className="search">
{(locationProps: { {urlQuery && (
location: { <Fragment>
search: string, <header className="search__header">
}, {isValid && (
}) => {
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> <Fragment>
<header className="search__header"> <SearchOptions />
{isValid && (
<Fragment>
<SearchOptions />
<h1 className="media__uri">{uri}</h1> <h1 className="media__uri">{uri}</h1>
{isChannel ? ( {isChannel ? (
<ChannelTile size="large" isSearchResult uri={uri} /> <ChannelTile size="large" isSearchResult uri={uri} />
) : ( ) : (
<FileTile size="large" isSearchResult displayHiddenMessage 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> </Fragment>
)} )}
</section> </header>
);
}} <div className="search__results-wrapper">
</Location> <FileListSearch query={urlQuery} />
<div className="card__content help">
{__('These search results are provided by LBRY, Inc.')}
</div>
</div>
</Fragment>
)}
</section>
</Page> </Page>
); );
} }

View file

@ -11,8 +11,14 @@ import { selectBlackListedOutpoints } from 'lbryinc';
import ShowPage from './view'; import ShowPage from './view';
const select = (state, props) => { const select = (state, props) => {
const { pathname } = props.location;
const urlParts = pathname.split('/');
const claimName = urlParts[1];
const claimId = urlParts[2];
// claimName and claimId come from the url `lbry.tv/{claimName}/{claimId}" // claimName and claimId come from the url `lbry.tv/{claimName}/{claimId}"
const uri = buildURI({ contentName: props.claimName, claimId: props.claimId }); const uri = buildURI({ contentName: claimName, claimId: claimId });
return { return {
claim: makeSelectClaimForUri(uri)(state), claim: makeSelectClaimForUri(uri)(state),
isResolvingUri: makeSelectIsUriResolving(uri)(state), isResolvingUri: makeSelectIsUriResolving(uri)(state),

45
src/ui/reducers.js Normal file
View file

@ -0,0 +1,45 @@
import { combineReducers } from 'redux';
import { connectRouter } from 'connected-react-router';
import {
claimsReducer,
fileInfoReducer,
searchReducer,
walletReducer,
notificationsReducer,
} from 'lbry-redux';
import {
userReducer,
rewardsReducer,
costInfoReducer,
blacklistReducer,
homepageReducer,
statsReducer,
} from 'lbryinc';
import appReducer from 'redux/reducers/app';
import availabilityReducer from 'redux/reducers/availability';
import contentReducer from 'redux/reducers/content';
import settingsReducer from 'redux/reducers/settings';
import subscriptionsReducer from 'redux/reducers/subscriptions';
import publishReducer from 'redux/reducers/publish';
export default history =>
combineReducers({
router: connectRouter(history),
app: appReducer,
availability: availabilityReducer,
blacklist: blacklistReducer,
claims: claimsReducer,
content: contentReducer,
costInfo: costInfoReducer,
fileInfo: fileInfoReducer,
homepage: homepageReducer,
notifications: notificationsReducer,
publish: publishReducer,
rewards: rewardsReducer,
search: searchReducer,
settings: settingsReducer,
stats: statsReducer,
subscriptions: subscriptionsReducer,
user: userReducer,
wallet: walletReducer,
});

View file

@ -4,13 +4,11 @@ import isDev from 'electron-is-dev';
import { ipcRenderer, remote } from 'electron'; import { ipcRenderer, remote } from 'electron';
// @endif // @endif
import path from 'path'; import path from 'path';
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types'; import * as MODALS from 'constants/modal_types';
import { Lbry, doBalanceSubscribe, doFetchFileInfosAndPublishedClaims, doError } from 'lbry-redux'; import { Lbry, doBalanceSubscribe, doFetchFileInfosAndPublishedClaims, doError } from 'lbry-redux';
import Native from 'native'; import Native from 'native';
import { doFetchDaemonSettings } from 'redux/actions/settings'; import { doFetchDaemonSettings } from 'redux/actions/settings';
import { doAuthNavigate } from 'redux/actions/navigation';
import { doCheckSubscriptionsInit } from 'redux/actions/subscriptions'; import { doCheckSubscriptionsInit } from 'redux/actions/subscriptions';
import { import {
selectIsUpgradeSkipped, selectIsUpgradeSkipped,
@ -390,7 +388,7 @@ export function doConditionalAuthNavigate(newSession) {
const modal = selectModal(state); const modal = selectModal(state);
if (newSession || (modal && modal.id !== MODALS.EMAIL_COLLECTION)) { if (newSession || (modal && modal.id !== MODALS.EMAIL_COLLECTION)) {
dispatch(doAuthNavigate()); // dispatch(doAuthNavigate());
} }
}; };
} }

View file

@ -7,7 +7,7 @@ import * as MODALS from 'constants/modal_types';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
// @endif // @endif
import { doOpenModal } from 'redux/actions/app'; import { doOpenModal } from 'redux/actions/app';
import { navigate } from '@reach/router'; import { push } from 'connected-react-router';
import { setSubscriptionLatest, doUpdateUnreadSubscriptions } from 'redux/actions/subscriptions'; import { setSubscriptionLatest, doUpdateUnreadSubscriptions } from 'redux/actions/subscriptions';
import { makeSelectUnreadByChannel } from 'redux/selectors/subscriptions'; import { makeSelectUnreadByChannel } from 'redux/selectors/subscriptions';
import { import {
@ -83,7 +83,7 @@ export function doUpdateLoadStatus(uri: string, outpoint: string) {
silent: false, silent: false,
}); });
notif.onclick = () => { notif.onclick = () => {
navigate(formatLbryUriForWeb(uri)); dispatch(push(formatLbryUriForWeb(uri)));
}; };
} }

View file

@ -21,7 +21,7 @@ import {
} from 'lbry-redux'; } from 'lbry-redux';
import { doOpenModal } from 'redux/actions/app'; import { doOpenModal } from 'redux/actions/app';
import { selectosNotificationsEnabled } from 'redux/selectors/settings'; import { selectosNotificationsEnabled } from 'redux/selectors/settings';
import { navigate } from '@reach/router'; import { push } from 'connected-react-router';
import analytics from 'analytics'; import analytics from 'analytics';
import { formatLbryUriForWeb } from 'util/uri'; import { formatLbryUriForWeb } from 'util/uri';
// @if TARGET='app' // @if TARGET='app'
@ -328,7 +328,7 @@ export const doCheckPendingPublishes = () => (dispatch: Dispatch, getState: GetS
silent: false, silent: false,
}); });
notif.onclick = () => { notif.onclick = () => {
navigate(formatLbryUriForWeb(claim.permanent_url)); dispatch(push(formatLbryUriForWeb(claim.permanent_url)));
}; };
} }
} }

View file

@ -1,72 +0,0 @@
import * as ACTIONS from 'constants/action_types';
const getCurrentPath = () => {
const { hash } = document.location;
if (hash !== '') return hash.replace(/^#/, '');
return '/discover';
};
const reducers = {};
const defaultState = {
currentPath: getCurrentPath(),
pathAfterAuth: '/discover',
index: 0,
stack: [],
};
reducers[ACTIONS.DAEMON_READY] = state => {
const { currentPath } = state;
return Object.assign({}, state, {
stack: [{ path: currentPath, scrollY: 0 }],
});
};
reducers[ACTIONS.CHANGE_AFTER_AUTH_PATH] = (state, action) =>
Object.assign({}, state, {
pathAfterAuth: action.data.path,
});
reducers[ACTIONS.HISTORY_NAVIGATE] = (state, action) => {
const { stack, index } = state;
const { url: path, index: newIndex } = action.data;
const newState = {
currentPath: path,
};
if (newIndex >= 0) {
newState.index = newIndex;
} else if (!stack[index] || stack[index].path !== path) {
// ^ Check for duplicated
newState.stack = [...stack.slice(0, index + 1), { path, scrollY: 0 }];
newState.index = newState.stack.length - 1;
}
return Object.assign({}, state, newState);
};
reducers[ACTIONS.WINDOW_SCROLLED] = (state, action) => {
const { index } = state;
const { scrollY } = action.data;
return Object.assign({}, state, {
stack: state.stack.map((stackItem, itemIndex) => {
if (itemIndex !== index) {
return stackItem;
}
return {
...stackItem,
scrollY,
};
}),
});
};
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) {
return handler(state, action);
}
return state;
}

View file

@ -1,31 +1,12 @@
import appReducer from 'redux/reducers/app';
import availabilityReducer from 'redux/reducers/availability';
import contentReducer from 'redux/reducers/content';
import {
claimsReducer,
fileInfoReducer,
searchReducer,
walletReducer,
notificationsReducer,
} from 'lbry-redux';
import {
userReducer,
rewardsReducer,
costInfoReducer,
blacklistReducer,
homepageReducer,
statsReducer,
} from 'lbryinc';
import navigationReducer from 'redux/reducers/navigation';
import settingsReducer from 'redux/reducers/settings';
import subscriptionsReducer from 'redux/reducers/subscriptions';
import publishReducer from 'redux/reducers/publish';
import { persistStore, autoRehydrate } from 'redux-persist'; import { persistStore, autoRehydrate } from 'redux-persist';
import createCompressor from 'redux-persist-transform-compress'; import createCompressor from 'redux-persist-transform-compress';
import createFilter from 'redux-persist-transform-filter'; import createFilter from 'redux-persist-transform-filter';
import localForage from 'localforage'; import localForage from 'localforage';
import { createStore, applyMiddleware, compose, combineReducers } from 'redux'; import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk'; import thunk from 'redux-thunk';
import { createHashHistory, createBrowserHistory } from 'history';
import { connectRouter, routerMiddleware } from 'connected-react-router';
import createRootReducer from './reducers';
function isFunction(object) { function isFunction(object) {
return typeof object === 'function'; return typeof object === 'function';
@ -55,34 +36,18 @@ function enableBatching(reducer) {
}; };
} }
const reducers = combineReducers({ // @if TARGET='app'
app: appReducer, const history = createHashHistory();
navigation: navigationReducer, // @endif
availability: availabilityReducer, // @if TARGET='web'
claims: claimsReducer, const history = createBrowserHistory();
fileInfo: fileInfoReducer, // @endif
content: contentReducer,
costInfo: costInfoReducer,
rewards: rewardsReducer,
search: searchReducer,
settings: settingsReducer,
wallet: walletReducer,
user: userReducer,
subscriptions: subscriptionsReducer,
publish: publishReducer,
notifications: notificationsReducer,
blacklist: blacklistReducer,
homepage: homepageReducer,
stats: statsReducer,
});
const bulkThunk = createBulkThunkMiddleware(); const bulkThunk = createBulkThunkMiddleware();
const middleware = [thunk, bulkThunk]; const middleware = [routerMiddleware(history), thunk, bulkThunk];
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore( const store = createStore(
enableBatching(reducers), enableBatching(createRootReducer(history)),
{}, // initial state {}, // initial state
composeEnhancers( composeEnhancers(
autoRehydrate({ autoRehydrate({
@ -126,4 +91,4 @@ window.cacheStore = persistStore(store, persistOptions, err => {
}); });
// @endif // @endif
export default store; export { store, history };

View file

@ -7,6 +7,18 @@
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="text/javascript" src="ui.js"></script> <script type="text/javascript">
// Use relative path if we are in electron
let src = 'ui.js';
const userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf('electron') === -1) {
src = `/${src}`;
}
const script = document.createElement('script');
script.setAttribute('src', src);
document.body.appendChild(script);
</script>
</body> </body>
</html> </html>

View file

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

139
yarn.lock
View file

@ -734,6 +734,13 @@
dependencies: dependencies:
regenerator-runtime "^0.12.0" regenerator-runtime "^0.12.0"
"@babel/runtime@^7.3.1":
version "7.4.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.3.tgz#79888e452034223ad9609187a0ad1fe0d2ad4bdc"
integrity sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==
dependencies:
regenerator-runtime "^0.13.2"
"@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2": "@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2":
version "7.2.2" version "7.2.2"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907"
@ -850,17 +857,6 @@
resolved "https://registry.yarnpkg.com/@posthtml/esm/-/esm-1.0.0.tgz#09bcb28a02438dcee22ad1970ca1d85a000ae0cf" resolved "https://registry.yarnpkg.com/@posthtml/esm/-/esm-1.0.0.tgz#09bcb28a02438dcee22ad1970ca1d85a000ae0cf"
integrity sha512-dEVG+ITnvqKGa4v040tP+n8LOKOqr94qjLva7bE5pnfm2KHJwsKz69J4KMxgWLznbpBJzy8vQfCayEk3vLZnZQ== 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": "@samverschueren/stream-to-observable@^0.3.0":
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
@ -2545,6 +2541,14 @@ connect-history-api-fallback@^1.3.0:
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc"
integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==
connected-react-router@^6.3.2:
version "6.3.2"
resolved "https://registry.yarnpkg.com/connected-react-router/-/connected-react-router-6.3.2.tgz#8c90b77f1d7b783608cd50e4f69bf60e47265425"
integrity sha512-YxrAfMExl/OBsi+ojA4ywZeC7cmQ52MnZ4bhzqLhhjuOiXcQogC4kW0kodouXAXrXDovb2l3yEhDfpH99/wYcw==
dependencies:
immutable "^3.8.1"
seamless-immutable "^7.1.3"
console-browserify@^1.1.0: console-browserify@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10"
@ -2697,7 +2701,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
safe-buffer "^5.0.1" safe-buffer "^5.0.1"
sha.js "^2.4.8" sha.js "^2.4.8"
create-react-context@^0.2.1: create-react-context@^0.2.2:
version "0.2.3" version "0.2.3"
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.3.tgz#9ec140a6914a22ef04b8b09b7771de89567cb6f3" resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.3.tgz#9ec140a6914a22ef04b8b09b7771de89567cb6f3"
integrity sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag== integrity sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==
@ -5104,6 +5108,18 @@ highlight.js@^9.3.0:
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.15.6.tgz#72d4d8d779ec066af9a17cb14360c3def0aa57c4" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.15.6.tgz#72d4d8d779ec066af9a17cb14360c3def0aa57c4"
integrity sha512-zozTAWM1D6sozHo8kqhfYgsac+B+q0PmsjXeyDrYIHHcBN0zTVT66+s2GW1GZv7DbyaROdLXKdabwS/WqPyIdQ== integrity sha512-zozTAWM1D6sozHo8kqhfYgsac+B+q0PmsjXeyDrYIHHcBN0zTVT66+s2GW1GZv7DbyaROdLXKdabwS/WqPyIdQ==
history@^4.9.0:
version "4.9.0"
resolved "https://registry.yarnpkg.com/history/-/history-4.9.0.tgz#84587c2068039ead8af769e9d6a6860a14fa1bca"
integrity sha512-H2DkjCjXf0Op9OAr6nJ56fcRkTSNrUiv41vNJ6IswJjif6wlpZK0BTfFbi7qK9dXLSYZxkq5lBsj3vUjlYBYZA==
dependencies:
"@babel/runtime" "^7.1.2"
loose-envify "^1.2.0"
resolve-pathname "^2.2.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
value-equal "^0.4.0"
hmac-drbg@^1.0.0: hmac-drbg@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@ -5374,6 +5390,11 @@ immediate@~3.0.5:
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
immutable@^3.8.1:
version "3.8.2"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=
import-fresh@^2.0.0: import-fresh@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
@ -5495,7 +5516,7 @@ interpret@^1.0.0, interpret@^1.1.0:
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296"
integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==
invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: invariant@^2.2.2, invariant@^2.2.4:
version "2.2.4" version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
@ -6510,7 +6531,7 @@ longest-streak@^2.0.1:
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.2.tgz#2421b6ba939a443bb9ffebf596585a50b4c38e2e" resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.2.tgz#2421b6ba939a443bb9ffebf596585a50b4c38e2e"
integrity sha512-TmYTeEYxiAmSVdpbnQDXGtvYOIRsCMg89CVZzwzc2o7GFL1CjoiRPjH5ec0NFAVlAx3fVof9dX/t6KKRAo2OWA== integrity sha512-TmYTeEYxiAmSVdpbnQDXGtvYOIRsCMg89CVZzwzc2o7GFL1CjoiRPjH5ec0NFAVlAx3fVof9dX/t6KKRAo2OWA==
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.4.0: loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@ -7798,6 +7819,13 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
path-to-regexp@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
integrity sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=
dependencies:
isarray "0.0.1"
path-type@^1.0.0: path-type@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
@ -8385,7 +8413,7 @@ promise@^7.1.1:
dependencies: dependencies:
asap "~2.0.3" 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: 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, prop-types@^15.7.2:
version "15.7.2" version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@ -8641,7 +8669,12 @@ react-hot-loader@^4.7.2:
shallowequal "^1.0.2" shallowequal "^1.0.2"
source-map "^0.7.3" source-map "^0.7.3"
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: react-is@^16.6.0, react-is@^16.8.2:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
react-is@^16.7.0, react-is@^16.8.1:
version "16.8.3" version "16.8.3"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.3.tgz#4ad8b029c2a718fc0cfc746c8d4e1b7221e5387d" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.3.tgz#4ad8b029c2a718fc0cfc746c8d4e1b7221e5387d"
integrity sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA== integrity sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==
@ -8678,18 +8711,46 @@ react-pose@^4.0.5:
popmotion-pose "^3.4.0" popmotion-pose "^3.4.0"
tslib "^1.9.1" tslib "^1.9.1"
react-redux@^5.0.3: react-redux@^6.0.1:
version "5.1.1" version "6.0.1"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.1.tgz#88e368682c7fa80e34e055cd7ac56f5936b0f52f" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-6.0.1.tgz#0d423e2c1cb10ada87293d47e7de7c329623ba4d"
integrity sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg== integrity sha512-T52I52Kxhbqy/6TEfBv85rQSDz6+Y28V/pf52vDWs1YRXG19mcFOGfHnY2HsNFHyhP+ST34Aih98fvt6tqwVcQ==
dependencies:
"@babel/runtime" "^7.3.1"
hoist-non-react-statics "^3.3.0"
invariant "^2.2.4"
loose-envify "^1.4.0"
prop-types "^15.7.2"
react-is "^16.8.2"
react-router-dom@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.0.tgz#542a9b86af269a37f0b87218c4c25ea8dcf0c073"
integrity sha512-wSpja5g9kh5dIteZT3tUoggjnsa+TPFHSMrpHXMpFsaHhQkm/JNVGh2jiF9Dkh4+duj4MKCkwO6H08u6inZYgQ==
dependencies: dependencies:
"@babel/runtime" "^7.1.2" "@babel/runtime" "^7.1.2"
history "^4.9.0"
loose-envify "^1.3.1"
prop-types "^15.6.2"
react-router "5.0.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-router@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.0.0.tgz#349863f769ffc2fa10ee7331a4296e86bc12879d"
integrity sha512-6EQDakGdLG/it2x9EaCt9ZpEEPxnd0OCLBHQ1AcITAAx7nCnyvnzf76jKWG1s2/oJ7SSviUgfWHofdYljFexsA==
dependencies:
"@babel/runtime" "^7.1.2"
create-react-context "^0.2.2"
history "^4.9.0"
hoist-non-react-statics "^3.1.0" hoist-non-react-statics "^3.1.0"
invariant "^2.2.4" loose-envify "^1.3.1"
loose-envify "^1.1.0" path-to-regexp "^1.7.0"
prop-types "^15.6.1" prop-types "^15.6.2"
react-is "^16.6.0" react-is "^16.6.0"
react-lifecycles-compat "^3.0.0" tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-simplemde-editor@^4.0.0: react-simplemde-editor@^4.0.0:
version "4.0.0" version "4.0.0"
@ -8926,6 +8987,11 @@ regenerator-runtime@^0.12.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
regenerator-runtime@^0.13.2:
version "0.13.2"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447"
integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==
regenerator-transform@^0.13.4: regenerator-transform@^0.13.4:
version "0.13.4" version "0.13.4"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.4.tgz#18f6763cf1382c69c36df76c6ce122cc694284fb" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.4.tgz#18f6763cf1382c69c36df76c6ce122cc694284fb"
@ -9209,6 +9275,11 @@ resolve-from@^4.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve-pathname@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879"
integrity sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==
resolve-url@^0.2.1: resolve-url@^0.2.1:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
@ -9386,6 +9457,11 @@ scss-tokenizer@^0.2.3:
js-base64 "^2.1.8" js-base64 "^2.1.8"
source-map "^0.4.2" source-map "^0.4.2"
seamless-immutable@^7.1.3:
version "7.1.4"
resolved "https://registry.yarnpkg.com/seamless-immutable/-/seamless-immutable-7.1.4.tgz#6e9536def083ddc4dea0207d722e0e80d0f372f8"
integrity sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A==
seek-bzip@^1.0.5: seek-bzip@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc" resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc"
@ -10315,6 +10391,16 @@ timers-browserify@^2.0.4:
dependencies: dependencies:
setimmediate "^1.0.4" setimmediate "^1.0.4"
tiny-invariant@^1.0.2:
version "1.0.4"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.4.tgz#346b5415fd93cb696b0c4e8a96697ff590f92463"
integrity sha512-lMhRd/djQJ3MoaHEBrw8e2/uM4rs9YMNk0iOr8rHQ0QdbM7D4l0gFl3szKdeixrlyfm9Zqi4dxHCM2qVG8ND5g==
tiny-warning@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.2.tgz#1dfae771ee1a04396bdfde27a3adcebc6b648b28"
integrity sha512-rru86D9CpQRLvsFG5XFdy0KdLAvjdQDyZCsRcuu60WtzFylDM3eAWSxEVz5kzL2Gp544XiUvPbVKtOA/txLi9Q==
tmp@^0.0.33: tmp@^0.0.33:
version "0.0.33" version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@ -10887,6 +10973,11 @@ validate-npm-package-license@^3.0.1:
spdx-correct "^3.0.0" spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0" spdx-expression-parse "^3.0.0"
value-equal@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7"
integrity sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==
vary@~1.1.2: vary@~1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"