on master help sign in
feat: implement local ignore list
766cfe4Matt Condon (shrugs)
src/components/InlineEthereumTransaction.js+1−1
@@ -51,7 +51,7 @@ export default function InlineEthereumTransaction({
51 const showConfigureInput = !(signed || broadcasted || confirmed || completed);
51 const showConfigureInput = !(signed || broadcasted || confirmed || completed);
52 // show the send/loading button while signed, broadcasting, or confirme
52 // show the send/loading button while signed, broadcasting, or confirme
53 const showBroadcastButton = signed || broadcasted || confirmed;
53 const showBroadcastButton = signed || broadcasted || confirmed;
54− const canBroadcast = signed;
54+ const canBroadcast = signed && !needFunds;
55 const isLoading = broadcasted || confirmed;
55 const isLoading = broadcasted || confirmed;
56 // show signed tx only when signing (for offline usage)
56 // show signed tx only when signing (for offline usage)
57 const showSignedTx = signed;
57 const showSignedTx = signed;
src/lib/routeNames.js−1
@@ -14,7 +14,6 @@ export const ROUTE_NAMES = {
14 PERMISSIONS: Symbol('PERMISSIONS'),
14 PERMISSIONS: Symbol('PERMISSIONS'),
15 //
15 //
16 ACCEPT_TRANSFER: Symbol('ACCEPT_TRANSFER'),
16 ACCEPT_TRANSFER: Symbol('ACCEPT_TRANSFER'),
17− REJECT_TRANSFER: Symbol('REJECT_TRANSFER'),
18 CANCEL_TRANSFER: Symbol('CANCEL_TRANSFER'),
17 CANCEL_TRANSFER: Symbol('CANCEL_TRANSFER'),
19 LANDING: Symbol('LANDING'),
18 LANDING: Symbol('LANDING'),
20 VIEW_POINT: Symbol('VIEW_POINT'),
19 VIEW_POINT: Symbol('VIEW_POINT'),
src/lib/router.js−2
@@ -14,7 +14,6 @@ import Activate from 'views/Activate';
14import PartySetPoolSize from 'views/Party/PartySetPoolSize';
14import PartySetPoolSize from 'views/Party/PartySetPoolSize';
15import AcceptTransfer from 'views/AcceptTransfer';
15import AcceptTransfer from 'views/AcceptTransfer';
16import CancelTransfer from 'views/CancelTransfer';
16import CancelTransfer from 'views/CancelTransfer';
17−import RejectTransfer from 'views/RejectTransfer';
18
17
19import { ROUTE_NAMES } from './routeNames';
18import { ROUTE_NAMES } from './routeNames';
20
19
@@ -33,7 +32,6 @@ export const ROUTES = {
33 // TODO: nest this route under the Party.js router
32 // TODO: nest this route under the Party.js router
34 [ROUTE_NAMES.PARTY_SET_POOL_SIZE]: PartySetPoolSize,
33 [ROUTE_NAMES.PARTY_SET_POOL_SIZE]: PartySetPoolSize,
35 [ROUTE_NAMES.ACCEPT_TRANSFER]: AcceptTransfer,
34 [ROUTE_NAMES.ACCEPT_TRANSFER]: AcceptTransfer,
36− [ROUTE_NAMES.REJECT_TRANSFER]: RejectTransfer,
37 // TODO: replace this with deep link to AdminCancelTransfer
35 // TODO: replace this with deep link to AdminCancelTransfer
38 [ROUTE_NAMES.CANCEL_TRANSFER]: CancelTransfer,
36 [ROUTE_NAMES.CANCEL_TRANSFER]: CancelTransfer,
39};
37};
src/lib/useEthereumTransaction.js+7−4
@@ -14,7 +14,6 @@ import {
14 hexify,
14 hexify,
15} from './txn';
15} from './txn';
16import * as need from './need';
16import * as need from './need';
17−import useLifecycle from './useLifecycle';
18import { ensureFundsFor } from './tank';
17import { ensureFundsFor } from './tank';
19import useDeepEqualReference from './useDeepEqualReference';
18import useDeepEqualReference from './useDeepEqualReference';
20
19
@@ -174,7 +173,6 @@ export default function useEthereumTransaction(
174 ]);
173 ]);
175
174
176 const reset = useCallback(() => {
175 const reset = useCallback(() => {
177− setUnsignedTransaction(undefined);
178 setTxHash(undefined);
176 setTxHash(undefined);
179 setSignedTransaction(undefined);
177 setSignedTransaction(undefined);
180 setGasPrice(suggestedGasPrice);
178 setGasPrice(suggestedGasPrice);
@@ -188,7 +186,12 @@ export default function useEthereumTransaction(
188 suggestedGasPrice,
186 suggestedGasPrice,
189 ]);
187 ]);
190
188
191− useLifecycle(() => {
189+ useEffect(() => {
190+ // if nonce or chainId is undefined, re-fetch on-chain info
191+ if (!(nonce === undefined || chainId === undefined)) {
192+ return;
193+ }
194+
192 (async () => {
195 (async () => {
193 try {
196 try {
194 setError(undefined);
197 setError(undefined);
@@ -207,7 +210,7 @@ export default function useEthereumTransaction(
207 setError(error);
210 setError(error);
208 }
211 }
209 })();
212 })();
210− });
213+ }, [_wallet, _web3, setError, nonce, chainId]);
211
214
212 useEffect(() => {
215 useEffect(() => {
213 let cancelled = false;
216 let cancelled = false;
src/lib/useLocalStorageState.jsnew file+38
@@ -0,0 +1,38 @@
1+import { useState, useEffect } from 'react';
2+
3+/**
4+ * useLocalStorageState is useState but it persists to localStorage
5+ *
6+ * inspired by:
7+ * https://github.com/streamich/react-use/blob/master/src/useLocalStorage.ts
8+ * https://usehooks.com/useLocalStorage/
9+ */
10+export default function useLocalStorageState(key, initialValue) {
11+ const [state, setState] = useState(() => {
12+ try {
13+ const value = localStorage.getItem(key);
14+ if (value === null) {
15+ localStorage.setItem(key, JSON.stringify(initialValue));
16+ return initialValue;
17+ } else {
18+ return JSON.parse(value);
19+ }
20+ } catch {
21+ // if user is in private mode or has storage restriction
22+ // localStorage can throw. JSON.parse and JSON.stringify
23+ // can throw, too.
24+ return initialValue;
25+ }
26+ });
27+
28+ useEffect(() => {
29+ try {
30+ localStorage.setItem(key, JSON.stringify(state));
31+ } catch {
32+ // if user is in private mode or has storage restriction
33+ // localStorage can throw. Also JSON.stringify can throw.
34+ }
35+ }, [key, state]);
36+
37+ return [state, setState];
38+}
src/lib/useRejectedIncomingPointTransfers.jsnew file+19
@@ -0,0 +1,19 @@
1+import { useCallback } from 'react';
2+
3+import useLocalStorageState from './useLocalStorageState';
4+
5+const REJECTED_POINTS_KEY = 'bridge:rejected-points';
6+
7+export default function useRejectedIncomingPointTransfers() {
8+ const [rejectedPoints, setRejectedPoints] = useLocalStorageState(
9+ REJECTED_POINTS_KEY,
10+ []
11+ );
12+
13+ const addRejectedPoint = useCallback(
14+ point => setRejectedPoints([...rejectedPoints, point]),
15+ [rejectedPoints, setRejectedPoints]
16+ );
17+
18+ return [rejectedPoints, addRejectedPoint];
19+}
src/views/Points.js+11−3
@@ -17,6 +17,7 @@ import View from 'components/View';
17import FooterButton from 'components/FooterButton';
17import FooterButton from 'components/FooterButton';
18import Blinky from 'components/Blinky';
18import Blinky from 'components/Blinky';
19import Passport from 'components/Passport';
19import Passport from 'components/Passport';
20+import useRejectedIncomingPointTransfers from 'lib/useRejectedIncomingPointTransfers';
20
21
21const getFromMaybe = (obj, key, defaultValue) =>
22const getFromMaybe = (obj, key, defaultValue) =>
22 obj.matchWith({
23 obj.matchWith({
@@ -81,13 +82,21 @@ export default function Points() {
81 const { setPointCursor } = usePointCursor();
82 const { setPointCursor } = usePointCursor();
82 const { controlledPoints, getDetails } = usePointCache();
83 const { controlledPoints, getDetails } = usePointCache();
83 const isEclipticOwner = useIsEclipticOwner();
84 const isEclipticOwner = useIsEclipticOwner();
85+ const [
86+ rejectedPoints,
87+ addRejectedPoint,
88+ ] = useRejectedIncomingPointTransfers();
84
89
85 const address = need.addressFromWallet(wallet);
90 const address = need.addressFromWallet(wallet);
86
91
87 const loading = Nothing.hasInstance(controlledPoints);
92 const loading = Nothing.hasInstance(controlledPoints);
88
93
89 const ownedPoints = getFromMaybe(controlledPoints, 'ownedPoints', []);
94 const ownedPoints = getFromMaybe(controlledPoints, 'ownedPoints', []);
90− const incomingPoints = getFromMaybe(controlledPoints, 'incomingPoints', []);
95+ const incomingPoints = getFromMaybe(
96+ controlledPoints,
97+ 'incomingPoints',
98+ []
99+ ).filter(point => !rejectedPoints.includes(point));
91 const managingPoints = getFromMaybe(controlledPoints, 'managingPoints', []);
100 const managingPoints = getFromMaybe(controlledPoints, 'managingPoints', []);
92 const votingPoints = getFromMaybe(controlledPoints, 'votingPoints', []);
101 const votingPoints = getFromMaybe(controlledPoints, 'votingPoints', []);
93 const spawningPoints = getFromMaybe(controlledPoints, 'spawningPoints', []);
102 const spawningPoints = getFromMaybe(controlledPoints, 'spawningPoints', []);
@@ -149,8 +158,7 @@ export default function Points() {
149 {
158 {
150 text: 'Reject',
159 text: 'Reject',
151 onClick: () => {
160 onClick: () => {
152− setPointCursor(Just(point));
161+ addRejectedPoint(point);
153− history.push(history.names.REJECT_TRANSFER);
154 },
162 },
155 },
163 },
156 ]}
164 ]}
src/views/RejectTransfer.jsdeleted−93
@@ -1,93 +0,0 @@
1−import React, { useCallback } from 'react';
2−import cn from 'classnames';
3−import { Grid, Text } from 'indigo-react';
4−import * as azimuth from 'azimuth-js';
5−
6−import { useNetwork } from 'store/network';
7−import { usePointCursor } from 'store/pointCursor';
8−import { usePointCache } from 'store/pointCache';
9−import { useHistory } from 'store/history';
10−
11−import * as need from 'lib/need';
12−import useCurrentPointName from 'lib/useCurrentPointName';
13−import useEthereumTransaction from 'lib/useEthereumTransaction';
14−import { GAS_LIMITS } from 'lib/constants';
15−import { ETH_ZERO_ADDR } from 'lib/wallet';
16−import useLifecycle from 'lib/useLifecycle';
17−
18−import ViewHeader from 'components/ViewHeader';
19−import MiniBackButton from 'components/MiniBackButton';
20−import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
21−import View from 'components/View';
22−
23−function useRejectTransfer() {
24− const { contracts } = useNetwork();
25− const { pointCursor } = usePointCursor();
26− const { syncDetails, syncControlledPoints } = usePointCache();
27−
28− const _contracts = need.contracts(contracts);
29− const _point = need.point(pointCursor);
30−
31− const { construct, ...rest } = useEthereumTransaction(
32− useCallback(
33− () =>
34− azimuth.ecliptic.setTransferProxy(_contracts, _point, ETH_ZERO_ADDR),
35− [_contracts, _point]
36− ),
37− useCallback(
38− () => Promise.all([syncDetails(_point), syncControlledPoints()]),
39− [_point, syncControlledPoints, syncDetails]
40− ),
41− GAS_LIMITS.SET_PROXY
42− );
43−
44− useLifecycle(() => {
45− construct();
46− });
47−
48− return {
49− ...rest,
50− };
51−}
52−
53−export default function RejectTransfer() {
54− const { pop } = useHistory();
55− const { getDetails } = usePointCache();
56− const { pointCursor } = usePointCursor();
57− const _point = need.point(pointCursor);
58−
59− const name = useCurrentPointName();
60− const _details = need.details(getDetails(_point));
61−
62− const { completed, bind } = useRejectTransfer();
63−
64− return (
65− <View inset>
66− <Grid>
67− <Grid.Item full as={MiniBackButton} onClick={() => pop()} />
68−
69− <Grid.Item full as={ViewHeader}>
70− Reject Incoming Transfer
71− </Grid.Item>
72−
73− <Grid.Item
74− full
75− as={Text}
76− className={cn('f5', {
77− green3: completed,
78− })}>
79− {completed
80− ? `The incoming transfer of ${name} has been rejected.`
81− : `Reject the incoming transfer of ${name} from ${_details.owner}.`}
82− </Grid.Item>
83−
84− <Grid.Item
85− full
86− as={InlineEthereumTransaction}
87− {...bind}
88− onReturn={() => pop()}
89− />
90− </Grid>
91− </View>
92− );
93−}