on master help sign in
refactor: unified back button, fix history back button reliability
commitf9d545dce6f52dc19c2d3f74e5873fe6de720c5b authorMatt Condon (shrugs) <mattgcondon@gmail.com> dateTue 30 Jul 2019, 09:15 (7 years ago) parent02ebd11
29 files changed+205−180
src/components/MiniBackButton.js+1−1 src/components/View.js+41−9 src/indigo-react/components/IconButton.js+5−3 src/lib/routeNames.js−1 src/lib/useRouter.js+20−22 src/store/history.js+5−2 src/style/indigo-proposals.scss+2−1 src/views/AcceptTransfer.js+3−6 src/views/Activate/ActivateCode.js+1−1 src/views/Activate/PassportTransfer.js+1−4 src/views/Admin.js+1−1 src/views/Admin/AdminCancelTransfer.js−3 src/views/Admin/AdminEditPermissions.js+1−3 src/views/Admin/AdminHome.js+1−3 src/views/Admin/AdminNetworkingKeys.js−3 src/views/Admin/AdminRedownload.js−2 src/views/Admin/AdminReticket.js+1−3 src/views/Admin/AdminSetProxy.js−3 src/views/Admin/AdminTransfer.js−2 src/views/CancelTransfer.js+5−10 src/views/CreateGalaxy.js+4−7 src/views/Invite.js+1−1 src/views/Invite/InviteEmail.js+10−18 src/views/IssueChild.js+7−10 src/views/Login.js+6−6 src/views/Party/PartySetPoolSize.js+3−5 src/views/Point.js+3−3 src/views/Points.js+56−28 src/views/ViewPoint.js+27−19
src/components/MiniBackButton.js+1−1
@@ -5,7 +5,7 @@ import { IconButton } from 'indigo-react';
55import { ReactComponent as Back } from 'assets/back.svg';
66
77export default ({ className, ...rest }) => (
8− <IconButton {...rest} flush>
8+ <IconButton {...rest}>
99 <Back className={cn('black', className)} />
1010 </IconButton>
1111);
src/components/View.js+41−9
@@ -1,18 +1,38 @@
1−import React from 'react';
1+import React, { useCallback } from 'react';
22import cn from 'classnames';
33import { Flex } from 'indigo-react';
44
55import Footer from './Footer';
66import useBreakpoints from 'lib/useBreakpoints';
7+import MiniBackButton from './MiniBackButton';
8+import { useHistory } from 'store/history';
79
810// View is a top-level component that all Views must render to inherit styling
9−function View({ className, children, inset = false, full = false, ...rest }) {
11+function View({
12+ className,
13+ children,
14+ inset = false,
15+ full = false,
16+ pop,
17+ ...rest
18+}) {
19+ const { size } = useHistory();
1020 const isMobile = useBreakpoints([true, false, false]);
1121 const shouldInset = inset && !isMobile;
1222
23+ const insetPadding = cn({
24+ pt10: shouldInset,
25+ pt5: !shouldInset,
26+ });
27+
28+ const goBack = useCallback(() => pop(), [pop]);
29+
30+ const showBackButton = size > 1 && !!pop;
31+
1332 return (
1433 <Flex
15− col
34+ row={!isMobile}
35+ col={isMobile}
1636 justify="between"
1737 className={cn(
1838 'minh-100 ph5',
@@ -24,13 +44,25 @@ function View({ className, children, inset = false, full = false, ...rest }) {
2444 )}
2545 {...rest}>
2646 <Flex.Item
27− className={cn('pb5', {
28− pt10: shouldInset,
29− pt5: !shouldInset,
30− })}>
31− {children}
47+ as={Flex}
48+ col
49+ style={{ width: '48px' }}
50+ className={cn(insetPadding)}>
51+ {showBackButton && (
52+ <MiniBackButton
53+ hpadding={!isMobile}
54+ vpadding={isMobile}
55+ onClick={goBack}
56+ />
57+ )}
3258 </Flex.Item>
33− <Footer.Target />
59+
60+ <Flex.Item flex={1} as={Flex} col justify="between">
61+ <Flex.Item className={cn('pb5', insetPadding)}>{children}</Flex.Item>
62+ <Flex.Item as={Footer.Target} />
63+ </Flex.Item>
64+
65+ <Flex.Item style={{ width: '48px' }} className={cn(insetPadding)} />
3466 </Flex>
3567 );
3668}
src/indigo-react/components/IconButton.js+5−3
@@ -10,7 +10,8 @@ export default function IconButton({
1010 disabled = false,
1111 secondary = false,
1212 solid = false,
13− flush = false, // if true, remove horizontal padding
13+ hpadding = true,
14+ vpadding = true,
1415 ...rest
1516}) {
1617 return (
@@ -19,9 +20,10 @@ export default function IconButton({
1920 align="center"
2021 justify="center"
2122 className={cn(
22− 'pointer pv2',
23+ 'pointer',
2324 {
24− ph2: !flush,
25+ ph2: hpadding,
26+ pv2: vpadding,
2527 },
2628 {
2729 'bg-black': solid && !secondary && !disabled,
src/lib/routeNames.js−1
@@ -15,7 +15,6 @@ export const ROUTE_NAMES = {
1515 //
1616 ACCEPT_TRANSFER: Symbol('ACCEPT_TRANSFER'),
1717 CANCEL_TRANSFER: Symbol('CANCEL_TRANSFER'),
18− LANDING: Symbol('LANDING'),
1918 VIEW_POINT: Symbol('VIEW_POINT'),
2019 POINTS: Symbol('POINTS'),
2120 POINT: Symbol('POINT'),
src/lib/useRouter.js+20−22
@@ -4,13 +4,11 @@ import { last, includes as _includes, findIndex } from 'lodash';
44const NULL_DATA = {};
55
66/**
7− * @param primary whether or not this is the top-level router
87 * @param names map of string to view key, used for consumer references
98 * @param views map of view keys to react component views
109 * @param initialRoutes initial set of routes following the { key, data } format
1110 */
1211export default function useRouter({
13− primary = false,
1412 names = {},
1513 views = {},
1614 initialRoutes = [],
@@ -52,15 +50,27 @@ export default function useRouter({
5250 );
5351 const pop = useCallback(
5452 (count = 1) => {
55− if (size > 1) {
56− // pop as expected
57− return setRoutes(routes => [...routes.slice(0, routes.length - count)]);
58− }
53+ if (size <= 1) {
54+ // if we are at the root, pass this event to our parent
55+ if (oldPopState.current) {
56+ console.log('attempting to pass to parent');
57+ window.history.back();
58+ }
59+
60+ console.log('at root');
5961
60− // if we are at the root, pass this event to our parent
61− if (oldPopState.current) {
62− window.history.back();
62+ return;
6363 }
64+
65+ // on pop, tell the browser of a new state to avoid giving the user
66+ // the ability to go forward
67+ // TODO: allow the user to go forward by storing our data in history
68+ // and using the url for other state
69+ // (or just importing an actually good router lib)
70+ window.history.pushState(null, null, null);
71+
72+ // pop as expected
73+ return setRoutes(routes => [...routes.slice(0, routes.length - count)]);
6474 },
6575 [size, setRoutes, oldPopState]
6676 );
@@ -102,19 +112,7 @@ export default function useRouter({
102112
103113 // construct new onpopstate handler
104114 window.onpopstate = e => {
105− if (size <= 1 && oldPopState.current) {
106− // if this is the root route and there's a parent handler,
107− // give the event to the handler
108− return oldPopState.current(e);
109− }
110−
111− // on pop, tell the browser of a new state to avoid giving the user
112− // the ability to go forward
113− // TODO: allow the user to go forward by storing our data in history
114− // and using the url for other state
115− window.history.pushState(null, null, null);
116−
117− // then update our local state for rendering
115+ e.stopImmediatePropagation();
118116 pop();
119117 };
120118
src/store/history.js+5−2
@@ -1,6 +1,7 @@
11import React, { createContext, forwardRef, useContext } from 'react';
22
33import useRouter from 'lib/useRouter';
4+import { LocalRouterProvider } from 'lib/LocalRouter';
45
56/**
67 * `useHistory` is just a global `useRouter`
@@ -11,10 +12,12 @@ import useRouter from 'lib/useRouter';
1112export const HistoryContext = createContext(null);
1213
1314export function HistoryProvider({ names, views, initialRoutes, children }) {
14− const router = useRouter({ names, views, initialRoutes, primary: true });
15+ const router = useRouter({ names, views, initialRoutes });
1516
1617 return (
17− <HistoryContext.Provider value={router}>{children}</HistoryContext.Provider>
18+ <HistoryContext.Provider value={router}>
19+ <LocalRouterProvider value={router}>{children}</LocalRouterProvider>
20+ </HistoryContext.Provider>
1821 );
1922}
2023
src/style/indigo-proposals.scss+2−1
@@ -1,6 +1,7 @@
11.mw1 {
22 margin: 0 auto;
3− max-width: 30rem;
3+ max-width: calc(32rem + 48px * 2);
4+ // TODO: this css is gross, fix
45}
56
67.mw2 {
src/views/AcceptTransfer.js+3−6
@@ -6,7 +6,6 @@ import * as azimuth from 'azimuth-js';
66import { useNetwork } from 'store/network';
77import { usePointCursor } from 'store/pointCursor';
88import { usePointCache } from 'store/pointCache';
9−import { useHistory } from 'store/history';
109import { useWallet } from 'store/wallet';
1110
1211import * as need from 'lib/need';
@@ -14,9 +13,9 @@ import useCurrentPointName from 'lib/useCurrentPointName';
1413import useEthereumTransaction from 'lib/useEthereumTransaction';
1514import { GAS_LIMITS } from 'lib/constants';
1615import useLifecycle from 'lib/useLifecycle';
16+import { useLocalRouter } from 'lib/LocalRouter';
1717
1818import ViewHeader from 'components/ViewHeader';
19−import MiniBackButton from 'components/MiniBackButton';
2019import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
2120import View from 'components/View';
2221
@@ -58,17 +57,15 @@ function useAcceptTransfer() {
5857}
5958
6059export default function AcceptTransfer() {
61− const { pop } = useHistory();
60+ const { pop } = useLocalRouter();
6261
6362 const name = useCurrentPointName();
6463
6564 const { completed, bind } = useAcceptTransfer();
6665
6766 return (
68− <View inset>
67+ <View pop={pop} inset>
6968 <Grid>
70− <Grid.Item full as={MiniBackButton} onClick={() => pop()} />
71−
7269 <Grid.Item full as={ViewHeader}>
7370 Accept Transfer
7471 </Grid.Item>
src/views/Activate/ActivateCode.js+1−1
@@ -122,7 +122,7 @@ export default function ActivateCode() {
122122 useSyncKnownPoints([derivedPoint.getOrElse(null)].filter(p => p !== null));
123123
124124 return (
125− <View>
125+ <View inset>
126126 <Grid>
127127 <Grid.Item as={Passport} point={derivedPoint} full />
128128 <Grid.Item as={H4} className="mt3 mb2" full>
src/views/Activate/PassportTransfer.js+1−4
@@ -59,7 +59,6 @@ export default function PassportTransfer({ className, resetActivateRouter }) {
5959 const goToLogin = useCallback(
6060 () =>
6161 replaceWith([
62− { key: names.LANDING },
6362 { key: names.LOGIN },
6463 { key: names.POINTS },
6564 { key: names.POINT },
@@ -75,13 +74,11 @@ export default function PassportTransfer({ className, resetActivateRouter }) {
7574 // we only need to clear activate because that will unmount the passport
7675 // router (which will be set to initialRoutes when mounted again)
7776
78− // 1) replace history
79− replaceWith([{ key: names.LANDING }, { key: names.ACTIVATE }]);
8077 // 2) reset local router
8178 resetActivateRouter();
8279 // 3) clear the state
8380 resetActivateFlow();
84− }, [replaceWith, names, resetActivateRouter, resetActivateFlow]);
81+ }, [resetActivateRouter, resetActivateFlow]);
8582
8683 const handleUpdate = useCallback(
8784 ({ type, state, value }) => {
src/views/Admin.js+1−1
@@ -52,7 +52,7 @@ export default function Admin() {
5252 });
5353
5454 return (
55− <View inset>
55+ <View pop={router.pop} inset>
5656 <LocalRouterProvider value={router}>
5757 <Grid className="mb4">
5858 <Grid.Item
src/views/Admin/AdminCancelTransfer.js−3
@@ -16,7 +16,6 @@ import { ETH_ZERO_ADDR } from 'lib/wallet';
1616import useLifecycle from 'lib/useLifecycle';
1717
1818import ViewHeader from 'components/ViewHeader';
19−import MiniBackButton from 'components/MiniBackButton';
2019import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
2120
2221function useCancelTransfer() {
@@ -59,8 +58,6 @@ export default function AdminCancelTransfer() {
5958
6059 return (
6160 <Grid>
62− <Grid.Item full as={MiniBackButton} onClick={() => pop()} />
63−
6461 <Grid.Item full as={ViewHeader}>
6562 Cancel Outgoing Transfer
6663 </Grid.Item>
src/views/Admin/AdminEditPermissions.js+1−3
@@ -18,10 +18,9 @@ import useCurrentPermissions from 'lib/useCurrentPermissions';
1818import ViewHeader from 'components/ViewHeader';
1919import { ForwardButton } from 'components/Buttons';
2020import { matchBlinkyDate } from 'components/Blinky';
21−import MiniBackButton from 'components/MiniBackButton';
2221
2322export default function AdminEditPermissions() {
24− const { pop, push, names } = useLocalRouter();
23+ const { push, names } = useLocalRouter();
2524 const { contracts } = useNetwork();
2625 const { pointCursor } = usePointCursor();
2726 const { getDetails, getRekeyDate } = usePointCache();
@@ -92,7 +91,6 @@ export default function AdminEditPermissions() {
9291
9392 return (
9493 <Grid>
95− <Grid.Item full as={MiniBackButton} onClick={() => pop()} />
9694 <Grid.Item full as={ViewHeader}>
9795 Permissions
9896 </Grid.Item>
src/views/Admin/AdminHome.js+1−3
@@ -11,10 +11,9 @@ import useKeyfileGenerator from 'lib/useKeyfileGenerator';
1111import { ForwardButton } from 'components/Buttons';
1212import ViewHeader from 'components/ViewHeader';
1313import DownloadKeyfileButton from 'components/DownloadKeyfileButton';
14−import MiniBackButton from 'components/MiniBackButton';
1514
1615export default function AdminHome() {
17− const { push, names, pop } = useLocalRouter();
16+ const { push, names } = useLocalRouter();
1817 const { urbitWallet } = useWallet();
1918
2019 const { bind: keyfileBind } = useKeyfileGenerator();
@@ -38,7 +37,6 @@ export default function AdminHome() {
3837 return (
3938 <>
4039 <Grid>
41− <Grid.Item full as={MiniBackButton} onClick={() => pop()} />
4240 <Grid.Item full as={ViewHeader}>
4341 Admin
4442 </Grid.Item>
src/views/Admin/AdminNetworkingKeys.js−3
@@ -34,7 +34,6 @@ import { useHexInput, useCheckboxInput } from 'lib/useInputs';
3434import useKeyfileGenerator from 'lib/useKeyfileGenerator';
3535
3636import ViewHeader from 'components/ViewHeader';
37−import MiniBackButton from 'components/MiniBackButton';
3837import { BootArvoButton } from 'components/Buttons';
3938import FooterButton from 'components/FooterButton';
4039import DownloadKeyfileButton from 'components/DownloadKeyfileButton';
@@ -315,8 +314,6 @@ export default function AdminNetworkingKeys() {
315314 return (
316315 <>
317316 <Grid>
318− <Grid.Item full as={MiniBackButton} onClick={() => pop()} />
319−
320317 <Grid.Item full as={ViewHeader}>
321318 {renderTitle()}
322319 </Grid.Item>
src/views/Admin/AdminRedownload.js−2
@@ -13,7 +13,6 @@ import { useWallet } from 'store/wallet';
1313import { Grid, P } from 'indigo-react';
1414import ViewHeader from 'components/ViewHeader';
1515import { useLocalRouter } from 'lib/LocalRouter';
16−import MiniBackButton from 'components/MiniBackButton';
1716
1817export default function AdminRedownload() {
1918 const { urbitWallet } = useWallet();
@@ -36,7 +35,6 @@ export default function AdminRedownload() {
3635 return (
3736 <>
3837 <Grid>
39− <Grid.Item full as={MiniBackButton} onClick={() => pop()} />
4038 <Grid.Item full as={ViewHeader}>
4139 Download Passport
4240 </Grid.Item>
src/views/Admin/AdminReticket.js+1−3
@@ -5,7 +5,6 @@ import { Grid, Flex } from 'indigo-react';
55import { LocalRouterProvider } from 'lib/LocalRouter';
66import useRouter from 'lib/useRouter';
77
8−import MiniBackButton from 'components/MiniBackButton';
98import Steps from 'components/Steps';
109
1110import ReticketConfirm from './Reticket/ReticketConfirm';
@@ -53,8 +52,7 @@ export default function AdminReticket() {
5352 return (
5453 <LocalRouterProvider value={router}>
5554 <Grid>
56− <Grid.Item full as={Flex} row justify="between" align="center">
57− <Flex.Item as={MiniBackButton} onClick={() => router.pop()} />
55+ <Grid.Item full as={Flex} row align="center">
5856 <Flex.Item as={Steps} num={router.size} total={STEPS.length} />
5957 </Grid.Item>
6058 </Grid>
src/views/Admin/AdminSetProxy.js−3
@@ -20,7 +20,6 @@ import { useAddressInput, useCheckboxInput } from 'lib/useInputs';
2020import useEthereumTransaction from 'lib/useEthereumTransaction';
2121
2222import ViewHeader from 'components/ViewHeader';
23−import MiniBackButton from 'components/MiniBackButton';
2423import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
2524import { GAS_LIMITS } from 'lib/constants';
2625
@@ -153,8 +152,6 @@ export default function AdminSetProxy() {
153152
154153 return (
155154 <Grid>
156− <Grid.Item full as={MiniBackButton} onClick={() => pop()} />
157−
158155 <Grid.Item full as={ViewHeader}>
159156 {properProxyType} Address
160157 </Grid.Item>
src/views/Admin/AdminTransfer.js−2
@@ -15,7 +15,6 @@ import useEthereumTransaction from 'lib/useEthereumTransaction';
1515import { GAS_LIMITS } from 'lib/constants';
1616
1717import ViewHeader from 'components/ViewHeader';
18−import MiniBackButton from 'components/MiniBackButton';
1918import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
2019
2120function useTransfer() {
@@ -64,7 +63,6 @@ export default function AdminTransfer() {
6463
6564 return (
6665 <Grid>
67− <Grid.Item full as={MiniBackButton} onClick={() => pop()} />
6866 <Grid.Item full as={ViewHeader}>
6967 Transfer Point
7068 </Grid.Item>
src/views/CancelTransfer.js+5−10
@@ -3,19 +3,14 @@ import React from 'react';
33import View from 'components/View';
44
55import AdminCancelTransfer from './Admin/AdminCancelTransfer';
6−import { LocalRouterProvider } from 'lib/LocalRouter';
7−import { useHistory } from 'store/history';
6+import { useLocalRouter } from 'lib/LocalRouter';
87
98export default function CancelTransfer() {
10− const history = useHistory();
9+ const { pop } = useLocalRouter();
1110
1211 return (
13− // TODO: this is a hack so AdminCancelTransfer works unmodified
14− // TODO: consolidate history & 'local' router providers
15− <LocalRouterProvider value={history}>
16− <View inset>
17− <AdminCancelTransfer />
18− </View>
19− </LocalRouterProvider>
12+ <View pop={pop} inset>
13+ <AdminCancelTransfer />
14+ </View>
2015 );
2116}
src/views/CreateGalaxy.js+4−7
@@ -5,7 +5,6 @@ import { Grid, Text, Input } from 'indigo-react';
55import * as azimuth from 'azimuth-js';
66
77import { useNetwork } from 'store/network';
8−import { useHistory } from 'store/history';
98import { usePointCache } from 'store/pointCache';
109
1110import * as need from 'lib/need';
@@ -13,12 +12,12 @@ import { useAddressInput, useGalaxyInput } from 'lib/useInputs';
1312import useEthereumTransaction from 'lib/useEthereumTransaction';
1413import { GAS_LIMITS } from 'lib/constants';
1514import patp2dec from 'lib/patp2dec';
15+import { isZeroAddress } from 'lib/wallet';
16+import { useLocalRouter } from 'lib/LocalRouter';
1617
1718import ViewHeader from 'components/ViewHeader';
18−import MiniBackButton from 'components/MiniBackButton';
1919import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
2020import View from 'components/View';
21−import { isZeroAddress } from 'lib/wallet';
2221
2322function useCreateGalaxy() {
2423 const { contracts } = useNetwork();
@@ -42,7 +41,7 @@ function useCreateGalaxy() {
4241}
4342
4443export default function CreateGalaxy() {
45− const { pop } = useHistory();
44+ const { pop } = useLocalRouter();
4645 const { contracts } = useNetwork();
4746 const _contracts = need.contracts(contracts);
4847
@@ -128,10 +127,8 @@ export default function CreateGalaxy() {
128127 ]);
129128
130129 return (
131− <View inset>
130+ <View pop={pop} inset>
132131 <Grid>
133− <Grid.Item full as={MiniBackButton} onClick={() => pop()} />
134−
135132 <Grid.Item full as={ViewHeader}>
136133 Create a Galaxy
137134 </Grid.Item>
src/views/Invite.js+1−1
@@ -53,7 +53,7 @@ export default function Invite() {
5353 const acceptedInvitesText = matchBlinky(acceptedInvites);
5454
5555 return (
56− <View inset>
56+ <View pop={router.pop} inset>
5757 <LocalRouterProvider value={router}>
5858 <Grid className="mb4">
5959 <Grid.Item
src/views/Invite/InviteEmail.js+10−18
@@ -29,7 +29,6 @@ import {
2929 hexify,
3030} from 'lib/txn';
3131import * as tank from 'lib/tank';
32−import { useLocalRouter } from 'lib/LocalRouter';
3332import useArray from 'lib/useArray';
3433import { buildEmailInputConfig } from 'lib/useInputs';
3534import { MIN_PLANET, GAS_LIMITS, DEFAULT_GAS_PRICE_GWEI } from 'lib/constants';
@@ -39,7 +38,6 @@ import useSetState from 'lib/useSetState';
3938import pluralize from 'lib/pluralize';
4039import useMailer from 'lib/useMailer';
4140
42−import MiniBackButton from 'components/MiniBackButton';
4341import LoadableButton from 'components/LoadableButton';
4442import Highlighted from 'components/Highlighted';
4543
@@ -96,7 +94,6 @@ const buildAccessoryFor = (dones, errors) => name => {
9694// TODO: test with tank, successful txs
9795export default function InviteEmail() {
9896 // TODO: resumption after error?
99− const { pop } = useLocalRouter();
10097 const { contracts, web3, networkType } = useNetwork();
10198 const { wallet, walletType, walletHdPath } = useWallet();
10299 const { syncInvites, getInvites } = usePointCache();
@@ -427,21 +424,16 @@ export default function InviteEmail() {
427424
428425 return (
429426 <Grid gap={3}>
430− <Grid.Item as={Grid} full>
431− <Grid.Item as={Flex} cols={[1, 11]} align="center">
432− <MiniBackButton onClick={() => pop()} />
433− </Grid.Item>
434− <Grid.Item cols={[11, 13]} justifySelf="end">
435− {/* use hidden class instead of removing component from dom */}
436− {/* in order to avoid janky reflow */}
437− <IconButton
438− onClick={() => appendInput()}
439− disabled={!canAddInvite}
440− className={cn({ hidden: isDone })}
441− solid>
442− +
443− </IconButton>
444− </Grid.Item>
427+ <Grid.Item full as={Flex} justify="end">
428+ {/* use hidden class instead of removing component from dom */}
429+ {/* in order to avoid janky reflow */}
430+ <IconButton
431+ onClick={() => appendInput()}
432+ disabled={!canAddInvite}
433+ className={cn({ hidden: isDone })}
434+ solid>
435+ +
436+ </IconButton>
445437 </Grid.Item>
446438
447439 {isDone && (
src/views/IssueChild.js+7−10
@@ -6,23 +6,22 @@ import * as azimuth from 'azimuth-js';
66import * as ob from 'urbit-ob';
77
88import { useNetwork } from 'store/network';
9−import { useHistory } from 'store/history';
109import { usePointCache } from 'store/pointCache';
10+import { usePointCursor } from 'store/pointCursor';
1111
1212import * as need from 'lib/need';
1313import { useAddressInput, usePointInput } from 'lib/useInputs';
1414import useEthereumTransaction from 'lib/useEthereumTransaction';
1515import { GAS_LIMITS } from 'lib/constants';
1616import patp2dec from 'lib/patp2dec';
17+import useLifecycle from 'lib/useLifecycle';
18+import { validateNameInNumberSet } from 'lib/validators';
19+import { getSpawnCandidate } from 'lib/child';
20+import { useLocalRouter } from 'lib/LocalRouter';
1721
1822import ViewHeader from 'components/ViewHeader';
19−import MiniBackButton from 'components/MiniBackButton';
2023import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
2124import View from 'components/View';
22−import useLifecycle from 'lib/useLifecycle';
23−import { usePointCursor } from 'store/pointCursor';
24−import { validateNameInNumberSet } from 'lib/validators';
25−import { getSpawnCandidate } from 'lib/child';
2625
2726function useIssueChild() {
2827 const { contracts } = useNetwork();
@@ -49,7 +48,7 @@ function useIssueChild() {
4948}
5049
5150export default function IssueChild() {
52− const { pop } = useHistory();
51+ const { pop } = useLocalRouter();
5352 const { contracts } = useNetwork();
5453 const { pointCursor } = usePointCursor();
5554
@@ -131,10 +130,8 @@ export default function IssueChild() {
131130 });
132131
133132 return (
134− <View inset>
133+ <View pop={pop} inset>
135134 <Grid>
136− <Grid.Item full as={MiniBackButton} onClick={() => pop()} />
137−
138135 <Grid.Item full as={ViewHeader}>
139136 Issue Child Point
140137 </Grid.Item>
src/views/Login.js+6−6
@@ -63,7 +63,7 @@ const walletTypeToViewName = walletType => {
6363
6464export default function Login() {
6565 // globals
66− const { push, replaceWith, names } = useHistory();
66+ const { pop, push, names } = useHistory();
6767 const { contracts } = useNetwork();
6868 const { wallet, walletType } = useWallet();
6969 const { pointCursor, setPointCursor } = usePointCursor();
@@ -75,10 +75,10 @@ export default function Login() {
7575 walletTypeToViewName(walletType)
7676 );
7777
78− const goToActivate = useCallback(
79− () => replaceWith([{ key: names.LANDING }, { key: names.ACTIVATE }]),
80− [replaceWith, names]
81− );
78+ const goToActivate = useCallback(() => push(names.ACTIVATE), [
79+ push,
80+ names.ACTIVATE,
81+ ]);
8282
8383 const goToPoints = useCallback(() => {
8484 push(names.POINTS);
@@ -120,7 +120,7 @@ export default function Login() {
120120 }, [contracts, pointCursor, setPointCursor, wallet, goToPoint, goToPoints]);
121121
122122 return (
123− <View inset>
123+ <View pop={pop} inset>
124124 <Grid>
125125 <Grid.Item full as={Crumbs} routes={[{ text: 'Multipass' }]} />
126126 <Grid.Item full as={H4} className="mt4">
src/views/Party/PartySetPoolSize.js+3−5
@@ -11,12 +11,11 @@ import * as need from 'lib/need';
1111import { usePointInput, useNumberInput } from 'lib/useInputs';
1212import useEthereumTransaction from 'lib/useEthereumTransaction';
1313import { GAS_LIMITS } from 'lib/constants';
14+import { useLocalRouter } from 'lib/LocalRouter';
1415
1516import ViewHeader from 'components/ViewHeader';
16−import MiniBackButton from 'components/MiniBackButton';
1717import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
1818import patp2dec from 'lib/patp2dec';
19−import { useHistory } from 'store/history';
2019import View from 'components/View';
2120import useCurrentPermissions from 'lib/useCurrentPermissions';
2221import WarningBox from 'components/WarningBox';
@@ -46,7 +45,7 @@ function useSetPoolSize() {
4645}
4746
4847export default function PartySetPoolSize() {
49− const { pop } = useHistory();
48+ const { pop } = useLocalRouter();
5049 const { contracts } = useNetwork();
5150
5251 const _contracts = need.contracts(contracts);
@@ -88,9 +87,8 @@ export default function PartySetPoolSize() {
8887 }, [poolOwner, construct, validPoint, validPoolSize, poolSize, unconstruct]);
8988
9089 return (
91− <View inset>
90+ <View pop={pop} inset>
9291 <Grid>
93− <Grid.Item full as={MiniBackButton} onClick={() => pop()} />
9492 <Grid.Item full as={ViewHeader}>
9593 Set Pool Size
9694 </Grid.Item>
src/views/Point.js+3−3
@@ -5,7 +5,6 @@ import { Grid } from 'indigo-react';
55import { azimuth } from 'azimuth-js';
66
77import { usePointCursor } from 'store/pointCursor';
8−import { useHistory } from 'store/history';
98
109import View from 'components/View';
1110import Passport from 'components/Passport';
@@ -15,9 +14,10 @@ import { matchBlinky } from 'components/Blinky';
1514import useInvites from 'lib/useInvites';
1615import { useSyncOwnedPoints } from 'lib/useSyncPoints';
1716import useCurrentPermissions from 'lib/useCurrentPermissions';
17+import { useLocalRouter } from 'lib/LocalRouter';
1818
1919export default function Point() {
20− const { push, names } = useHistory();
20+ const { pop, push, names } = useLocalRouter();
2121 const { pointCursor } = usePointCursor();
2222
2323 const point = need.point(pointCursor);
@@ -86,7 +86,7 @@ export default function Point() {
8686 useSyncOwnedPoints([point]);
8787
8888 return (
89− <View>
89+ <View pop={pop} inset>
9090 <Passport point={Just(point)} />
9191 <Grid className="pt2">
9292 {inviteButton}
src/views/Points.js+56−28
@@ -1,6 +1,7 @@
1−import React from 'react';
1+import React, { useCallback } from 'react';
22import { Just, Nothing } from 'folktale/maybe';
33import { Grid, H5, H1, HelpText, LinkButton, Flex } from 'indigo-react';
4+import { get } from 'lodash';
45
56import { useHistory } from 'store/history';
67import { useWallet } from 'store/wallet';
@@ -8,34 +9,33 @@ import { usePointCache } from 'store/pointCache';
89import { usePointCursor } from 'store/pointCursor';
910
1011import * as need from 'lib/need';
11−import { ROUTE_NAMES } from 'lib/routeNames';
1212import { isZeroAddress, abbreviateAddress } from 'lib/wallet';
1313import useIsEclipticOwner from 'lib/useIsEclipticOwner';
1414import { useSyncKnownPoints } from 'lib/useSyncPoints';
1515
1616import View from 'components/View';
17−import FooterButton from 'components/FooterButton';
1817import Blinky from 'components/Blinky';
1918import Passport from 'components/Passport';
2019import useRejectedIncomingPointTransfers from 'lib/useRejectedIncomingPointTransfers';
2120import pluralize from 'lib/pluralize';
21+import Footer from 'components/Footer';
22+import { ForwardButton } from 'components/Buttons';
2223
23−const getFromMaybe = (obj, key, defaultValue) =>
24+const maybeGetResult = (obj, key, defaultValue) =>
2425 obj.matchWith({
2526 Nothing: () => defaultValue,
2627 Just: p =>
2728 p.value.matchWith({
28− Ok: r => r.value[key],
29+ Ok: r => get(r.value, key, defaultValue),
2930 Error: e => defaultValue,
3031 }),
3132 });
3233
33−const hasTransferProxy = (details, point) =>
34− !isZeroAddress(details.transferProxy);
34+const hasTransferProxy = details => !isZeroAddress(details.transferProxy);
3535
3636const PointList = function({ points, className, actions, ...rest }) {
3737 const { setPointCursor } = usePointCursor();
38− const history = useHistory();
38+ const { push, names } = useHistory();
3939
4040 return (
4141 <Grid gap={3} className={className}>
@@ -47,7 +47,7 @@ const PointList = function({ points, className, actions, ...rest }) {
4747 className="clickable"
4848 onClick={() => {
4949 setPointCursor(Just(point));
50− history.push(history.names.POINT);
50+ push(names.POINT);
5151 }}
5252 {...rest}
5353 />
@@ -79,7 +79,7 @@ function ActionButtons({ actions = [] }) {
7979
8080export default function Points() {
8181 const { wallet } = useWallet();
82− const history = useHistory();
82+ const { pop, push, names } = useHistory();
8383 const { setPointCursor } = usePointCursor();
8484 const { controlledPoints, getDetails } = usePointCache();
8585 const isEclipticOwner = useIsEclipticOwner();
@@ -92,15 +92,15 @@ export default function Points() {
9292
9393 const loading = Nothing.hasInstance(controlledPoints);
9494
95− const ownedPoints = getFromMaybe(controlledPoints, 'ownedPoints', []);
96− const incomingPoints = getFromMaybe(
95+ const ownedPoints = maybeGetResult(controlledPoints, 'ownedPoints', []);
96+ const incomingPoints = maybeGetResult(
9797 controlledPoints,
9898 'incomingPoints',
9999 []
100100 ).filter(point => !rejectedPoints.includes(point));
101− const managingPoints = getFromMaybe(controlledPoints, 'managingPoints', []);
102− const votingPoints = getFromMaybe(controlledPoints, 'votingPoints', []);
103− const spawningPoints = getFromMaybe(controlledPoints, 'spawningPoints', []);
101+ const managingPoints = maybeGetResult(controlledPoints, 'managingPoints', []);
102+ const votingPoints = maybeGetResult(controlledPoints, 'votingPoints', []);
103+ const spawningPoints = maybeGetResult(controlledPoints, 'spawningPoints', []);
104104 const outgoingPoints = ownedPoints.filter(point =>
105105 getDetails(point).matchWith({
106106 Nothing: () => false,
@@ -115,7 +115,8 @@ export default function Points() {
115115 ...spawningPoints,
116116 ];
117117
118− const isEmpty = incomingPoints.length === 0 && multipassPoints.length === 0;
118+ const displayEmptyState =
119+ !loading && incomingPoints.length === 0 && multipassPoints.length === 0;
119120
120121 // sync display details for known points
121122 useSyncKnownPoints([
@@ -126,9 +127,19 @@ export default function Points() {
126127 ...spawningPoints,
127128 ]);
128129
130+ const goCreateGalaxy = useCallback(() => push(names.CREATE_GALAXY), [
131+ names.CREATE_GALAXY,
132+ push,
133+ ]);
134+
135+ const goViewPoint = useCallback(() => push(names.VIEW_POINT), [
136+ names.VIEW_POINT,
137+ push,
138+ ]);
139+
129140 return (
130− <View>
131− <Grid className="mt6">
141+ <View pop={pop} inset>
142+ <Grid>
132143 <Grid.Item full as={H1} className="f6 mono gray4 mb4">
133144 {abbreviateAddress(address)}
134145 </Grid.Item>
@@ -139,7 +150,7 @@ export default function Points() {
139150 </Grid.Item>
140151 )}
141152
142− {isEmpty && (
153+ {displayEmptyState && (
143154 <Grid.Item full as={HelpText} className="mt8 t-center">
144155 No points to display. This wallet is not the owner or proxy for any
145156 points.
@@ -166,7 +177,7 @@ export default function Points() {
166177 text: 'Accept',
167178 onClick: () => {
168179 setPointCursor(Just(point));
169− history.push(history.names.ACCEPT_TRANSFER);
180+ push(names.ACCEPT_TRANSFER);
170181 },
171182 },
172183 {
@@ -204,7 +215,7 @@ export default function Points() {
204215 onClick: () => {
205216 setPointCursor(Just(point));
206217 // TODO: deep linking to fix this duplicate route
207− history.push(history.names.CANCEL_TRANSFER);
218+ push(names.CANCEL_TRANSFER);
208219 },
209220 },
210221 ]}
@@ -224,13 +235,30 @@ export default function Points() {
224235 </Grid.Item>
225236 )}
226237
227− {isEclipticOwner && (
228− <FooterButton
229− detail="You have the authority to create a new Galaxy."
230− onClick={() => history.push(ROUTE_NAMES.CREATE_GALAXY)}>
231− Create a galaxy
232− </FooterButton>
233− )}
238+ <Footer>
239+ <Grid>
240+ <Grid.Divider />
241+ {isEclipticOwner && (
242+ <>
243+ <Grid.Item
244+ full
245+ as={ForwardButton}
246+ detail="You have the authority to create a new Galaxy."
247+ onClick={goCreateGalaxy}>
248+ Create a galaxy
249+ </Grid.Item>
250+ <Grid.Divider />
251+ </>
252+ )}
253+ <Grid.Item
254+ full
255+ as={ForwardButton}
256+ detail="View a point"
257+ onClick={goViewPoint}>
258+ View a point
259+ </Grid.Item>
260+ </Grid>
261+ </Footer>
234262 </Grid>
235263 </View>
236264 );
src/views/ViewPoint.js+27−19
@@ -1,19 +1,19 @@
11import React, { useCallback } from 'react';
22import { Just } from 'folktale/maybe';
3−import { Input, H1, P } from 'indigo-react';
3+import { Input, Grid, Text } from 'indigo-react';
44
55import { useHistory } from 'store/history';
66import { usePointCursor } from 'store/pointCursor';
77
8−import { ROUTE_NAMES } from 'lib/routeNames';
98import patp2dec from 'lib/patp2dec';
109import { usePointInput } from 'lib/useInputs';
1110
1211import View from 'components/View';
1312import { ForwardButton } from 'components/Buttons';
13+import ViewHeader from 'components/ViewHeader';
1414
1515export default function ViewPoint() {
16− const history = useHistory();
16+ const { pop, popAndPush, names } = useHistory();
1717 const { setPointCursor } = usePointCursor();
1818 const [pointInput, { data: pointName, pass }] = usePointInput({
1919 name: 'point',
@@ -23,24 +23,32 @@ export default function ViewPoint() {
2323 const disabled = !pass;
2424 const goForward = useCallback(() => {
2525 setPointCursor(Just(patp2dec(pointName)));
26− history.popAndPush(ROUTE_NAMES.POINT);
27− }, [setPointCursor, history, pointName]);
26+ popAndPush(names.POINT);
27+ }, [setPointCursor, pointName, popAndPush, names.POINT]);
2828
2929 return (
30− <View>
31− <H1>View a Point</H1>
32−
33− <P>Enter a point name to view its public information.</P>
34−
35− <Input {...pointInput} onEnter={goForward} />
36−
37− <ForwardButton
38− className="mt3"
39− disabled={disabled}
40− onClick={goForward}
41− solid>
42− Continue
43− </ForwardButton>
30+ <View pop={pop} inset>
31+ <Grid>
32+ <Grid.Item full as={ViewHeader}>
33+ View a Point
34+ </Grid.Item>
35+
36+ <Grid.Item full as={Text}>
37+ Enter a point name to view its public information.
38+ </Grid.Item>
39+
40+ <Grid.Item full as={Input} {...pointInput} onEnter={goForward} />
41+
42+ <Grid.Item
43+ full
44+ as={ForwardButton}
45+ className="mt3"
46+ disabled={disabled}
47+ onClick={goForward}
48+ solid>
49+ Continue
50+ </Grid.Item>
51+ </Grid>
4452 </View>
4553 );
4654}