on master help sign in
Updating L1 pending transaction styling
fa856b9Will Galebach
src/components/InlineEthereumTransaction.js−1
@@ -144,7 +144,6 @@ export default function InlineEthereumTransaction({
144 className="eth-tx-button"
144 className="eth-tx-button"
145 full
145 full
146 as={RestartButton}
146 as={RestartButton}
147− center
148 onClick={onReturn}>
147 onClick={onReturn}>
149 Return
148 Return
150 </Grid.Item>
149 </Grid.Item>
src/components/L2/PendingTransaction.tsx−23
@@ -5,29 +5,6 @@ import LayerIndicator from './LayerIndicator';
5
5
6import './PendingTransaction.scss';
6import './PendingTransaction.scss';
7
7
8−export const getPendingTitle = (type?: string) =>
9− type === 'set-management-proxy'
10− ? 'Management Address Changed'
11− : type === 'configure-keys'
12− ? 'Network Keys Configured'
13− : type === 'transfer-point'
14− ? 'Point Transfered'
15− : type === 'set-transfer-proxy'
16− ? 'Transfer Proxy Changed'
17− : type === 'set-spawn-proxy'
18− ? 'Spawn Proxy Changed'
19− : type === 'escape'
20− ? 'Change Sponsor'
21− : type === 'cancel-escape'
22− ? 'Cancel Escape'
23− : type === 'adopt'
24− ? 'Accept Sponsorship'
25− : type === 'reject'
26− ? 'Reject Sponsorship'
27− : type === 'detach'
28− ? 'Detach Sponsee'
29− : '';
30−
31export const getPendingL1Title = (type?: L1TxnType) =>
8export const getPendingL1Title = (type?: L1TxnType) =>
32 type === L1TxnType.setNetworkKeys
9 type === L1TxnType.setNetworkKeys
33 ? 'Network Keys Configured'
10 ? 'Network Keys Configured'
src/components/NetworkKeys.js+3−3
@@ -14,7 +14,7 @@ const renderNetworkKey = key => {
14 const segments = segmentNetworkKey(key);
14 const segments = segmentNetworkKey(key);
15
15
16 if (!segments) {
16 if (!segments) {
17− return 'Unset';
17+ return 'Not set';
18 }
18 }
19
19
20 return segments.join('\n');
20 return segments.join('\n');
@@ -40,7 +40,7 @@ export default function NetworkKeys({ point }) {
40 {key.matchWith({
40 {key.matchWith({
41 Nothing: () => (
41 Nothing: () => (
42 <Grid.Item full as="code" className="f5 mono gray4">
42 <Grid.Item full as="code" className="f5 mono gray4">
43− Unset
43+ Not set
44 </Grid.Item>
44 </Grid.Item>
45 ),
45 ),
46 Just: ({ value: key }) => (
46 Just: ({ value: key }) => (
@@ -65,7 +65,7 @@ export default function NetworkKeys({ point }) {
65 {value.matchWith({
65 {value.matchWith({
66 Nothing: () => (
66 Nothing: () => (
67 <Flex.Item as={H5} className="f5 gray4">
67 <Flex.Item as={H5} className="f5 gray4">
68− Unset
68+ Not set
69 </Flex.Item>
69 </Flex.Item>
70 ),
70 ),
71 Just: ({ value }) => <Flex.Item as={H5}>{value}</Flex.Item>,
71 Just: ({ value }) => <Flex.Item as={H5}>{value}</Flex.Item>,
src/form/formatters.ts+14−12
@@ -12,14 +12,14 @@ export const buildFormatter = (formatters = []) =>
12/**
12/**
13 * inserts a ~ or - before every pat block
13 * inserts a ~ or - before every pat block
14 */
14 */
15−export const ensurePatFormat = s => {
15+export const ensurePatFormat = (s?: string) => {
16 // bail if falsy or empty string, nothing to do
16 // bail if falsy or empty string, nothing to do
17 if (!s || s.length === 0) {
17 if (!s || s.length === 0) {
18 return s || '';
18 return s || '';
19 }
19 }
20
20
21 // ensure there's a dash every 6 + 1 characters
21 // ensure there's a dash every 6 + 1 characters
22− const dashAt = i => i * (PAT_BLOCK_CHAR_LENGTH + 1);
22+ const dashAt = (i: number) => i * (PAT_BLOCK_CHAR_LENGTH + 1);
23 const maxBlocks = TICKET_MAX_BYTE_LEN / 2;
23 const maxBlocks = TICKET_MAX_BYTE_LEN / 2;
24
24
25 // for every index that may need a separator
25 // for every index that may need a separator
@@ -41,7 +41,7 @@ export const ensurePatFormat = s => {
41 return s;
41 return s;
42};
42};
43
43
44−export const ensureHexPrefix = s => {
44+export const ensureHexPrefix = (s?: string) => {
45 if (!s || s.length < HEX_PREFIX.length) {
45 if (!s || s.length < HEX_PREFIX.length) {
46 return s || '';
46 return s || '';
47 }
47 }
@@ -49,7 +49,7 @@ export const ensureHexPrefix = s => {
49 return s.indexOf(HEX_PREFIX) !== 0 ? `${HEX_PREFIX}${s}` : s;
49 return s.indexOf(HEX_PREFIX) !== 0 ? `${HEX_PREFIX}${s}` : s;
50};
50};
51
51
52−export const convertToNumber = s => {
52+export const convertToNumber = (s?: string) => {
53 if (!s) {
53 if (!s) {
54 return s || '';
54 return s || '';
55 }
55 }
@@ -61,7 +61,7 @@ export const convertToNumber = s => {
61 }
61 }
62};
62};
63
63
64−export const downcase = s => {
64+export const downcase = (s?: string) => {
65 if (!s) {
65 if (!s) {
66 return s;
66 return s;
67 }
67 }
@@ -69,7 +69,7 @@ export const downcase = s => {
69 return s.toLowerCase();
69 return s.toLowerCase();
70};
70};
71
71
72−export const stripHexPrefix = s => {
72+export const stripHexPrefix = (s?: string) => {
73 if (!s) {
73 if (!s) {
74 return s;
74 return s;
75 }
75 }
@@ -79,7 +79,7 @@ export const stripHexPrefix = s => {
79 : s;
79 : s;
80};
80};
81
81
82−export const ensureSigPrefix = s => {
82+export const ensureSigPrefix = (s?: string) => {
83 if (!s || s.length < SIG_PREFIX.length) {
83 if (!s || s.length < SIG_PREFIX.length) {
84 return s || '';
84 return s || '';
85 }
85 }
@@ -87,7 +87,7 @@ export const ensureSigPrefix = s => {
87 return s.indexOf(SIG_PREFIX) !== 0 ? `${SIG_PREFIX}${s}` : s;
87 return s.indexOf(SIG_PREFIX) !== 0 ? `${SIG_PREFIX}${s}` : s;
88};
88};
89
89
90−export const stripSigPrefix = s => {
90+export const stripSigPrefix = (s: string) => {
91 if (!s) {
91 if (!s) {
92 return s;
92 return s;
93 }
93 }
@@ -113,9 +113,11 @@ export const capitalize = (s: string) => {
113 return s.charAt(0).toUpperCase() + s.slice(1);
113 return s.charAt(0).toUpperCase() + s.slice(1);
114};
114};
115
115
116−export const titleize = (s: string): string => {
116+export const titleize = (s?: string): string => {
117 return s
117 return s
118+ ? s
118− .split(' ')
119+ .split(' ')
119− .map(_s => capitalize(_s))
120+ .map(_s => capitalize(_s))
120− .join(' ');
121+ .join(' ')
122+ : '';
121};
123};
src/lib/constants.ts+43−3
@@ -128,7 +128,14 @@ const INVITES_PER_PAGE = 7;
128const DEFAULT_NUM_INVITES = 5;
128const DEFAULT_NUM_INVITES = 5;
129const DEFAULT_CSV_NAME = 'urbit_invites.csv';
129const DEFAULT_CSV_NAME = 'urbit_invites.csv';
130
130
131−const TRANSACTION_STATUS_ICONS = {
131+const TRANSACTION_STATUS_ICONS: {
132+ [key: string]:
133+ | 'Checkmark'
134+ | 'ArrowNorth'
135+ | 'Clock'
136+ | 'ExclaimationMark'
137+ | 'NullIcon';
138+} = {
132 confirmed: 'Checkmark',
139 confirmed: 'Checkmark',
133 pending: 'Clock',
140 pending: 'Clock',
134 sending: 'ArrowNorth',
141 sending: 'ArrowNorth',
@@ -136,30 +143,63 @@ const TRANSACTION_STATUS_ICONS = {
136 unknown: 'NullIcon',
143 unknown: 'NullIcon',
137};
144};
138
145
139−const TRANSACTION_TYPE_ICONS = {
146+const TRANSACTION_TYPE_ICONS: {
147+ [key: string]:
148+ | 'ArrowRefresh'
149+ | 'BootNode'
150+ | 'Delete'
151+ | 'EjectedSponsor'
152+ | 'EscapeApproved'
153+ | 'EscapeRejected'
154+ | 'EscapeRequested'
155+ | 'ShipSpawned'
156+ | 'Swap';
157+} = {
140 adopt: 'EscapeApproved',
158 adopt: 'EscapeApproved',
141 'cancel-escape': 'EscapeRejected',
159 'cancel-escape': 'EscapeRejected',
160+ 'cancel-transfer': 'Delete',
142 'configure-keys': 'BootNode',
161 'configure-keys': 'BootNode',
143 detach: 'EjectedSponsor',
162 detach: 'EjectedSponsor',
144 escape: 'EscapeRequested',
163 escape: 'EscapeRequested',
164+ migrate: 'Swap',
145 reject: 'EscapeRejected',
165 reject: 'EscapeRejected',
146 'set-management-proxy': 'ArrowRefresh',
166 'set-management-proxy': 'ArrowRefresh',
147 'set-spawn-proxy': 'ArrowRefresh',
167 'set-spawn-proxy': 'ArrowRefresh',
148 'set-transfer-proxy': 'ArrowRefresh',
168 'set-transfer-proxy': 'ArrowRefresh',
169+ 'set-voting-proxy': 'ArrowRefresh',
149 spawn: 'ShipSpawned',
170 spawn: 'ShipSpawned',
150 'transfer-point': 'Swap',
171 'transfer-point': 'Swap',
151};
172};
152
173
153−const TRANSACTION_TYPE_TITLES = {
174+const TRANSACTION_TYPE_TITLES: {
175+ [key: string]:
176+ | 'Ship Sponsored'
177+ | 'Escape Canceled'
178+ | 'Transfer Canceled'
179+ | 'Network Keys Configured'
180+ | 'Sponsee Detached'
181+ | 'Escaped Sponsor'
182+ | 'Migrating to Layer 2'
183+ | 'Sponsorship Rejected'
184+ | 'Management Proxy Changed'
185+ | 'Spawn Proxy Changed'
186+ | 'Transfer Proxy Changed'
187+ | 'Voting Proxy Changed'
188+ | 'Ship Spawned'
189+ | 'Ship Transferred';
190+} = {
154 adopt: 'Ship Sponsored',
191 adopt: 'Ship Sponsored',
155 'cancel-escape': 'Escape Canceled',
192 'cancel-escape': 'Escape Canceled',
193+ 'cancel-transfer': 'Transfer Canceled',
156 'configure-keys': 'Network Keys Configured',
194 'configure-keys': 'Network Keys Configured',
157 detach: 'Sponsee Detached',
195 detach: 'Sponsee Detached',
158 escape: 'Escaped Sponsor',
196 escape: 'Escaped Sponsor',
197+ migrate: 'Migrating to Layer 2',
159 reject: 'Sponsorship Rejected',
198 reject: 'Sponsorship Rejected',
160 'set-management-proxy': 'Management Proxy Changed',
199 'set-management-proxy': 'Management Proxy Changed',
161 'set-spawn-proxy': 'Spawn Proxy Changed',
200 'set-spawn-proxy': 'Spawn Proxy Changed',
162 'set-transfer-proxy': 'Transfer Proxy Changed',
201 'set-transfer-proxy': 'Transfer Proxy Changed',
202+ 'set-voting-proxy': 'Voting Proxy Changed',
163 spawn: 'Ship Spawned',
203 spawn: 'Ship Spawned',
164 'transfer-point': 'Ship Transferred',
204 'transfer-point': 'Ship Transferred',
165};
205};
src/lib/router.js+1−1
@@ -21,7 +21,7 @@ import IssueChild from 'views/Point/IssueChild';
21import PartySetPoolSize from 'views/Party/PartySetPoolSize';
21import PartySetPoolSize from 'views/Party/PartySetPoolSize';
22import InviteCohort from 'views/Invite/Cohort';
22import InviteCohort from 'views/Invite/Cohort';
23import AcceptTransfer from 'views/AcceptTransfer.tsx';
23import AcceptTransfer from 'views/AcceptTransfer.tsx';
24−import CancelTransfer from 'views/CancelTransfer';
24+import CancelTransfer from 'views/CancelTransfer.tsx';
25import Bitcoin from 'views/Bitcoin/Bitcoin';
25import Bitcoin from 'views/Bitcoin/Bitcoin';
26import Xpub from 'views/Bitcoin/Xpub';
26import Xpub from 'views/Bitcoin/Xpub';
27import SignTransaction from 'views/Bitcoin/SignTransaction';
27import SignTransaction from 'views/Bitcoin/SignTransaction';
src/lib/types/PendingL1Transaction.ts+10−8
@@ -1,13 +1,14 @@
1export enum L1TxnType {
1export enum L1TxnType {
2 migrate = 'migrate',
2 migrate = 'migrate',
3− migrateSpawn = 'migrateSpawn',
3+ migrateSpawn = 'migrate-spawn',
4− spawnProxy = 'spawnProxy',
4+ spawnProxy = 'set-spawn-proxy',
5− managementProxy = 'managementProxy',
5+ managementProxy = 'set-management-proxy',
6− transferProxy = 'transferProxy',
6+ transferProxy = 'set-transfer-proxy',
7− votingProxy = 'votingProxy',
7+ votingProxy = 'set-voting-proxy',
8− acceptTransfer = 'acceptTransfer',
8+ acceptTransfer = 'transfer-point',
9− spawnPoint = 'spawnPoint',
9+ cancelTransfer = 'cancel-transfer',
10+ spawn = 'spawn',
10− setNetworkKeys = 'setNetworkKeys',
11+ setNetworkKeys = 'configure-keys',
11}
12}
12
13
13export interface PendingL1Txn {
14export interface PendingL1Txn {
@@ -15,6 +16,7 @@ export interface PendingL1Txn {
15 point: number;
16 point: number;
16 type: L1TxnType;
17 type: L1TxnType;
17 hash: string;
18 hash: string;
19+ time: number;
18}
20}
19
21
20export interface PendingL1 {
22export interface PendingL1 {
src/views/AcceptTransfer.tsx+17−2
@@ -14,6 +14,7 @@ import useCurrentPointName from 'lib/useCurrentPointName';
14import useEthereumTransaction from 'lib/useEthereumTransaction';
14import useEthereumTransaction from 'lib/useEthereumTransaction';
15import { GAS_LIMITS } from 'lib/constants';
15import { GAS_LIMITS } from 'lib/constants';
16import { useLocalRouter } from 'lib/LocalRouter';
16import { useLocalRouter } from 'lib/LocalRouter';
17+import { L1TxnType } from 'lib/types/PendingL1Transaction';
17
18
18import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
19import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
19import View from 'components/View';
20import View from 'components/View';
@@ -40,7 +41,7 @@ function useAcceptTransfer() {
40
41
41 const transaction = useEthereumTransaction(
42 const transaction = useEthereumTransaction(
42 useCallback(
43 useCallback(
43− reset =>
44+ (reset: boolean) =>
44 azimuth.ecliptic.transferPoint(_contracts, _point, _address, reset),
45 azimuth.ecliptic.transferPoint(_contracts, _point, _address, reset),
45 [_contracts, _point, _address]
46 [_contracts, _point, _address]
46 ),
47 ),
@@ -69,13 +70,27 @@ export default function AcceptTransfer() {
69
70
70 const [reset, setReset] = useState(true);
71 const [reset, setReset] = useState(true);
71
72
73+ const {
74+ completed,
75+ bind,
76+ inputsLocked,
77+ construct,
78+ txHashes,
72− const { completed, bind, inputsLocked, construct } = useAcceptTransfer();
79+ } = useAcceptTransfer();
73
80
74 useEffect(() => {
81 useEffect(() => {
82+ // Update this one
75 if (completed) {
83 if (completed) {
76 checkForUpdates({
84 checkForUpdates({
77 point: point.value,
85 point: point.value,
78 message: `${point.patp} has been transferred to you!`,
86 message: `${point.patp} has been transferred to you!`,
87+ l1Txn: {
88+ id: `accept-transfer-${point.value}`,
89+ point: point.value,
90+ type: L1TxnType.cancelTransfer,
91+ hash: txHashes[0],
92+ time: new Date().getTime(),
93+ },
79 });
94 });
80 }
95 }
81 }, [completed]); // eslint-disable-line react-hooks/exhaustive-deps
96 }, [completed]); // eslint-disable-line react-hooks/exhaustive-deps
src/views/CancelTransfer.js → src/views/CancelTransfer.tsxrenamed+19−13
@@ -3,7 +3,6 @@ import cn from 'classnames';
3import { Grid, Text, Button } from 'indigo-react';
3import { Grid, Text, Button } from 'indigo-react';
4import * as azimuth from 'azimuth-js';
4import * as azimuth from 'azimuth-js';
5import { Nothing } from 'folktale/maybe';
5import { Nothing } from 'folktale/maybe';
6−import { Row } from '@tlon/indigo-react';
7
6
8import { useNetwork } from 'store/network';
7import { useNetwork } from 'store/network';
9import { usePointCursor } from 'store/pointCursor';
8import { usePointCursor } from 'store/pointCursor';
@@ -17,6 +16,7 @@ import useEthereumTransaction from 'lib/useEthereumTransaction';
17import { ETH_ZERO_ADDR, GAS_LIMITS } from 'lib/constants';
16import { ETH_ZERO_ADDR, GAS_LIMITS } from 'lib/constants';
18import useLifecycle from 'lib/useLifecycle';
17import useLifecycle from 'lib/useLifecycle';
19import useRoller from 'lib/useRoller';
18import useRoller from 'lib/useRoller';
19+import { L1TxnType } from 'lib/types/PendingL1Transaction';
20
20
21import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
21import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
22import View from 'components/View';
22import View from 'components/View';
@@ -26,9 +26,9 @@ import BodyPane from 'components/L2/Window/BodyPane';
26import L2BackHeader from 'components/L2/Headers/L2BackHeader';
26import L2BackHeader from 'components/L2/Headers/L2BackHeader';
27
27
28function useCancelTransfer() {
28function useCancelTransfer() {
29− const { contracts } = useNetwork();
29+ const { contracts }: any = useNetwork();
30− const { pointCursor } = usePointCursor();
30+ const { pointCursor }: any = usePointCursor();
31− const { syncDetails } = usePointCache();
31+ const { syncDetails }: any = usePointCache();
32
32
33 const _contracts = need.contracts(contracts);
33 const _contracts = need.contracts(contracts);
34 const _point = need.point(pointCursor);
34 const _point = need.point(pointCursor);
@@ -53,23 +53,30 @@ function useCancelTransfer() {
53}
53}
54
54
55function AdminCancelTransfer() {
55function AdminCancelTransfer() {
56− const { pop } = useLocalRouter();
56+ const { pop }: any = useLocalRouter();
57− const { getDetails } = usePointCache();
57+ const { getDetails }: any = usePointCache();
58 const { point } = useRollerStore();
58 const { point } = useRollerStore();
59 const { setProxyAddress, checkForUpdates } = useRoller();
59 const { setProxyAddress, checkForUpdates } = useRoller();
60− const { pointCursor, setPointCursor } = usePointCursor();
60+ const { pointCursor, setPointCursor }: any = usePointCursor();
61 const _point = need.point(pointCursor);
61 const _point = need.point(pointCursor);
62
62
63 const name = useCurrentPointName();
63 const name = useCurrentPointName();
64 const _details = need.details(getDetails(_point));
64 const _details = need.details(getDetails(_point));
65
65
66− const { completed, bind } = useCancelTransfer();
66+ const { completed, bind, txHashes } = useCancelTransfer();
67
67
68 useEffect(() => {
68 useEffect(() => {
69 if (completed) {
69 if (completed) {
70 checkForUpdates({
70 checkForUpdates({
71 point: point.value,
71 point: point.value,
72 message: `${point.patp} transfer cancelled`,
72 message: `${point.patp} transfer cancelled`,
73+ l1Txn: {
74+ id: `cancel-${point.value}`,
75+ point: point.value,
76+ type: L1TxnType.cancelTransfer,
77+ hash: txHashes[0],
78+ time: new Date().getTime(),
79+ },
73 });
80 });
74 }
81 }
75 }, [completed]); // eslint-disable-line react-hooks/exhaustive-deps
82 }, [completed]); // eslint-disable-line react-hooks/exhaustive-deps
@@ -94,14 +101,13 @@ function AdminCancelTransfer() {
94 return (
101 return (
95 <Window>
102 <Window>
96 <HeaderPane>
103 <HeaderPane>
97− <Row className="header-row">
98− <h5>Cancel Outgoing Transfer</h5>
104+ <h5>Cancel Outgoing Transfer</h5>
99− </Row>
100 </HeaderPane>
105 </HeaderPane>
101 <BodyPane>
106 <BodyPane>
102 <Grid.Item
107 <Grid.Item
103 full
108 full
104 as={Text}
109 as={Text}
110+ style={{ fontSize: 14 }}
105 className={cn('f5 wrap mb5', {
111 className={cn('f5 wrap mb5', {
106 green3: completed,
112 green3: completed,
107 })}>
113 })}>
@@ -139,8 +145,8 @@ function AdminCancelTransfer() {
139}
145}
140
146
141export default function CancelTransfer() {
147export default function CancelTransfer() {
142− const { pop } = useLocalRouter();
148+ const { pop }: any = useLocalRouter();
143− const { setPointCursor } = usePointCursor();
149+ const { setPointCursor }: any = usePointCursor();
144
150
145 const goBack = useCallback(() => {
151 const goBack = useCallback(() => {
146 setPointCursor(Nothing());
152 setPointCursor(Nothing());
src/views/Point/IssueChild.tsx+2−1
@@ -144,8 +144,9 @@ export default function IssueChild() {
144 l1Txn: {
144 l1Txn: {
145 id: `${pointToSpawn}`,
145 id: `${pointToSpawn}`,
146 point: point.value,
146 point: point.value,
147− type: L1TxnType.spawnPoint,
147+ type: L1TxnType.spawn,
148 hash: txHashes[0],
148 hash: txHashes[0],
149+ time: new Date().getTime(),
149 },
150 },
150 });
151 });
151 }
152 }
src/views/Point/MigrateL2.tsx+1
@@ -144,6 +144,7 @@ export default function MigrateL2() {
144 point: selectedPoint.value,
144 point: selectedPoint.value,
145 type: transfer ? L1TxnType.migrate : L1TxnType.migrateSpawn,
145 type: transfer ? L1TxnType.migrate : L1TxnType.migrateSpawn,
146 hash: txHashes[0],
146 hash: txHashes[0],
147+ time: new Date().getTime(),
147 },
148 },
148 });
149 });
149
150
src/views/Point/Point.tsx+17−11
@@ -23,10 +23,10 @@ import { useHasNetworkKeysSet } from 'lib/useHasNetworkKeysSet';
23import LoadingOverlay from 'components/L2/LoadingOverlay';
23import LoadingOverlay from 'components/L2/LoadingOverlay';
24import PendingTransaction, {
24import PendingTransaction, {
25 getPendingL1Title,
25 getPendingL1Title,
26− getPendingTitle,
27} from 'components/L2/PendingTransaction';
26} from 'components/L2/PendingTransaction';
28import Modal from 'components/L2/Modal';
27import Modal from 'components/L2/Modal';
29import './Point.scss';
28import './Point.scss';
29+import { TransactionRow } from 'views/Transactions/TransactionRow';
30
30
31export default function Point() {
31export default function Point() {
32 const { pop, push, names }: any = useLocalRouter();
32 const { pop, push, names }: any = useLocalRouter();
@@ -131,21 +131,27 @@ export default function Point() {
131 />
131 />
132 )}
132 )}
133 {!!otherPending.length &&
133 {!!otherPending.length &&
134− otherPending.map((pendingTx, ind) => (
134+ otherPending.map(pendingTx => (
135− <PendingTransaction
135+ <TransactionRow
136+ className="point-view"
137+ key={pendingTx.time}
138+ ship={point.patp}
139+ type={pendingTx.rawTx?.tx?.type}
136− key={`pending-tx-${ind}`}
140+ status="pending"
137− layer={2}
141+ time={pendingTx.time}
138− nextRoll={nextRoll}
139− title={getPendingTitle(pendingTx.rawTx?.tx?.type)}
140 />
142 />
141 ))}
143 ))}
142 {!!pendingL1Transactions.length &&
144 {!!pendingL1Transactions.length &&
143 pendingL1Transactions.map((pendingTx, ind) => (
145 pendingL1Transactions.map((pendingTx, ind) => (
144− <PendingTransaction
146+ <TransactionRow
147+ className="point-view"
148+ key={pendingTx.time}
149+ ship={point.patp}
150+ type={pendingTx.type as string}
145− key={`pending-tx-${ind}`}
151+ status="pending"
146− layer={1}
152+ time={pendingTx.time}
147 hash={pendingTx.hash}
153 hash={pendingTx.hash}
148− title={getPendingL1Title(pendingTx.type)}
154+ layer={1}
149 />
155 />
150 ))}
156 ))}
151 <Passport
157 <Passport
src/views/Point/UrbitID.js+1−1
@@ -14,7 +14,7 @@ import SigilGenerator from '../UrbitID/SigilGenerator';
14import DownloadKeys from '../UrbitID/DownloadKeys';
14import DownloadKeys from '../UrbitID/DownloadKeys';
15import SetProxy from '../UrbitID/SetProxy';
15import SetProxy from '../UrbitID/SetProxy';
16import ResetKeys from '../UrbitID/ResetKeys';
16import ResetKeys from '../UrbitID/ResetKeys';
17−import Transfer from '../UrbitID/Transfer';
17+import Transfer from '../UrbitID/Transfer.tsx';
18
18
19const NAMES = {
19const NAMES = {
20 HOME: 'HOME',
20 HOME: 'HOME',
src/views/Transactions/TransactionHistory.scss+190−163
@@ -91,216 +91,243 @@
91 display: none; /* Chrome */
91 display: none; /* Chrome */
92 }
92 }
93 }
93 }
94+}
94
95
95− .address-row {
96+.address-row {
96− @include tx-row();
97+ @include tx-row();
97
98
98− .icon {
99+ .icon {
99− grid-area: icon;
100+ grid-area: icon;
100− background-color: black;
101+ background-color: black;
101− border-radius: 2px;
102+ border-radius: 2px;
102− width: 24px;
103+ width: 24px;
103− height: 24px;
104+ height: 24px;
104− }
105+ }
105
106
106− .info {
107+ .info {
107− grid-area: info;
108+ grid-area: info;
108− display: flex;
109+ display: flex;
109− flex-flow: column nowrap;
110+ flex-flow: column nowrap;
110− justify-content: center;
111+ justify-content: center;
111
112
112− .address {
113+ .address {
113− font-family: Source Code Pro;
114+ font-family: Source Code Pro;
114− font-size: 14px;
115+ font-size: 14px;
115− font-style: normal;
116+ font-style: normal;
116− font-weight: 500;
117+ font-weight: 500;
117− line-height: 16px;
118+ line-height: 16px;
118− letter-spacing: 0em;
119+ letter-spacing: 0em;
119− }
120 }
120 }
121 }
121 }
122+}
122
123
123− .patp-row {
124+.patp-row {
124− @include tx-row();
125+ @include tx-row();
125
126
126− .icon {
127+ .icon {
127− grid-area: icon;
128+ grid-area: icon;
128−
129+
129− .sigil {
130+ .sigil {
130− background-color: black;
131+ background-color: black;
131− border-radius: 2px;
132+ border-radius: 2px;
132− padding: 6px;
133+ padding: 6px;
133− }
134 }
134 }
135+ }
135
136
136− .info {
137+ .info {
137− grid-area: info;
138+ grid-area: info;
138− display: flex;
139+ display: flex;
139− flex-flow: column nowrap;
140+ flex-flow: column nowrap;
140− justify-content: center;
141+ justify-content: center;
141
142
142− .patp {
143+ .patp {
143− font-family: Source Code Pro;
144+ font-family: Source Code Pro;
144− font-size: 14px;
145+ font-size: 14px;
145− font-style: normal;
146+ font-style: normal;
146− font-weight: 500;
147+ font-weight: 500;
147− line-height: 16px;
148+ line-height: 16px;
148− letter-spacing: 0em;
149+ letter-spacing: 0em;
149− }
150 }
150 }
151 }
151 }
152+}
153+
154+.transaction-row {
155+ @include tx-row();
152
156
153− .transaction-row {
157+ &.point-view {
154− @include tx-row();
158+ padding: 8px;
159+ border: 1px solid $light-gray;
160+ border-radius: $large-border-radius;
161+ margin-bottom: 16px;
162+ }
155
163
156− .icon {
164+ .icon {
157− grid-area: icon;
165+ grid-area: icon;
158− padding-top: 6px;
166+ padding-top: 6px;
159
167
160− svg {
168+ svg {
161− height: 16px;
169+ height: 16px;
162− width: 16px;
170+ width: 16px;
163− }
164 }
171 }
172+ }
165
173
166− .info {
174+ .info {
167− grid-area: info;
175+ grid-area: info;
176+ }
177+
178+ .title-row {
179+ display: flex;
180+ flex-direction: row;
181+ justify-content: space-between;
182+ align-items: center;
183+ margin-bottom: 8px;
184+
185+ .title {
186+ font-family: Inter;
187+ font-size: 14px;
188+ font-style: normal;
189+ font-weight: 400;
190+ line-height: 24px;
191+ letter-spacing: -0.011em;
168 }
192 }
169
193
170− .title-row {
194+ .status {
171− display: flex;
195+ .badge {
172− flex-direction: row;
173− justify-content: space-between;
174− align-items: center;
196+ align-items: center;
175− margin-bottom: 8px;
197+ background-color: black;
176−
198+ border-radius: 2px;
177− .title {
199+ color: white;
200+ display: flex;
201+ flex-flow: row nowrap;
178 font-family: Inter;
202 font-family: Inter;
179 font-size: 14px;
203 font-size: 14px;
180 font-style: normal;
204 font-style: normal;
181− font-weight: 400;
205+ font-weight: 500;
182− line-height: 24px;
206+ justify-content: space-between;
183− letter-spacing: -0.011em;
207+ letter-spacing: 0em;
208+ line-height: 18px;
209+ padding: 4px 8px;
210+ text-align: center;
211+ width: 78px;
212+
213+ svg {
214+ height: 14px;
215+ width: 14px;
216+ padding-top: 4px;
217+ path {
218+ fill: white;
219+ }
184− }
220+ }
185
221
186− .status {
222+ &.confirmed {
187− .badge {
223+ width: 92px;
188− align-items: center;
224+ background: #009F65;
225+ }
226+
227+ &.sending {
228+ width: 74px;
229+ background: #00000033;
189− background-color: black;
230+ color: black;
190− border-radius: 2px;
191− color: white;
192− display: flex;
193− flex-flow: row nowrap;
194− font-family: Inter;
195− font-size: 14px;
196− font-style: normal;
197− font-weight: 500;
198− justify-content: space-between;
199− letter-spacing: 0em;
200− line-height: 18px;
201− padding: 4px 8px;
202− text-align: center;
203− width: 78px;
204
231
205 svg {
232 svg {
206− height: 14px;
207− width: 14px;
208− padding-top: 4px;
209 path {
233 path {
210− fill: white;
234+ fill: black;
211 }
235 }
212 }
236 }
237+ }
213
238
214− &.confirmed {
239+ &.failed {
215− width: 92px;
216− background: #009F65;
240+ background-color: rgb(246,200,196);
217− }
241+ color: rgb(236,81,66);
218−
242+ width: 62px;
219− &.sending {
220− width: 74px;
221− background: #00000033;
222− color: black;
223
243
224− svg {
244+ span.icon-wrapper {
225− path {
245+ height: 16px;
226− fill: black;
246+ width: 16px;
227− }
247+ border-radius: 50%;
228− }
248+ background-color: #FF4136;
229 }
249 }
230
250
231− &.failed {
232− background-color: rgb(246,200,196);
233− color: rgb(236,81,66);
234− width: 62px;
235−
236− span.icon-wrapper {
237− height: 16px;
238− width: 16px;
239− border-radius: 50%;
240− background-color: #FF4136;
241− }
242−
243− svg {
251+ svg {
244− padding-top: 1px;
252+ padding-top: 1px;
245−
253+
246− path {
254+ path {
247− fill: white;
255+ fill: white;
248− }
249 }
256 }
250 }
257 }
258+ }
251
259
252− &.pending {
260+ &.pending {
253− background-color: $rollup-yellow;
261+ background-color: $rollup-yellow;
254− color: #000;
262+ color: #000;
255− width: 100px;
263+ width: 100px;
256− width: 105px;
264+ width: 105px;
257−
265+
258− svg {
266+ svg {
259− path {
267+ path {
260− fill: #000;
268+ fill: #000;
261− }
262 }
269 }
263 }
270 }
271+ }
264
272
265− &.unknown {
273+ &.unknown {
266− width: 82px;
274+ width: 82px;
267− }
268 }
275 }
269 }
276 }
270 }
277 }
271−
278+ }
279+
272− .info-row {
280+ .info-row {
281+ display: flex;
282+ flex-direction: row;
283+ justify-content: space-between;
284+ align-items: center;
285+
286+ .hash-container {
273 display: flex;
287 display: flex;
274− flex-direction: row;
288+ flex-flow: row nowrap;
275 justify-content: space-between;
289 justify-content: space-between;
276 align-items: center;
290 align-items: center;
291+ width: 31%;
277
292
278− .hash-container {
293+ .etherscan {
279− display: flex;
280− flex-flow: row nowrap;
281− justify-content: space-between;
282− align-items: center;
283− width: 31%;
284− }
285−
286− .hash {
287− font-family: Source Code Pro;
288 font-size: 14px;
294 font-size: 14px;
289− font-style: normal;
295+ color: $dark-gray;
290− font-weight: 400;
296+
291− line-height: 16px;
297+ svg {
292− letter-spacing: -0.011em;
298+ height: 18px;
293− color: rgba(0,0,0,0.6);
299+ width: 18px;
300+ margin-bottom: -4px;
301+ -ms-transform:rotate(315deg); /* IE9 */
302+ -moz-transform: rotate(315deg); /* FF3.5/3.6 */
303+ -o-transform: rotate(315deg); /* Opera 10.5 */
304+ -webkit-transform: rotate(315deg); /* Saf3.1+ */
305+ transform: rotate(315deg); /* Newer browsers */
306+
307+ path {
308+ fill: $dark-gray;
309+ }
310+ }
294 }
311 }
312+ }
295
313
296− .date {
314+ .hash {
297− font-family: Inter;
315+ font-family: Source Code Pro;
298− font-size: 14px;
316+ font-size: 14px;
299− font-style: normal;
317+ font-style: normal;
300− font-weight: 400;
318+ font-weight: 400;
301− line-height: 16px;
319+ line-height: 16px;
302− letter-spacing: -0.011em;
320+ letter-spacing: -0.011em;
303− }
321+ color: rgba(0,0,0,0.6);
304 }
322 }
323+
324+ .date {
325+ font-family: Inter;
326+ font-size: 14px;
327+ font-style: normal;
328+ font-weight: 400;
329+ line-height: 16px;
330+ letter-spacing: -0.011em;
305− }
331+ }
332+ }
306−}
333+}
src/views/Transactions/TransactionRow.tsx+32−9
@@ -15,14 +15,22 @@ import { abbreviateAddress } from 'lib/utils/address';
15import { isPlanet, isStar } from 'lib/utils/point';
15import { isPlanet, isStar } from 'lib/utils/point';
16import { titleize } from 'form/formatters';
16import { titleize } from 'form/formatters';
17import LayerIndicator from 'components/L2/LayerIndicator';
17import LayerIndicator from 'components/L2/LayerIndicator';
18+import { useExploreTxUrl } from 'lib/explorer';
19+
20+interface TransactionRowProps extends RollerTransaction {
21+ className?: string;
22+ layer?: 1 | 2;
23+}
18
24
19export const TransactionRow = ({
25export const TransactionRow = ({
26+ className = '',
20 ship,
27 ship,
21− type,
28+ type = '',
22 hash,
29 hash,
23− status,
30+ status = 'pending',
24 time,
31 time,
25−}: RollerTransaction) => {
32+ layer = 2,
33+}: TransactionRowProps) => {
26 const { nextRoll } = useTimerStore();
34 const { nextRoll } = useTimerStore();
27
35
28 // For spawn events (associated with the spawner), we want to show the spawnee tier;
36 // For spawn events (associated with the spawner), we want to show the spawnee tier;
@@ -70,7 +78,7 @@ export const TransactionRow = ({
70
78
71 const longDate = useMemo(() => {
79 const longDate = useMemo(() => {
72 if (!parsedDate) {
80 if (!parsedDate) {
73− return null;
81+ return;
74 }
82 }
75
83
76 return parsedDate.toISOString();
84 return parsedDate.toISOString();
@@ -82,13 +90,17 @@ export const TransactionRow = ({
82 <span className={'icon-wrapper'}>
90 <span className={'icon-wrapper'}>
83 <Icon icon={TRANSACTION_STATUS_ICONS[status] || 'Bug'} />
91 <Icon icon={TRANSACTION_STATUS_ICONS[status] || 'Bug'} />
84 </span>
92 </span>
93+ <span>
85− <span>{status === 'pending' ? nextRoll : titleize(status)}</span>
94+ {status === 'pending' && layer === 2 ? nextRoll : titleize(status)}
95+ </span>
86 </div>
96 </div>
87 );
97 );
88− }, [nextRoll, status]);
98+ }, [nextRoll, status, layer]);
99+
100+ const txHash = useExploreTxUrl(hash);
89
101
90 return (
102 return (
91− <Box className="transaction-row">
103+ <Box className={`transaction-row ${className}`}>
92 <Box className="icon">{icon}</Box>
104 <Box className="icon">{icon}</Box>
93 <Box className="info">
105 <Box className="info">
94 <Row className="title-row">
106 <Row className="title-row">
@@ -97,8 +109,19 @@ export const TransactionRow = ({
97 </Row>
109 </Row>
98 <Row className="info-row">
110 <Row className="info-row">
99 <Box className="hash-container">
111 <Box className="hash-container">
112+ {layer === 1 ? (
113+ <a
114+ className="etherscan"
115+ href={txHash}
116+ rel="noreferrer"
117+ target="_blank">
118+ Etherscan
119+ <Icon className="arrow" icon="ArrowEast" />
120+ </a>
121+ ) : (
100− <Box className="hash">{shortHash}</Box>
122+ !!hash && <Box className="hash">{shortHash}</Box>
123+ )}
101− <LayerIndicator layer={2} size={'sm'} />
124+ <LayerIndicator layer={layer} size={'sm'} />
102 </Box>
125 </Box>
103 <Box className="date" title={longDate}>
126 <Box className="date" title={longDate}>
104 {shortDate}
127 {shortDate}
src/views/UrbitID/Home.tsx+1−1
@@ -136,7 +136,7 @@ export default function UrbitIDHome() {
136 <Box>
136 <Box>
137 <Box>Spawn Proxy Address</Box>
137 <Box>Spawn Proxy Address</Box>
138 <div className="mt1 mono black subtitle">
138 <div className="mt1 mono black subtitle">
139− {noSpawn ? 'Unset' : abbreviateAddress(point.spawnProxy)}
139+ {noSpawn ? 'Not set' : abbreviateAddress(point.spawnProxy)}
140 </div>
140 </div>
141 </Box>
141 </Box>
142 <Row>
142 <Row>
src/views/UrbitID/SetProxy.js+2−1
@@ -198,6 +198,7 @@ export default function SetProxy() {
198 point: point.value,
198 point: point.value,
199 type: proxyTypeToL1TxnType(data.proxyType),
199 type: proxyTypeToL1TxnType(data.proxyType),
200 hash: txHashes[0],
200 hash: txHashes[0],
201+ time: new Date().getTime(),
201 },
202 },
202 });
203 });
203 }
204 }
@@ -289,7 +290,7 @@ export default function SetProxy() {
289 gray4: !completed && !isProxySet,
290 gray4: !completed && !isProxySet,
290 green3: completed,
291 green3: completed,
291 })}>
292 })}>
292− {isProxySet ? proxyAddress : 'Unset'}
293+ {isProxySet ? proxyAddress : 'Not set'}
293 </Flex.Item>
294 </Flex.Item>
294 </Grid.Item>
295 </Grid.Item>
295
296
src/views/UrbitID/Transfer.js → src/views/UrbitID/Transfer.tsxrenamed+50−24
@@ -1,8 +1,6 @@
1−import React, { useCallback, useMemo, useState } from 'react';
1+import React, { useCallback, useEffect, useMemo, useState } from 'react';
2−import cn from 'classnames';
3import { Grid, Text, Button } from 'indigo-react';
2import { Grid, Text, Button } from 'indigo-react';
4import * as azimuth from 'azimuth-js';
3import * as azimuth from 'azimuth-js';
5−import { Row } from '@tlon/indigo-react';
6
4
7import { useNetwork } from 'store/network';
5import { useNetwork } from 'store/network';
8import { usePointCursor } from 'store/pointCursor';
6import { usePointCursor } from 'store/pointCursor';
@@ -26,18 +24,20 @@ import FormError from 'form/FormError';
26import Window from 'components/L2/Window/Window';
24import Window from 'components/L2/Window/Window';
27import HeaderPane from 'components/L2/Window/HeaderPane';
25import HeaderPane from 'components/L2/Window/HeaderPane';
28import BodyPane from 'components/L2/Window/BodyPane';
26import BodyPane from 'components/L2/Window/BodyPane';
27+import { L1TxnType } from 'lib/types/PendingL1Transaction';
29
28
30function useTransfer() {
29function useTransfer() {
31− const { contracts } = useNetwork();
30+ const { contracts }: any = useNetwork();
32− const { pointCursor } = usePointCursor();
31+ const { pointCursor }: any = usePointCursor();
33− const { syncDetails } = usePointCache();
32+ const { syncDetails }: any = usePointCache();
34
33
35 const _contracts = need.contracts(contracts);
34 const _contracts = need.contracts(contracts);
36 const _point = need.point(pointCursor);
35 const _point = need.point(pointCursor);
37
36
38 return useEthereumTransaction(
37 return useEthereumTransaction(
39 useCallback(
38 useCallback(
39+ (address: string) =>
40− address => azimuth.ecliptic.setTransferProxy(_contracts, _point, address),
40+ azimuth.ecliptic.setTransferProxy(_contracts, _point, address),
41 [_contracts, _point]
41 [_contracts, _point]
42 ),
42 ),
43 useCallback(() => syncDetails(_point), [_point, syncDetails]),
43 useCallback(() => syncDetails(_point), [_point, syncDetails]),
@@ -46,14 +46,15 @@ function useTransfer() {
46}
46}
47
47
48export default function AdminTransfer() {
48export default function AdminTransfer() {
49− const { pop } = useLocalRouter();
49+ const { pop }: any = useLocalRouter();
50 const name = useCurrentPointName();
50 const name = useCurrentPointName();
51− const { pointCursor } = usePointCursor();
52− const { starReleaseDetails } = useStarReleaseCache();
51+ const { starReleaseDetails }: any = useStarReleaseCache();
52+ const { point } = useRollerStore();
53 const {
53 const {
54− point: { isL2 },
54+ setProxyAddress,
55− } = useRollerStore();
56− const { setProxyAddress, getPendingTransactions } = useRoller();
55+ getPendingTransactions,
56+ checkForUpdates,
57+ } = useRoller();
57 const [owner, setNewOwner] = useState('');
58 const [owner, setNewOwner] = useState('');
58
59
59 const {
60 const {
@@ -62,18 +63,34 @@ export default function AdminTransfer() {
62 completed,
63 completed,
63 inputsLocked,
64 inputsLocked,
64 bind,
65 bind,
66+ txHashes,
65 } = useTransfer();
67 } = useTransfer();
66
68
67− const point = need.point(pointCursor);
68−
69 const needLockupWarning = useMemo(() => {
69 const needLockupWarning = useMemo(() => {
70 const az = azimuth.azimuth;
70 const az = azimuth.azimuth;
71 return (
71 return (
72− az.getPointSize(point) === az.PointSize.Galaxy &&
72+ az.getPointSize(point.value) === az.PointSize.Galaxy &&
73− starReleaseDetails.map(a => a.kind).getOrElse('none') !== 'none'
73+ starReleaseDetails.map((a: any) => a.kind).getOrElse('none') !== 'none'
74 );
74 );
75 }, [starReleaseDetails, point]);
75 }, [starReleaseDetails, point]);
76
76
77+ useEffect(() => {
78+ // Update this one
79+ if (completed) {
80+ checkForUpdates({
81+ point: point.value,
82+ message: `${point.patp}'s transfer proxy has been set!`,
83+ l1Txn: {
84+ id: `set-transfer-proxy-${point.value}`,
85+ point: point.value,
86+ type: L1TxnType.transferProxy,
87+ hash: txHashes[0],
88+ time: new Date().getTime(),
89+ },
90+ });
91+ }
92+ }, [completed]); // eslint-disable-line react-hooks/exhaustive-deps
93+
77 const validate = useMemo(
94 const validate = useMemo(
78 () => composeValidator({ address: buildAddressValidator() }),
95 () => composeValidator({ address: buildAddressValidator() }),
79 []
96 []
@@ -84,14 +101,25 @@ export default function AdminTransfer() {
84 if (owner === '') return;
101 if (owner === '') return;
85 try {
102 try {
86 await setProxyAddress('transfer', owner);
103 await setProxyAddress('transfer', owner);
87− getPendingTransactions(point);
104+ getPendingTransactions();
105+ checkForUpdates({
106+ point: point.value,
107+ message: `${point.patp}'s transfer proxy has been set!`,
108+ });
88 pop();
109 pop();
89 } catch (error) {
110 } catch (error) {
90 // setError(error);
111 // setError(error);
91 } finally {
112 } finally {
92 // setLoading(false);
113 // setLoading(false);
93 }
114 }
115+ }, [
116+ point,
117+ owner,
94− }, [owner, getPendingTransactions, point, pop, setProxyAddress]);
118+ getPendingTransactions,
119+ pop,
120+ setProxyAddress,
121+ checkForUpdates,
122+ ]);
95
123
96 const onValues = useCallback(
124 const onValues = useCallback(
97 ({ valid, values }) => {
125 ({ valid, values }) => {
@@ -109,9 +137,7 @@ export default function AdminTransfer() {
109 return (
137 return (
110 <Window>
138 <Window>
111 <HeaderPane>
139 <HeaderPane>
112− <Row className="header-row">
113− <h5>Transfer Point</h5>
140+ <h5>Transfer Point</h5>
114− </Row>
115 </HeaderPane>
141 </HeaderPane>
116 <BodyPane>
142 <BodyPane>
117 <Grid className="w-full" full gap={1}>
143 <Grid className="w-full" full gap={1}>
@@ -119,7 +145,7 @@ export default function AdminTransfer() {
119 {({ handleSubmit, values }) => (
145 {({ handleSubmit, values }) => (
120 <>
146 <>
121 {!completed && (
147 {!completed && (
122− <Grid.Item full as={Text} className={cn('f5 wrap')}>
148+ <Grid.Item full as={Text} style={{ fontSize: 14 }}>
123 Transfer <span className="mono">{name}</span> to a new
149 Transfer <span className="mono">{name}</span> to a new
124 owner.
150 owner.
125 </Grid.Item>
151 </Grid.Item>
@@ -160,7 +186,7 @@ export default function AdminTransfer() {
160
186
161 <Grid.Item full as={FormError} />
187 <Grid.Item full as={FormError} />
162
188
163− {isL2 ? (
189+ {point.isL2 ? (
164 <Grid.Item
190 <Grid.Item
165 as={Button}
191 as={Button}
166 full
192 full
src/views/UrbitOS/NetworkKeys.tsx+1
@@ -189,6 +189,7 @@ export default function UrbitOSNetworkKeys({
189 point: point.value,
189 point: point.value,
190 type: L1TxnType.setNetworkKeys,
190 type: L1TxnType.setNetworkKeys,
191 hash: txHashes[0],
191 hash: txHashes[0],
192+ time: new Date().getTime(),
192 },
193 },
193 });
194 });
194 }
195 }