on master help sign in
WIP: api hanging
70004bbWill Galebach
src/assets/star.svgnew file+3
@@ -0,0 +1,3 @@
1+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<path fill-rule="evenodd" clip-rule="evenodd" d="M7.5 0.5V4.73077H6.5V0.5H7.5ZM4.80029 5.5074L2.03106 2.73817L2.73817 2.03106L5.5074 4.80029L4.80029 5.5074ZM8.4926 4.80029L11.2618 2.03106L11.9689 2.73817L9.19971 5.5074L8.4926 4.80029ZM9.26988 6.49519L13.5006 6.50065L13.4994 7.50064L9.26859 7.49519L9.26988 6.49519ZM0.5 6.49583H4.73077V7.49583H0.5V6.49583ZM5.5074 9.19971L2.73817 11.9689L2.03106 11.2618L4.80029 8.4926L5.5074 9.19971ZM11.2618 11.9689L8.4926 9.19971L9.19971 8.4926L11.9689 11.2618L11.2618 11.9689ZM7.5 9.26923V13.5H6.5V9.26923H7.5Z" fill="white"/>
3+</svg>
src/assets/wallet.svgnew file+3
@@ -0,0 +1,3 @@
1+<svg width="13" height="12" viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<path d="M0.5 2V10C0.5 10.2652 0.605357 10.5196 0.792893 10.7071C0.98043 10.8946 1.23478 11 1.5 11H11.5C11.6326 11 11.7598 10.9473 11.8536 10.8536C11.9473 10.7598 12 10.6326 12 10.5V3.5C12 3.36739 11.9473 3.24021 11.8536 3.14645C11.7598 3.05268 11.6326 3 11.5 3H10M0.5 2C0.5 2.26522 0.605357 2.51957 0.792893 2.70711C0.98043 2.89464 1.23478 3 1.5 3H10M0.5 2C0.5 1.73478 0.605357 1.48043 0.792893 1.29289C0.98043 1.10536 1.23478 1 1.5 1H10V3" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
3+</svg>
src/components/L2/Dropdowns/AccountsDropdown.tsx+10−7
@@ -16,6 +16,11 @@ import Dropdown from './Dropdown';
16import './AccountsDropdown.scss';
16import './AccountsDropdown.scss';
17import { abbreviateAddress } from 'lib/utils/address';
17import { abbreviateAddress } from 'lib/utils/address';
18
18
19+interface PointLayer {
20+ point: number;
21+ layer: 1 | 2;
22+}
23+
19const AccountsDropdown = () => {
24const AccountsDropdown = () => {
20 const [open, setOpen] = useState<boolean>(false);
25 const [open, setOpen] = useState<boolean>(false);
21 const walletInfo: any = useWallet();
26 const walletInfo: any = useWallet();
@@ -25,10 +30,8 @@ const AccountsDropdown = () => {
25
30
26 const canBitcoin = Just.hasInstance(walletInfo.urbitWallet);
31 const canBitcoin = Just.hasInstance(walletInfo.urbitWallet);
27
32
28− const points =
33+ console.log('WHAT', controlledPoints)
29− controlledPoints?.value?.value?.ownedPoints?.map((point: string) =>
34+ const points = controlledPoints?.value?.value?.pointsWithLayers || [];
30− Number(point)
31− ) || [];
32
35
33 const currentAddress = walletInfo?.wallet?.value?.address || '';
36 const currentAddress = walletInfo?.wallet?.value?.address || '';
34 const displayAddress = abbreviateAddress(currentAddress);
37 const displayAddress = abbreviateAddress(currentAddress);
@@ -49,14 +52,14 @@ const AccountsDropdown = () => {
49 <Dropdown
52 <Dropdown
50 className="accounts-dropdown"
53 className="accounts-dropdown"
51 open={open}
54 open={open}
52− value={displayAddress}
55+ value={currentAddress.slice(0, 6)}
53 toggleOpen={() => setOpen(!open)}>
56 toggleOpen={() => setOpen(!open)}>
54 <CopiableAddressWrap className="current-address">
57 <CopiableAddressWrap className="current-address">
55 {displayAddress}
58 {displayAddress}
56 </CopiableAddressWrap>
59 </CopiableAddressWrap>
57 <Box className="divider" />
60 <Box className="divider" />
58 <Box className="points">
61 <Box className="points">
59− {points.map((point: number) => {
62+ {points.map(({ point, layer }: PointLayer) => {
60 const patp = ob.patp(point);
63 const patp = ob.patp(point);
61
64
62 return (
65 return (
@@ -66,7 +69,7 @@ const AccountsDropdown = () => {
66 key={`point-${point}`}>
69 key={`point-${point}`}>
67 <Box>{patp}</Box>
70 <Box>{patp}</Box>
68 <Row>
71 <Row>
69− <LayerIndicator layer={1} size="md" />
72+ <LayerIndicator layer={layer} size="md" />
70 <Box className="sigil">
73 <Box className="sigil">
71 <Sigil patp={patp} size={1} colors={['#000000', '#FFFFFF']} />
74 <Sigil patp={patp} size={1} colors={['#000000', '#FFFFFF']} />
72 </Box>
75 </Box>
src/components/L2/Dropdowns/Dropdown.scss+2−2
@@ -5,7 +5,7 @@
5 position: relative;
5 position: relative;
6
6
7 .selector {
7 .selector {
8− background-color: $washedGray;
8+ background-color: $washed-gray;
9 padding: 8px;
9 padding: 8px;
10 min-width: 84px;
10 min-width: 84px;
11 border-radius: $medium-border-radius;
11 border-radius: $medium-border-radius;
@@ -26,7 +26,7 @@
26 }
26 }
27
27
28 .content-border {
28 .content-border {
29− border: 3px solid $washedGray;
29+ border: 3px solid $washed-gray;
30 position: absolute;
30 position: absolute;
31 top: 36px;
31 top: 36px;
32 left: -3px;
32 left: -3px;
src/components/L2/Headers/L2BackHeader.scss+8−3
@@ -6,13 +6,13 @@
6 justify-content: space-between;
6 justify-content: space-between;
7 align-items: center;
7 align-items: center;
8 font-size: 14px;
8 font-size: 14px;
9− margin: 48px 0px 16px;
9+ margin: 48px 0px 8px;
10
10
11 svg.back-button {
11 svg.back-button {
12 padding: 6px 9px;
12 padding: 6px 9px;
13 width: 14px;
13 width: 14px;
14 height: 20px;
14 height: 20px;
15− background-color: $washedGray;
15+ background-color: $washed-gray;
16 border-radius: $medium-border-radius;
16 border-radius: $medium-border-radius;
17 cursor: pointer;
17 cursor: pointer;
18 }
18 }
@@ -31,6 +31,11 @@
31 }
31 }
32
32
33 .eth-balance {
33 .eth-balance {
34− font-weight: 600;
34+ .wallet-icon {
35+ height: 12px;
36+ width: 14px;
37+ margin-right: 4px;
38+ margin-top: 2px;
39+ }
35 }
40 }
36}
41}
src/components/L2/Headers/L2BackHeader.tsx+5−1
@@ -11,6 +11,7 @@ import { useWallet } from 'store/wallet';
11import { toBN } from 'web3-utils';
11import { toBN } from 'web3-utils';
12import BN from 'bn.js';
12import BN from 'bn.js';
13import { isDevelopment } from 'lib/flags';
13import { isDevelopment } from 'lib/flags';
14+import { ReactComponent as Wallet } from 'assets/wallet.svg';
14
15
15export interface L2BackHeaderProps {
16export interface L2BackHeaderProps {
16 back?: () => void;
17 back?: () => void;
@@ -66,7 +67,10 @@ const L2BackHeader = ({ back, hideBalance = false }: L2BackHeaderProps) => {
66 </Row>
67 </Row>
67 ) : (
68 ) : (
68 !hideBalance && (
69 !hideBalance && (
69− <Box className="eth-balance">Balance: {Number(ethBalance)} ETH</Box>
70+ <Row className="eth-balance">
71+ <Wallet className="wallet-icon" />
72+ <Box>{ethBalance.toString()} ETH</Box>
73+ </Row>
70 )
74 )
71 )}
75 )}
72 </Row>
76 </Row>
src/components/L2/Headers/L2PointHeader.scss+61−2
@@ -10,7 +10,7 @@
10
10
11 .dropdown {
11 .dropdown {
12 border-radius: $medium-border-radius;
12 border-radius: $medium-border-radius;
13− background-color: $washedGray;
13+ background-color: $washed-gray;
14 }
14 }
15
15
16 .info {
16 .info {
@@ -28,7 +28,7 @@
28 }
28 }
29
29
30 .invites, .history {
30 .invites, .history {
31− background-color: $washedGray;
31+ background-color: $washed-gray;
32 padding: 8px;
32 padding: 8px;
33 }
33 }
34
34
@@ -47,4 +47,63 @@
47 width: 90px;
47 width: 90px;
48 }
48 }
49 }
49 }
50+
51+ .migrate-modal {
52+ padding: 16px;
53+ background-color: white;
54+ border-radius: $medium-border-radius;
55+ line-height: 24px;
56+ position: relative;
57+ width: 368px;
58+
59+ .close {
60+ position: absolute;
61+ top: 3px;
62+ right: 8px;
63+ font-size: 18px;
64+ font-weight: 200;
65+ cursor: pointer;
66+ }
67+
68+ .title {
69+ font-weight: 700;
70+ }
71+
72+ .message {
73+ width: 340px;
74+ }
75+
76+ .warning {
77+ font-weight: 600;
78+ color: $warning-red;
79+ }
80+
81+ .buttons {
82+ display: flex;
83+ flex-direction: row;
84+ justify-content: flex-end;
85+ margin-top: 24px;
86+
87+ .cancel, .migrate {
88+ border-radius: $medium-border-radius;
89+ border: none;
90+ height: 32px;
91+ font-family: $default-font;
92+
93+ &:hover {
94+ opacity: 0.8;
95+ }
96+ }
97+
98+ .cancel {
99+ margin-right: 8px;
100+ background-color: $washed-gray;
101+ }
102+
103+ .migrate {
104+ background-color: black;
105+ color: white;
106+ }
107+ }
108+ }
50}
109}
src/components/L2/Headers/L2PointHeader.tsx+43−2
@@ -1,15 +1,18 @@
1−import React, { useEffect, useCallback } from 'react';
1+import React, { useEffect, useCallback, useState } from 'react';
2+import { azimuth } from 'azimuth-js';
2−import { Box, Row } from '@tlon/indigo-react';
3+import { Box, Button, Icon, Row } from '@tlon/indigo-react';
3
4
4import useRoller from 'lib/useRoller';
5import useRoller from 'lib/useRoller';
5import { useHistory } from 'store/history';
6import { useHistory } from 'store/history';
6import { ReactComponent as InviteIcon } from 'assets/invite.svg';
7import { ReactComponent as InviteIcon } from 'assets/invite.svg';
7import { ReactComponent as HistoryIcon } from 'assets/history.svg';
8import { ReactComponent as HistoryIcon } from 'assets/history.svg';
8import AccountsDropdown from '../Dropdowns/AccountsDropdown';
9import AccountsDropdown from '../Dropdowns/AccountsDropdown';
10+import Modal from '../Modal';
9import { useRollerStore } from 'store/roller';
11import { useRollerStore } from 'store/roller';
10
12
11import './L2PointHeader.scss';
13import './L2PointHeader.scss';
12import { isDevelopment } from 'lib/flags';
14import { isDevelopment } from 'lib/flags';
15+import { usePointCursor } from 'store/pointCursor';
13
16
14export interface L2PointHeaderProps {
17export interface L2PointHeaderProps {
15 numInvites: number;
18 numInvites: number;
@@ -24,6 +27,8 @@ const L2PointHeader = ({
24}: L2PointHeaderProps) => {
27}: L2PointHeaderProps) => {
25 const { config } = useRoller();
28 const { config } = useRoller();
26 const { nextRoll, currentL2, pendingTransactions } = useRollerStore();
29 const { nextRoll, currentL2, pendingTransactions } = useRollerStore();
30+ const { pointCursor }: any = usePointCursor();
31+ const [showModal, setShowModal] = useState(false);
27
32
28 useEffect(() => {
33 useEffect(() => {
29 if (isDevelopment) {
34 if (isDevelopment) {
@@ -41,6 +46,14 @@ const L2PointHeader = ({
41 names.TRANSACTION_HISTORY,
46 names.TRANSACTION_HISTORY,
42 push,
47 push,
43 ]);
48 ]);
49+ const goMigrate = useCallback(() => push(names.MIGRATE_L2), [
50+ names.MIGRATE_L2,
51+ push,
52+ ]);
53+
54+ const isStar =
55+ pointCursor?.value &&
56+ azimuth.getPointSize(pointCursor?.value) === azimuth.PointSize.Star;
44
57
45 return (
58 return (
46 <Row className="l2-point-header">
59 <Row className="l2-point-header">
@@ -58,7 +71,35 @@ const L2PointHeader = ({
58 </Box>
71 </Box>
59 )}
72 )}
60 {currentL2 && <HistoryIcon className="history" onClick={goToHistory} />}
73 {currentL2 && <HistoryIcon className="history" onClick={goToHistory} />}
74+ {!currentL2 && isStar && (
75+ <Icon
76+ icon="Swap"
77+ className="history"
78+ onClick={() => setShowModal(true)}
79+ />
80+ )}
61 </Row>
81 </Row>
82+ <Modal show={showModal} hide={() => setShowModal(false)}>
83+ <Box className="migrate-modal">
84+ <Box className="close" onClick={() => setShowModal(false)}>
85+ &#215;
86+ </Box>
87+ <Box className="title">Migrating to Layer 2</Box>
88+ <Box className="message">
89+ We've upgraded Bridge to support Layer 2 transactions. If you don't
90+ migrate now, you can always do it later.
91+ </Box>
92+ <Box className="warning">Migrating to Layer 2 is irreversible.</Box>
93+ <Row className="buttons">
94+ <Button className="cancel" onClick={() => setShowModal(false)}>
95+ Cancel
96+ </Button>
97+ <Button className="migrate" onClick={goMigrate}>
98+ Migrate
99+ </Button>
100+ </Row>
101+ </Box>
102+ </Modal>
62 </Row>
103 </Row>
63 );
104 );
64};
105};
src/components/L2/Modal.scssnew file+14
@@ -0,0 +1,14 @@
1+@import 'style/shared/variables.scss';
2+
3+.modal {
4+ background-color: rgba(0,0,0,0.2);
5+ position: fixed;
6+ top: 0;
7+ bottom: 0;
8+ left: 0;
9+ right: 0;
10+ display: flex;
11+ flex-direction: column;
12+ justify-content: center;
13+ align-items: center;
14+}
src/components/L2/Modal.tsxnew file+26
@@ -0,0 +1,26 @@
1+import React, { MouseEvent } from 'react';
2+import { Box } from '@tlon/indigo-react';
3+
4+import './Modal.scss';
5+
6+export interface ModalProps {
7+ show: boolean;
8+ hide: () => void;
9+ children: React.ReactNode;
10+}
11+
12+export default function Modal({ show, hide, children }: ModalProps) {
13+ const dontHide = (e: MouseEvent) => {
14+ e.stopPropagation();
15+ };
16+
17+ if (!show) {
18+ return null;
19+ }
20+
21+ return (
22+ <Box className="modal" onClick={hide}>
23+ <Box onClick={dontHide}>{children}</Box>
24+ </Box>
25+ );
26+}
src/components/L2/Points/IncomingPoint.scss+1−1
@@ -42,6 +42,6 @@
42 }
42 }
43
43
44 .reject {
44 .reject {
45− background-color: $washedGray;
45+ background-color: $washed-gray;
46 }
46 }
47}
47}
src/components/MiniBackButton.scss+1−1
@@ -4,7 +4,7 @@ svg.back-button {
4 padding: 6px 9px;
4 padding: 6px 9px;
5 width: 14px;
5 width: 14px;
6 height: 20px;
6 height: 20px;
7− background-color: $washedGray;
7+ background-color: $washed-gray;
8 border-radius: $medium-border-radius;
8 border-radius: $medium-border-radius;
9 cursor: pointer;
9 cursor: pointer;
10}
10}
src/components/Passport.js+1
@@ -170,6 +170,7 @@ function Passport({
170 inverted={inverted}
170 inverted={inverted}
171 loading={loading}
171 loading={loading}
172 animationMode={animationMode}
172 animationMode={animationMode}
173+ key={`cell-${_col}`}
173 />
174 />
174 );
175 );
175 })}
176 })}
src/indigo-react/components/Grid.d.tsnew file+7
@@ -0,0 +1,7 @@
1+import React from 'react';
2+
3+const Grid = {
4+ Item: React.ReactNode,
5+}
6+
7+export default Grid;
src/lib/routeNames.js+1
@@ -34,4 +34,5 @@ export const ROUTE_NAMES = {
34 BITCOIN_XPUB: Symbol('BITCOIN_XPUB'),
34 BITCOIN_XPUB: Symbol('BITCOIN_XPUB'),
35 BITCOIN_SIGN_TRANSACTION: Symbol('BITCOIN_SIGN_TRANSACTION'),
35 BITCOIN_SIGN_TRANSACTION: Symbol('BITCOIN_SIGN_TRANSACTION'),
36 TRANSACTION_HISTORY: Symbol('TRANSACTION_HISTORY'),
36 TRANSACTION_HISTORY: Symbol('TRANSACTION_HISTORY'),
37+ MIGRATE_L2: Symbol('MIGRATE_L2'),
37};
38};
src/lib/router.js+9−6
@@ -1,25 +1,27 @@
1import Disclaimer from 'views/Disclaimer';
1import Disclaimer from 'views/Disclaimer';
2import Login from 'views/Login';
2import Login from 'views/Login';
3import Other from 'views/Login/Other.js';
3import Other from 'views/Login/Other.js';
4−import Senate from 'views/Senate';
4+import Senate from 'views/Point/Senate';
5import Documents from 'views/Senate/Documents';
5import Documents from 'views/Senate/Documents';
6import Upgrades from 'views/Senate/Upgrades';
6import Upgrades from 'views/Senate/Upgrades';
7import CreateGalaxy from 'views/CreateGalaxy';
7import CreateGalaxy from 'views/CreateGalaxy';
8−import IssueChild from 'views/IssueChild';
9import StarRelease from 'views/Release';
8import StarRelease from 'views/Release';
10import AcceptLockup from 'views/AcceptLockup';
9import AcceptLockup from 'views/AcceptLockup';
11
10
12−import Point from 'views/Point';
13import Points from 'views/Points';
11import Points from 'views/Points';
14import Activate from 'views/Activate';
12import Activate from 'views/Activate';
15
13
14+import Point from 'views/Point/Point';
15+import UrbitOS from 'views/Point/UrbitOS';
16+import UrbitID from 'views/Point/UrbitID';
17+import Residents from 'views/Point/Residents';
18+import MigrateL2 from 'views/Point/MigrateL2.tsx';
19+import IssueChild from 'views/Point/IssueChild';
20+
16import PartySetPoolSize from 'views/Party/PartySetPoolSize';
21import PartySetPoolSize from 'views/Party/PartySetPoolSize';
17import InviteCohort from 'views/Invite/Cohort';
22import InviteCohort from 'views/Invite/Cohort';
18import AcceptTransfer from 'views/AcceptTransfer';
23import AcceptTransfer from 'views/AcceptTransfer';
19import CancelTransfer from 'views/CancelTransfer';
24import CancelTransfer from 'views/CancelTransfer';
20−import UrbitOS from 'views/UrbitOS';
21−import UrbitID from 'views/UrbitID';
22−import Residents from 'views/Residents';
23import Adopt from 'views/Adopt';
25import Adopt from 'views/Adopt';
24import Bitcoin from 'views/Bitcoin/Bitcoin';
26import Bitcoin from 'views/Bitcoin/Bitcoin';
25import Xpub from 'views/Bitcoin/Xpub';
27import Xpub from 'views/Bitcoin/Xpub';
@@ -56,4 +58,5 @@ export const ROUTES = {
56 [ROUTE_NAMES.BITCOIN_XPUB]: Xpub,
58 [ROUTE_NAMES.BITCOIN_XPUB]: Xpub,
57 [ROUTE_NAMES.BITCOIN_SIGN_TRANSACTION]: SignTransaction,
59 [ROUTE_NAMES.BITCOIN_SIGN_TRANSACTION]: SignTransaction,
58 [ROUTE_NAMES.TRANSACTION_HISTORY]: TransactionHistory,
60 [ROUTE_NAMES.TRANSACTION_HISTORY]: TransactionHistory,
61+ [ROUTE_NAMES.MIGRATE_L2]: MigrateL2,
59};
62};
src/lib/useRoller.ts+5
@@ -344,6 +344,10 @@ export default function useRoller() {
344 ]
344 ]
345 );
345 );
346
346
347+ const migratePoint = useCallback(async () => {
348+ console.log('MIGRATING')
349+ }, []);
350+
347 // On load, get initial config
351 // On load, get initial config
348 useEffect(() => {
352 useEffect(() => {
349 if (config) {
353 if (config) {
@@ -377,5 +381,6 @@ export default function useRoller() {
377 getInvites,
381 getInvites,
378 getPendingTransactions,
382 getPendingTransactions,
379 generateInviteCodes,
383 generateInviteCodes,
384+ migratePoint,
380 };
385 };
381}
386}
src/lib/utils/roller.ts+2
@@ -14,3 +14,5 @@ export const getTimeToNextBatch = (nextBatch: number, now: number) => {
14
14
15 return `${hours}h ${padZero(minutes)}m ${padZero(seconds)}s`;
15 return `${hours}h ${padZero(minutes)}m ${padZero(seconds)}s`;
16};
16};
17+
18+export const isL2 = (dom?: string) => dom === 'l2' || dom === 'spawn';
src/store/lib/useControlledPointsStore.js+29−11
@@ -6,13 +6,14 @@ import * as azimuth from 'azimuth-js';
6import { useNetwork } from '../network';
6import { useNetwork } from '../network';
7import { useWallet } from 'store/wallet';
7import { useWallet } from 'store/wallet';
8import useRoller from 'lib/useRoller';
8import useRoller from 'lib/useRoller';
9+import { isL2 } from 'lib/utils/roller';
9
10
10const onlyUnique = (value, index, self) => self.indexOf(value) === index;
11const onlyUnique = (value, index, self) => self.indexOf(value) === index;
11
12
12export default function useControlledPointsStore() {
13export default function useControlledPointsStore() {
13 const { contracts } = useNetwork();
14 const { contracts } = useNetwork();
14 const { wallet } = useWallet();
15 const { wallet } = useWallet();
15− const { getPoints } = useRoller();
16+ const { api, getPoints } = useRoller();
16
17
17 const [controlledPoints, _setControlledPoints] = useState(Nothing());
18 const [controlledPoints, _setControlledPoints] = useState(Nothing());
18
19
@@ -34,21 +35,17 @@ export default function useControlledPointsStore() {
34 managingPoints,
35 managingPoints,
35 votingPoints,
36 votingPoints,
36 spawningPoints,
37 spawningPoints,
37− ] = await Promise.all([
38− getPoints('own', address),
39− getPoints('transfer', address),
40− getPoints('manage', address),
41− getPoints('vote', address),
42− getPoints('spawn', address),
43− ]);
44−
45− const [
46 ownedPointsL1,
38 ownedPointsL1,
47 incomingPointsL1,
39 incomingPointsL1,
48 managingPointsL1,
40 managingPointsL1,
49 votingPointsL1,
41 votingPointsL1,
50 spawningPointsL1,
42 spawningPointsL1,
51 ] = await Promise.all([
43 ] = await Promise.all([
44+ getPoints('own', address),
45+ getPoints('transfer', address),
46+ getPoints('manage', address),
47+ getPoints('vote', address),
48+ getPoints('spawn', address),
52 azimuth.azimuth.getOwnedPoints(_contracts, address),
49 azimuth.azimuth.getOwnedPoints(_contracts, address),
53 azimuth.azimuth.getTransferringFor(_contracts, address),
50 azimuth.azimuth.getTransferringFor(_contracts, address),
54 azimuth.azimuth.getManagerFor(_contracts, address),
51 azimuth.azimuth.getManagerFor(_contracts, address),
@@ -56,6 +53,26 @@ export default function useControlledPointsStore() {
56 azimuth.azimuth.getSpawningFor(_contracts, address),
53 azimuth.azimuth.getSpawningFor(_contracts, address),
57 ]);
54 ]);
58
55
56+ console.log(1)
57+ const pointsWithLayers = await Promise.all(
58+ ownedPoints.map(async ship => {
59+ console.log(2)
60+ try {
61+ const pointInfo = await api.getPoint(Number(ship));
62+ console.log(3, pointInfo)
63+ return {
64+ point: Number(ship),
65+ layer: isL2(pointInfo?.dominion) ? 1 : 2,
66+ };
67+ } catch (error) {
68+ console.warn(error)
69+ return { point: ship, layer: 1 };
70+ }
71+ })
72+ );
73+
74+ console.log(4, pointsWithLayers)
75+
59 if (
76 if (
60 ownedPoints === null &&
77 ownedPoints === null &&
61 incomingPoints === null &&
78 incomingPoints === null &&
@@ -90,6 +107,7 @@ export default function useControlledPointsStore() {
90 .concat(spawningPointsL1)
107 .concat(spawningPointsL1)
91 .map(Number)
108 .map(Number)
92 .filter(onlyUnique),
109 .filter(onlyUnique),
110+ pointsWithLayers,
93 })
111 })
94 )
112 )
95 );
113 );
@@ -98,7 +116,7 @@ export default function useControlledPointsStore() {
98 console.error('failed to fetch controlled points', error);
116 console.error('failed to fetch controlled points', error);
99 _setControlledPoints(Just(Result.Error(error)));
117 _setControlledPoints(Just(Result.Error(error)));
100 }
118 }
101− }, [contracts, wallet, getPoints]);
119+ }, [contracts, wallet, getPoints, api]);
102
120
103 // sync controlled points whenever wallet or contracts changes
121 // sync controlled points whenever wallet or contracts changes
104 useEffect(() => {
122 useEffect(() => {
src/store/roller.ts+1−3
@@ -1,11 +1,9 @@
1import create from 'zustand';
1import create from 'zustand';
2import { L2Point, PendingTransaction } from '@urbit/roller-api/build';
2import { L2Point, PendingTransaction } from '@urbit/roller-api/build';
3
3
4−import { HOUR } from 'lib/utils/roller';
4+import { HOUR, isL2 } from 'lib/utils/roller';
5import { Invite } from 'types/Invite';
5import { Invite } from 'types/Invite';
6
6
7−export const isL2 = (dom?: string) => dom === 'l2' || dom === 'spawn';
8−
9export interface RollerStore {
7export interface RollerStore {
10 nextBatchTime: number;
8 nextBatchTime: number;
11 nextRoll: string;
9 nextRoll: string;
src/store/storage/roller.ts+7
@@ -1,6 +1,7 @@
1import { Invite } from 'types/Invite';
1import { Invite } from 'types/Invite';
2
2
3const INVITES_KEY = 'invites';
3const INVITES_KEY = 'invites';
4+const HIDE_MIGRATION_MESSAGE_KEY = 'hide_migration_message';
4
5
5export interface StoredInvites {
6export interface StoredInvites {
6 available: Invite[];
7 available: Invite[];
@@ -32,3 +33,9 @@ export const setPendingInvites = (point: number, pendingInvites: Invite[]) => {
32};
33};
33
34
34export const clearInvitesStorage = () => localStorage.removeItem(INVITES_KEY);
35export const clearInvitesStorage = () => localStorage.removeItem(INVITES_KEY);
36+
37+export const getHideMigrationMessage = () =>
38+ Boolean(localStorage.getItem(HIDE_MIGRATION_MESSAGE_KEY));
39+
40+export const storeHideMigrationMessage = () =>
41+ localStorage.setItem(HIDE_MIGRATION_MESSAGE_KEY, 'true');
src/style/shared/variables.scss+4−1
@@ -1,4 +1,6 @@
1+$default-font: "Inter", -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji";
2+
1−$washedGray: rgba(0, 0, 0, 0.04);
3+$washed-gray: rgba(0, 0, 0, 0.04);
2$small-border-radius: 2px;
4$small-border-radius: 2px;
3$medium-border-radius: 4px;
5$medium-border-radius: 4px;
4$large-border-radius: 8px;
6$large-border-radius: 8px;
@@ -8,3 +10,4 @@ $mid-gray: rgba(0, 0, 0, 0.6);
8$dark-gray: rgba(102, 102, 102, 1);
10$dark-gray: rgba(102, 102, 102, 1);
9$rollup-yellow: rgba(255, 199, 0, 1);
11$rollup-yellow: rgba(255, 199, 0, 1);
10$accept-green: rgba(0, 159, 101, 1);
12$accept-green: rgba(0, 159, 101, 1);
13+$warning-red: rgb(242, 33, 33);
src/views/Invite/Cohort.js+1−1
@@ -17,7 +17,7 @@ import useRoller from 'lib/useRoller';
17import { convertToInt } from 'lib/convertToInt';
17import { convertToInt } from 'lib/convertToInt';
18import { generateUrl } from 'lib/utils/invite';
18import { generateUrl } from 'lib/utils/invite';
19
19
20−import { useIssueChild } from 'views/IssueChild';
20+import { useIssueChild } from 'views/Point/IssueChild';
21import View from 'components/View';
21import View from 'components/View';
22import L2BackHeader from 'components/L2/Headers/L2BackHeader';
22import L2BackHeader from 'components/L2/Headers/L2BackHeader';
23import Window from 'components/L2/Window/Window';
23import Window from 'components/L2/Window/Window';
src/views/Invite/Cohort.scss+3−3
@@ -12,7 +12,7 @@
12
12
13 .download-csv {
13 .download-csv {
14 padding: 6px 12px;
14 padding: 6px 12px;
15− background-color: $washedGray;
15+ background-color: $washed-gray;
16 border-radius: $medium-border-radius;
16 border-radius: $medium-border-radius;
17 display: flex;
17 display: flex;
18 flex-direction: row;
18 flex-direction: row;
@@ -72,7 +72,7 @@
72 }
72 }
73
73
74 .invite-url {
74 .invite-url {
75− background-color: $washedGray;
75+ background-color: $washed-gray;
76 border-radius: $medium-border-radius;
76 border-radius: $medium-border-radius;
77 border: 1px solid $light-gray;
77 border: 1px solid $light-gray;
78 width: 390px;
78 width: 390px;
@@ -80,7 +80,7 @@
80 }
80 }
81
81
82 .copy-invite {
82 .copy-invite {
83− background-color: $washedGray;
83+ background-color: $washed-gray;
84 border-radius: $medium-border-radius;
84 border-radius: $medium-border-radius;
85 width: 32px;
85 width: 32px;
86 height: 32px;
86 height: 32px;
src/views/IssueChild.js → src/views/Point/IssueChild.jsrenamed

The content is the same.

src/views/Point/MigrateL2.scssnew file+114
@@ -0,0 +1,114 @@
1+@import 'style/shared/variables.scss';
2+
3+.migrate-l2 {
4+ font-size: 14px;
5+
6+ .content {
7+ padding: 0 8px;
8+ line-height: 24px;
9+ display: flex;
10+ flex-direction: column;
11+ justify-content: space-between;
12+ min-height: 400px;
13+ width: 100%;
14+
15+ .bold {
16+ font-weight: 600;
17+ }
18+
19+ .info-list {
20+ margin: 40px 0;
21+
22+ .info-row {
23+ margin-bottom: 24px;
24+ width: 370px;
25+
26+ .icon-background {
27+ width: 24px;
28+ min-width: 24px;
29+ height: 24px;
30+ background-color: black;
31+ border-radius: 12px;
32+ display: flex;
33+ flex-direction: column;
34+ justify-content: center;
35+ align-items: center;
36+
37+ .icon {
38+ width: 14px;
39+ height: 14px;
40+ font-size: 14px;
41+ line-height: 14px;
42+ color: white;
43+ text-align: center;
44+ }
45+ }
46+
47+ .info-message {
48+ margin-left: 16px;
49+ }
50+
51+ .warning {
52+ color: $warning-red;
53+ font-weight: 600;
54+ }
55+ }
56+ }
57+
58+ .hide-row {
59+ display: flex;
60+ flex-direction: row;
61+ align-items: center;
62+
63+ .checkbox {
64+ height: 16px;
65+ width: 16px;
66+
67+ svg {
68+ width: 16px;
69+ height: 16px;
70+ color: white;
71+ }
72+ }
73+
74+ .dont-show {
75+ margin-left: 8px;
76+ font-weight: 600;
77+ }
78+ }
79+
80+ .buttons {
81+ display: flex;
82+ flex-direction: row;
83+ justify-content: flex-end;
84+ margin-top: 16px;
85+
86+ .cancel, .proceed {
87+ border-radius: $medium-border-radius;
88+ border: none;
89+ height: 32px;
90+ font-family: $default-font;
91+
92+ &:hover {
93+ opacity: 0.8;
94+ }
95+ }
96+
97+ .cancel {
98+ margin-right: 8px;
99+ background-color: $washed-gray;
100+ }
101+
102+ .proceed {
103+ background-color: black;
104+ color: white;
105+ }
106+ }
107+
108+ .ship-selector {
109+ .select-ship {
110+ color: $mid-gray;
111+ }
112+ }
113+ }
114+}
src/views/Point/MigrateL2.tsxnew file+198
@@ -0,0 +1,198 @@
1+import React, { useState, useEffect, useCallback } from 'react';
2+import { Grid } from 'indigo-react';
3+import * as azimuth from 'azimuth-js';
4+
5+import View from 'components/View';
6+import L2BackHeader from 'components/L2/Headers/L2BackHeader';
7+import BodyPane from 'components/L2/Window/BodyPane';
8+import HeaderPane from 'components/L2/Window/HeaderPane';
9+import Window from 'components/L2/Window/Window';
10+import { ReactComponent as StarIcon } from 'assets/star.svg';
11+
12+import { useLocalRouter } from 'lib/LocalRouter';
13+import * as need from 'lib/need';
14+
15+import { usePointCursor } from 'store/pointCursor';
16+
17+import './MigrateL2.scss';
18+import { Box, Button, Checkbox, Icon, Row } from '@tlon/indigo-react';
19+import {
20+ getHideMigrationMessage,
21+ storeHideMigrationMessage,
22+} from 'store/storage/roller';
23+import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
24+import { useNetwork } from 'store/network';
25+import { usePointCache } from 'store/pointCache';
26+import useEthereumTransaction from 'lib/useEthereumTransaction';
27+import { GAS_LIMITS } from 'lib/constants';
28+
29+const DUMMY_L2_ADDRESS = '0x1111111111111111111111111111111111111111';
30+
31+const useMigrate = () => {
32+ const { contracts }: any = useNetwork();
33+ const { syncDates }: any = usePointCache();
34+
35+ const _contracts = need.contracts(contracts);
36+
37+ const [selectedPoint, setSelectedPoint] = useState<number | null>(null);
38+
39+ return useEthereumTransaction(
40+ useCallback(
41+ (selectedPoint: number) => {
42+ setSelectedPoint(selectedPoint);
43+ return azimuth.ecliptic.setSpawnProxy(
44+ _contracts,
45+ selectedPoint,
46+ DUMMY_L2_ADDRESS
47+ );
48+ },
49+ [_contracts]
50+ ),
51+ useCallback(() => syncDates(selectedPoint), [selectedPoint, syncDates]),
52+ GAS_LIMITS.DEFAULT
53+ );
54+};
55+
56+export default function MigrateL2() {
57+ const { pop }: any = useLocalRouter();
58+
59+ const [proceed, setProceed] = useState(false);
60+ const [hideMessage, setHideMessage] = useState(false);
61+ const hideInfo = getHideMigrationMessage();
62+
63+ const { pointCursor }: any = usePointCursor();
64+ const point = need.point(pointCursor);
65+
66+ const { construct, unconstruct, completed, bind } = useMigrate();
67+
68+ useEffect(() => {
69+ if (point) {
70+ construct(point);
71+ } else {
72+ unconstruct();
73+ }
74+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
75+
76+ useEffect(() => {
77+ if (completed) {
78+ pop();
79+ }
80+ }, [completed, pop]);
81+
82+ const goBack = useCallback(() => {
83+ if (proceed) {
84+ setProceed(false);
85+ } else {
86+ pop();
87+ }
88+ }, [pop, proceed, setProceed]);
89+
90+ const goProceed = useCallback(() => {
91+ if (hideMessage) {
92+ storeHideMigrationMessage();
93+ }
94+
95+ setProceed(true);
96+ }, [hideMessage]);
97+
98+ const getContent = () => {
99+ if (proceed) {
100+ return (
101+ <Box className="content">
102+ <Box className="ship-selector">
103+ <Box>Ship</Box>
104+ <Box className="select-ship">Select one of your ships</Box>
105+
106+ </Box>
107+ <Grid.Item
108+ full
109+ as={InlineEthereumTransaction}
110+ label="Migrate"
111+ {...bind}
112+ onReturn={pop}
113+ />
114+ </Box>
115+ );
116+ }
117+
118+ return (
119+ <Box className="content">
120+ <Box className="message">
121+ You are about to migrate a node from Layer 1 to Layer 2. This will
122+ make transactions faster and cheaper. Here are some things you should
123+ know. <span className="bold">Learn More</span>
124+ </Box>
125+ {!hideInfo && (
126+ <Box className="info-list">
127+ <Row className="info-row">
128+ <Box className="icon-background">
129+ <StarIcon className="icon" />
130+ </Box>
131+ <Box className="info-message">
132+ <span className="warning">
133+ Migration to Layer 2 is irreversible
134+ </span>
135+ {'. '}
136+ You will not be able to revert back to Layer 1.
137+ </Box>
138+ </Row>
139+ <Row className="info-row">
140+ <Box className="icon-background">
141+ <Box className="icon">$</Box>
142+ </Box>
143+ <Box className="info-message">
144+ A migration is expensive, but a one-time fee. The estimated
145+ price is <span className="bold">$120</span>. Transaction fees
146+ will be subsidized on Layer 2.
147+ </Box>
148+ </Row>
149+ <Row className="info-row">
150+ <Box className="icon-background">
151+ <Icon icon="Clock" className="icon" color="white" />
152+ </Box>
153+ {/* <HistoryIcon className="icon" /> */}
154+ <Box className="info-message">
155+ All transactions will now be pushed at the end of the day. A
156+ timer will show when the next push is.
157+ </Box>
158+ </Row>
159+ </Box>
160+ )}
161+ {!hideInfo && (
162+ <Row className="hide-row">
163+ <Checkbox
164+ className="checkbox"
165+ selected={hideMessage}
166+ onClick={() => setHideMessage(!hideMessage)}
167+ />
168+ <Box className="dont-show">Do not show this again</Box>
169+ </Row>
170+ )}
171+ <Row className="buttons">
172+ <Button className="cancel" onClick={goBack}>
173+ Cancel
174+ </Button>
175+ <Button className="proceed" onClick={goProceed}>
176+ Proceed
177+ </Button>
178+ </Row>
179+ </Box>
180+ );
181+ };
182+
183+ return (
184+ <View
185+ className="migrate-l2"
186+ pop={pop}
187+ hideBack
188+ inset
189+ header={<L2BackHeader back={goBack} />}>
190+ <Window>
191+ <HeaderPane>
192+ <h5>Migrating to Layer 2</h5>
193+ </HeaderPane>
194+ <BodyPane>{getContent()}</BodyPane>
195+ </Window>
196+ </View>
197+ );
198+}
src/views/Point.js → src/views/Point/Point.jsrenamed+2−1
@@ -7,7 +7,7 @@ import { azimuth } from 'azimuth-js';
7
7
8import { usePointCursor } from 'store/pointCursor';
8import { usePointCursor } from 'store/pointCursor';
9import { useWallet } from 'store/wallet';
9import { useWallet } from 'store/wallet';
10−import { isL2, useRollerStore } from 'store/roller';
10+import { useRollerStore } from 'store/roller';
11
11
12import View from 'components/View';
12import View from 'components/View';
13import Greeting from 'components/Greeting';
13import Greeting from 'components/Greeting';
@@ -30,6 +30,7 @@ import Inviter from 'views/Invite/Inviter';
30import useRoller from 'lib/useRoller';
30import useRoller from 'lib/useRoller';
31
31
32import './Point.scss';
32import './Point.scss';
33+import { isL2 } from 'lib/utils/roller';
33
34
34function InviteForm({
35function InviteForm({
35 showInviteForm,
36 showInviteForm,
src/views/Point.scss → src/views/Point/Point.scssrenamed

The content is the same.

src/views/Residents.js → src/views/Point/Residents.jsrenamed

The content is the same.

src/views/Senate.js → src/views/Point/Senate.jsrenamed

The content is the same.

src/views/UrbitID.js → src/views/Point/UrbitID.jsrenamed+6−6
@@ -11,12 +11,12 @@ import View from 'components/View';
11import Crumbs from 'components/Crumbs';
11import Crumbs from 'components/Crumbs';
12import NavHeader from 'components/NavHeader';
12import NavHeader from 'components/NavHeader';
13
13
14−import UrbitIDHome from './UrbitID/Home';
14+import UrbitIDHome from '../UrbitID/Home';
15−import SigilGenerator from './UrbitID/SigilGenerator';
15+import SigilGenerator from '../UrbitID/SigilGenerator';
16−import DownloadKeys from './UrbitID/DownloadKeys';
16+import DownloadKeys from '../UrbitID/DownloadKeys';
17−import SetProxy from './UrbitID/SetProxy';
17+import SetProxy from '../UrbitID/SetProxy';
18−import ResetKeys from './UrbitID/ResetKeys';
18+import ResetKeys from '../UrbitID/ResetKeys';
19−import Transfer from './UrbitID/Transfer';
19+import Transfer from '../UrbitID/Transfer';
20
20
21const NAMES = {
21const NAMES = {
22 HOME: 'HOME',
22 HOME: 'HOME',
src/views/UrbitOS.js → src/views/Point/UrbitOS.jsrenamed+3−3
@@ -11,9 +11,9 @@ import View from 'components/View';
11import Crumbs from 'components/Crumbs';
11import Crumbs from 'components/Crumbs';
12import NavHeader from 'components/NavHeader';
12import NavHeader from 'components/NavHeader';
13
13
14−import UrbitOSHome from './UrbitOS/Home';
14+import UrbitOSHome from '../UrbitOS/Home';
15−import UrbitOSNetworkingKeys from './UrbitOS/NetworkingKeys';
15+import UrbitOSNetworkingKeys from '../UrbitOS/NetworkingKeys';
16−import UrbitOSChangeSponsor from './UrbitOS/ChangeSponsor';
16+import UrbitOSChangeSponsor from '../UrbitOS/ChangeSponsor';
17
17
18const NAMES = {
18const NAMES = {
19 HOME: 'HOME',
19 HOME: 'HOME',
src/views/Points.js+9−6
@@ -26,7 +26,7 @@ import CopiableAddress from 'components/copiable/CopiableAddress';
26import NavHeader from 'components/NavHeader';
26import NavHeader from 'components/NavHeader';
27import L2PointHeader from 'components/L2/Headers/L2PointHeader';
27import L2PointHeader from 'components/L2/Headers/L2PointHeader';
28import IncomingPoint from 'components/L2/Points/IncomingPoint';
28import IncomingPoint from 'components/L2/Points/IncomingPoint';
29−import { CONDITIONAL_STAR_RELEASE, LINEAR_STAR_RELEASE } from 'lib/constants';
29+import { useNetwork } from 'store/network';
30
30
31export const maybeGetResult = (obj, key, defaultValue) =>
31export const maybeGetResult = (obj, key, defaultValue) =>
32 obj.matchWith({
32 obj.matchWith({
@@ -66,9 +66,9 @@ export const getOutgoingPoints = (controlledPoints, getDetails) => {
66 .getOrElse([]);
66 .getOrElse([]);
67};
67};
68
68
69−export const isLocked = details =>
69+export const isLocked = (details, contracts) =>
70− details.owner === LINEAR_STAR_RELEASE ||
70+ details.owner === contracts.linearSR ||
71− details.owner === CONDITIONAL_STAR_RELEASE;
71+ details.owner === contracts.conditionalSR;
72
72
73const PointList = function({
73const PointList = function({
74 points,
74 points,
@@ -131,6 +131,7 @@ export default function Points() {
131 const { pop, push, popAndPush, names } = useHistory();
131 const { pop, push, popAndPush, names } = useHistory();
132 const { setPointCursor } = usePointCursor();
132 const { setPointCursor } = usePointCursor();
133 const { controlledPoints, getDetails } = usePointCache();
133 const { controlledPoints, getDetails } = usePointCache();
134+ const { contracts } = useNetwork();
134 const isEclipticOwner = useIsEclipticOwner();
135 const isEclipticOwner = useIsEclipticOwner();
135 const [
136 const [
136 rejectedPoints,
137 rejectedPoints,
@@ -143,6 +144,8 @@ export default function Points() {
143 [getDetails, controlledPoints]
144 [getDetails, controlledPoints]
144 );
145 );
145
146
147+ const _contracts = need.contracts(contracts);
148+
146 const maybeLockedPoints = useMemo(
149 const maybeLockedPoints = useMemo(
147 () =>
150 () =>
148 controlledPoints.chain(points =>
151 controlledPoints.chain(points =>
@@ -151,7 +154,7 @@ export default function Points() {
151 Ok: c => {
154 Ok: c => {
152 const points = c.value.ownedPoints.map(point =>
155 const points = c.value.ownedPoints.map(point =>
153 getDetails(point).chain(details =>
156 getDetails(point).chain(details =>
154− Just({ point, has: isLocked(details) })
157+ Just({ point, has: isLocked(details, _contracts) })
155 )
158 )
156 );
159 );
157 // if we have details for every point,
160 // if we have details for every point,
@@ -167,7 +170,7 @@ export default function Points() {
167 },
170 },
168 })
171 })
169 ),
172 ),
170− [getDetails, controlledPoints]
173+ [getDetails, controlledPoints, _contracts]
171 );
174 );
172
175
173 // if we can only interact with a single point, forget about the existence
176 // if we can only interact with a single point, forget about the existence