on master help sign in
Cleaned up the Point view
841a01aWill Galebach
src/components/InlineEthereumTransaction.js+2−2
@@ -224,13 +224,13 @@ export default function InlineEthereumTransaction({
224 />
224 />
225 )}
225 )}
226
226
227− {showSignedTx && fakeSigned && (
227+ {/* {showSignedTx && fakeSigned && (
228 <>
228 <>
229 <Grid.Item className="mt2 eth-tx-button" full as={NoticeBox}>
229 <Grid.Item className="mt2 eth-tx-button" full as={NoticeBox}>
230 Your wallet will sign the transaction upon sending it.
230 Your wallet will sign the transaction upon sending it.
231 </Grid.Item>
231 </Grid.Item>
232 </>
232 </>
233− )}
233+ )} */}
234 </>
234 </>
235 )}
235 )}
236 </BridgeForm>
236 </BridgeForm>
src/components/L2/Dropdowns/AccountsDropdown.tsx+16
@@ -22,6 +22,7 @@ import './AccountsDropdown.scss';
22import Modal from '../Modal';
22import Modal from '../Modal';
23import { useHasNetworkKeysSet } from 'lib/useHasNetworkKeysSet';
23import { useHasNetworkKeysSet } from 'lib/useHasNetworkKeysSet';
24import { useRollerStore } from 'store/roller';
24import { useRollerStore } from 'store/roller';
25+import useCurrentPermissions from 'lib/useCurrentPermissions';
25
26
26interface AccountsDropdownProps {
27interface AccountsDropdownProps {
27 showMigrate: boolean;
28 showMigrate: boolean;
@@ -59,20 +60,29 @@ const AccountsDropdown = ({ showMigrate = false }: AccountsDropdownProps) => {
59 },
60 },
60 [push, names.POINT, setOpen, setPointCursor]
61 [push, names.POINT, setOpen, setPointCursor]
61 );
62 );
63+
62 const goMigrate = useCallback(() => push(names.MIGRATE_L2), [
64 const goMigrate = useCallback(() => push(names.MIGRATE_L2), [
63 names.MIGRATE_L2,
65 names.MIGRATE_L2,
64 push,
66 push,
65 ]);
67 ]);
68+
66 const goBitcoin = useCallback(() => push(names.BITCOIN), [
69 const goBitcoin = useCallback(() => push(names.BITCOIN), [
67 names.BITCOIN,
70 names.BITCOIN,
68 push,
71 push,
69 ]);
72 ]);
70
73
74+ const goIssuePoint = useCallback(() => push(names.ISSUE_CHILD), [
75+ names.ISSUE_CHILD,
76+ push,
77+ ]);
78+
71 const onLogout = () => {
79 const onLogout = () => {
72 clearInvitesStorage();
80 clearInvitesStorage();
73 reset();
81 reset();
74 };
82 };
75
83
84+ const { canSpawn, isParent } = useCurrentPermissions();
85+
76 return (
86 return (
77 <Dropdown
87 <Dropdown
78 className="accounts-dropdown"
88 className="accounts-dropdown"
@@ -123,6 +133,12 @@ const AccountsDropdown = ({ showMigrate = false }: AccountsDropdownProps) => {
123 <Icon icon="Bitcoin" />
133 <Icon icon="Bitcoin" />
124 </Row>
134 </Row>
125 )}
135 )}
136+ {isParent && canSpawn && (
137+ <Row className="entry" onClick={goIssuePoint}>
138+ <Box>Issue Point</Box>
139+ <Icon icon="Plus" />
140+ </Row>
141+ )}
126 <Row className="entry" onClick={onLogout}>
142 <Row className="entry" onClick={onLogout}>
127 <Box>Logout</Box>
143 <Box>Logout</Box>
128 <Icon icon="LogOut" />
144 <Icon icon="LogOut" />
src/components/L2/Dropdowns/FeeDropdown.scss+2−2
@@ -28,13 +28,13 @@
28 }
28 }
29
29
30 .label {
30 .label {
31− margin-right: 32px;
31+ margin-right: 8px;
32 }
32 }
33
33
34 .custom-input {
34 .custom-input {
35 font-family: "Inter", -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji";
35 font-family: "Inter", -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji";
36 text-align: right;
36 text-align: right;
37− height: 32px;
37+ height: 24px;
38 }
38 }
39
39
40 .unit {
40 .unit {
src/components/L2/Window/BodyPane.scss+22
@@ -23,6 +23,10 @@
23 .subtitle {
23 .subtitle {
24 margin-top: 4px;
24 margin-top: 4px;
25 color: $mid-gray;
25 color: $mid-gray;
26+
27+ &.error-text {
28+ color: $warning-red;
29+ }
26 }
30 }
27
31
28 .arrow-button {
32 .arrow-button {
@@ -31,6 +35,24 @@
31 padding: 0 4px;
35 padding: 0 4px;
32 }
36 }
33
37
38+ button.primary {
39+ background-color: black;
40+ color: white;
41+ border-radius: $medium-border-radius;
42+ border: none;
43+ height: 32px;
44+ padding: 8px 16px;
45+ font-family: $default-font;
46+
47+ &:nth-child(2) {
48+ margin-left: 8px;
49+ }
50+
51+ &:hover {
52+ opacity: 0.8;
53+ }
54+ }
55+
34 button.secondary {
56 button.secondary {
35 background-color: $washed-gray;
57 background-color: $washed-gray;
36 border-radius: $medium-border-radius;
58 border-radius: $medium-border-radius;
src/lib/metamask.ts+6−2
@@ -2,8 +2,12 @@ import Web3 from 'web3';
2import { toHex } from 'web3-utils';
2import { toHex } from 'web3-utils';
3import { FakeSignableTx, FakeSignResult } from './txn';
3import { FakeSignableTx, FakeSignResult } from './txn';
4
4
5−export function MetamaskWallet(address) {
5+export class MetamaskWallet {
6+ address: string;
7+
8+ constructor(address: string) {
6− this.address = address;
9+ this.address = address;
10+ }
7}
11}
8
12
9// Cheat a bit and pretend to sign transaction
13// Cheat a bit and pretend to sign transaction
src/lib/useRoller.ts+5−5
@@ -145,11 +145,11 @@ export default function useRoller() {
145 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
145 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
146 }
146 }
147
147
148− if (Just.hasInstance(authMnemonic)) {
148+ // if (Just.hasInstance(authMnemonic)) {
149− throw new Error(
149+ // throw new Error(
150− "Auth key Error: A management mnemonic can't create invite codes"
150+ // "Auth key Error: A management mnemonic can't create invite codes"
151− );
151+ // );
152− }
152+ // }
153 let planets: UnspawnedPoints = await api.getUnspawned(_point);
153 let planets: UnspawnedPoints = await api.getUnspawned(_point);
154
154
155 const pendingTxs = await api.getPendingByShip(_point);
155 const pendingTxs = await api.getPendingByShip(_point);
src/views/Activate/MasterKeyConfirm.tsx+1−1
@@ -78,7 +78,7 @@ const MasterKeyConfirm = () => {
78 <ActivateHeader content={'Confirm Backup'} />
78 <ActivateHeader content={'Confirm Backup'} />
79 <ActivateParagraph
79 <ActivateParagraph
80 copy={
80 copy={
81− 'Confirm that you have stored your Master Key somewhere safe by entering it below'
81+ 'Confirm that you have stored your Master Ticket somewhere safe by entering it below'
82 }
82 }
83 />
83 />
84 </Box>
84 </Box>
src/views/Activate/MasterKeyDownload.tsx+1−1
@@ -71,7 +71,7 @@ const MasterKeyDownload = () => {
71 const header = useMemo(() => {
71 const header = useMemo(() => {
72 return (
72 return (
73 <Box>
73 <Box>
74− <ActivateHeader content={'Backup your Master Key.'} />
74+ <ActivateHeader content={'Backup your Master Ticket.'} />
75 <ActivateParagraph
75 <ActivateParagraph
76 copy={
76 copy={
77 'Download your backup and store it somewhere safe, e.g. your security deposit box or password manager.'
77 'Download your backup and store it somewhere safe, e.g. your security deposit box or password manager.'
src/views/Activate/MasterKeyReveal.tsx+2−2
@@ -34,10 +34,10 @@ const MasterKeyReveal = () => {
34 const header = useMemo(() => {
34 const header = useMemo(() => {
35 return (
35 return (
36 <Box>
36 <Box>
37− <ActivateHeader content={'Here is your Master Key.'} />
37+ <ActivateHeader content={'Here is your Master Ticket.'} />
38 <ActivateParagraph
38 <ActivateParagraph
39 copy={
39 copy={
40− "Your Master Key is your 4-word password for your Urbit. Make sure you're in a private place before you reveal it."
40+ "Your Master Ticket is your 4-word password for your Urbit. Make sure you're in a private place before you reveal it."
41 }
41 }
42 />
42 />
43 </Box>
43 </Box>
src/views/Login/LoginSelector.tsx+92−7
@@ -1,16 +1,28 @@
1import React, { useCallback, useState } from 'react';
1import React, { useCallback, useState } from 'react';
2import cn from 'classnames';
2import cn from 'classnames';
3import { Grid } from 'indigo-react';
3import { Grid } from 'indigo-react';
4+import { Box, Button, Icon, Row, Text } from '@tlon/indigo-react';
5+import { useHistory } from 'store/history';
6+import { Just, Nothing } from 'folktale/maybe';
7+import { FORM_ERROR } from 'final-form';
8+
9+import { useWallet } from 'store/wallet';
10+import { useNetwork } from 'store/network';
11+import { usePointCursor } from 'store/pointCursor';
12+
13+import { WALLET_TYPES } from 'lib/constants';
14+import { MetamaskWallet } from 'lib/metamask';
15+import { getAuthToken } from 'lib/authToken';
16+import * as need from 'lib/need';
17+import { useWalletConnect } from 'lib/useWalletConnect';
18+
4import Window from 'components/L2/Window/Window';
19import Window from 'components/L2/Window/Window';
5import HeaderPane from 'components/L2/Window/HeaderPane';
20import HeaderPane from 'components/L2/Window/HeaderPane';
6import BodyPane from 'components/L2/Window/BodyPane';
21import BodyPane from 'components/L2/Window/BodyPane';
7−import { Box, Button, Icon, Row, Text } from '@tlon/indigo-react';
8−import L2BackHeader from 'components/L2/Headers/L2BackHeader';
9−
10−import './LoginSelector.scss';
11−import { useHistory } from 'store/history';
12import Modal from 'components/L2/Modal';
22import Modal from 'components/L2/Modal';
13import L2BackButton from 'components/L2/Headers/L2BackButton';
23import L2BackButton from 'components/L2/Headers/L2BackButton';
24+import './LoginSelector.scss';
25+import { NAMES } from './Other';
14
26
15interface LoginSelectorProps {
27interface LoginSelectorProps {
16 className: string;
28 className: string;
@@ -23,6 +35,7 @@ interface LoginSelectorProps {
23 }[];
35 }[];
24 currentTab?: string;
36 currentTab?: string;
25 setCurrentTab: (tab?: string) => void;
37 setCurrentTab: (tab?: string) => void;
38+ goHome: () => void;
26}
39}
27
40
28export default function LoginSelector({
41export default function LoginSelector({
@@ -31,17 +44,83 @@ export default function LoginSelector({
31 options,
44 options,
32 currentTab,
45 currentTab,
33 setCurrentTab,
46 setCurrentTab,
47+ goHome,
34 // Tab props
48 // Tab props
35 ...rest
49 ...rest
36}: LoginSelectorProps) {
50}: LoginSelectorProps) {
37 const { push, names }: any = useHistory();
51 const { push, names }: any = useHistory();
38 const [showModal, setShowModal] = useState(false);
52 const [showModal, setShowModal] = useState(false);
39
53
54+ const {
55+ setWallet,
56+ setWalletType,
57+ resetWallet,
58+ setAuthToken,
59+ }: any = useWallet();
60+
61+ const { setPointCursor }: any = usePointCursor();
62+ const { authenticate } = useWalletConnect();
63+ const { web3 }: any = useNetwork();
64+ const _web3 = need.web3(web3);
65+
40 const goToActivate = useCallback(() => push(names.ACTIVATE), [
66 const goToActivate = useCallback(() => push(names.ACTIVATE), [
41 push,
67 push,
42 names.ACTIVATE,
68 names.ACTIVATE,
43 ]);
69 ]);
44
70
71+ const connectWalletConnect = async () => {
72+ resetWallet();
73+ setWalletType(WALLET_TYPES.WALLET_CONNECT);
74+ setPointCursor(Nothing());
75+ await authenticate();
76+ goHome();
77+ };
78+
79+ const connectMetamask = useCallback(async () => {
80+ try {
81+ resetWallet();
82+ setWalletType(WALLET_TYPES.METAMASK);
83+ setPointCursor(Nothing());
84+
85+ const accounts = await (window as any).ethereum.send(
86+ 'eth_requestAccounts'
87+ );
88+ const wallet = new MetamaskWallet(accounts.result[0]);
89+
90+ const authToken = await getAuthToken({
91+ address: wallet.address,
92+ walletType: WALLET_TYPES.METAMASK,
93+ web3: _web3,
94+ });
95+
96+ setAuthToken(Just(authToken));
97+ setWallet(Just(wallet));
98+ setWalletType(WALLET_TYPES.METAMASK);
99+ goHome();
100+ } catch (e) {
101+ console.error(e);
102+ return { [FORM_ERROR]: (e as any).message };
103+ }
104+ }, [
105+ _web3,
106+ setAuthToken,
107+ setWallet,
108+ setWalletType,
109+ resetWallet,
110+ setPointCursor,
111+ goHome,
112+ ]);
113+
114+ const selectOption = (value: string) => () => {
115+ if (value === NAMES.METAMASK) {
116+ connectMetamask();
117+ } else if (value === NAMES.WALLET_CONNECT) {
118+ connectWalletConnect();
119+ } else {
120+ setCurrentTab(value);
121+ }
122+ };
123+
45 if (!currentTab) {
124 if (!currentTab) {
46 return (
125 return (
47 <Grid className={className}>
126 <Grid className={className}>
@@ -50,7 +129,7 @@ export default function LoginSelector({
50 <Grid.Item
129 <Grid.Item
51 full
130 full
52 className={cn('f5 pv3 rel pointer login-menu-item')}
131 className={cn('f5 pv3 rel pointer login-menu-item')}
53− onClick={() => setCurrentTab(option.value)}>
132+ onClick={selectOption(option.value)}>
54 {option.text}
133 {option.text}
55 {option.menuIcon}
134 {option.menuIcon}
56 </Grid.Item>
135 </Grid.Item>
@@ -109,7 +188,13 @@ export default function LoginSelector({
109 </Row>
188 </Row>
110 </HeaderPane>
189 </HeaderPane>
111 <BodyPane className="login-body-pane">
190 <BodyPane className="login-body-pane">
191+ <Grid.Item
192+ full
193+ as={Tab}
194+ {...rest}
195+ goHome={goHome}
112− <Grid.Item full as={Tab} {...rest} className="login-selector" />
196+ className="login-selector"
197+ />
113 </BodyPane>
198 </BodyPane>
114 </Window>
199 </Window>
115 </Box>
200 </Box>
src/views/Login/Other.tsx+2−2
@@ -9,7 +9,7 @@ import LoginSelector from './LoginSelector';
9import { ReactComponent as MetamaskIcon } from 'assets/metamask.svg';
9import { ReactComponent as MetamaskIcon } from 'assets/metamask.svg';
10import { ReactComponent as WalletConnectIcon } from 'assets/wallet-connect.svg';
10import { ReactComponent as WalletConnectIcon } from 'assets/wallet-connect.svg';
11
11
12−const NAMES = {
12+export const NAMES = {
13 METAMASK: 'METAMASK',
13 METAMASK: 'METAMASK',
14 MNEMONIC: 'MNEMONIC',
14 MNEMONIC: 'MNEMONIC',
15 WALLET_CONNECT: 'WALLET_CONNECT',
15 WALLET_CONNECT: 'WALLET_CONNECT',
@@ -28,7 +28,7 @@ const settingsIcon = <Icon icon="Gear" />;
28
28
29const OPTIONS = [
29const OPTIONS = [
30 {
30 {
31− text: 'Master Key',
31+ text: 'Master Ticket',
32 value: NAMES.MASTER_TICKET,
32 value: NAMES.MASTER_TICKET,
33 menuIcon: rightArrowIcon,
33 menuIcon: rightArrowIcon,
34 headerIcon: settingsIcon,
34 headerIcon: settingsIcon,
src/views/Point/Point.tsx+24−53
@@ -1,5 +1,4 @@
1import { useCallback, useEffect, useState } from 'react';
1import { useCallback, useEffect, useState } from 'react';
2−import { azimuth } from 'azimuth-js';
3import { Just } from 'folktale/maybe';
2import { Just } from 'folktale/maybe';
4import { Grid, Flex } from 'indigo-react';
3import { Grid, Flex } from 'indigo-react';
5import { Box, Icon, Row, Button, Checkbox } from '@tlon/indigo-react';
4import { Box, Icon, Row, Button, Checkbox } from '@tlon/indigo-react';
@@ -12,7 +11,6 @@ import View from 'components/View';
12import Greeting from 'components/Greeting';
11import Greeting from 'components/Greeting';
13import Passport from 'components/Passport';
12import Passport from 'components/Passport';
14import Blinky from 'components/Blinky';
13import Blinky from 'components/Blinky';
15−import { ForwardButton } from 'components/Buttons';
16import L2PointHeader from 'components/L2/Headers/L2PointHeader';
14import L2PointHeader from 'components/L2/Headers/L2PointHeader';
17import LayerIndicator from 'components/L2/LayerIndicator';
15import LayerIndicator from 'components/L2/LayerIndicator';
18import Card from 'components/L2/Card';
16import Card from 'components/L2/Card';
@@ -29,7 +27,7 @@ import { isL2 } from 'lib/utils/roller';
29import { isDevelopment } from 'lib/flags';
27import { isDevelopment } from 'lib/flags';
30import { usePointCache } from 'store/pointCache';
28import { usePointCache } from 'store/pointCache';
31import Modal from 'components/L2/Modal';
29import Modal from 'components/L2/Modal';
32−import { isPlanet } from 'lib/utils/point';
30+import { isPlanet, isStar } from 'lib/utils/point';
33import { useHasNetworkKeysSet } from 'lib/useHasNetworkKeysSet';
31import { useHasNetworkKeysSet } from 'lib/useHasNetworkKeysSet';
34import { InviteForm } from './InviteForm';
32import { InviteForm } from './InviteForm';
35
33
@@ -48,13 +46,21 @@ export default function Point() {
48 setInvites,
46 setInvites,
49 } = useRollerStore();
47 } = useRollerStore();
50 const networkKeysSet = useHasNetworkKeysSet();
48 const networkKeysSet = useHasNetworkKeysSet();
49+ const [seenMissingKeys, setSeenMissingKeys] = useSeenMissingKeys();
51 const [showModal, setShowModal] = useState(false);
50 const [showModal, setShowModal] = useState(false);
52− const [seenMissingKeys, setSeeingMissingKeys] = useSeenMissingKeys();
53 const [hideMessage, setHideMessage] = useState(false);
51 const [hideMessage, setHideMessage] = useState(false);
54
52
53+ const hideModal = useCallback(() => {
54+ if (hideMessage) {
55+ setSeenMissingKeys(true);
56+ }
57+
58+ setShowModal(false);
59+ }, [hideMessage, setShowModal, setSeenMissingKeys]);
60+
55 useEffect(() => {
61 useEffect(() => {
56− setShowModal(!networkKeysSet);
62+ setShowModal(!networkKeysSet && !seenMissingKeys);
57− }, [networkKeysSet]);
63+ }, [networkKeysSet, seenMissingKeys]);
58
64
59 const loadL1Info = useCallback(async () => {
65 const loadL1Info = useCallback(async () => {
60 await syncExtras(point);
66 await syncExtras(point);
@@ -84,7 +90,7 @@ export default function Point() {
84 setInvites([]);
90 setInvites([]);
85 }, []); // eslint-disable-line react-hooks/exhaustive-deps
91 }, []); // eslint-disable-line react-hooks/exhaustive-deps
86
92
87− const { isParent, canManage, canSpawn, canVote } = useCurrentPermissions();
93+ const { isParent, canManage } = useCurrentPermissions();
88
94
89 // L1 invites
95 // L1 invites
90 const l1Invites = useInvites(point);
96 const l1Invites = useInvites(point);
@@ -104,46 +110,22 @@ export default function Point() {
104
110
105 const loadedInvites = Just.hasInstance(availableInvites);
111 const loadedInvites = Just.hasInstance(availableInvites);
106
112
107− const goSenate = useCallback(() => push(names.SENATE), [push, names]);
108−
109 const goCohort = useCallback(() => push(names.INVITE_COHORT), [push, names]);
113 const goCohort = useCallback(() => push(names.INVITE_COHORT), [push, names]);
110
114
111 const goUrbitOS = useCallback(() => {
115 const goUrbitOS = useCallback(() => {
112− if (hideMessage) setSeeingMissingKeys(hideMessage);
116+ if (hideMessage) setSeenMissingKeys(hideMessage);
113
117
114 push(names.URBIT_OS);
118 push(names.URBIT_OS);
115− }, [push, names, hideMessage, setSeeingMissingKeys]);
119+ }, [push, names, hideMessage, setSeenMissingKeys]);
116
120
117 const goUrbitID = useCallback(() => push(names.URBIT_ID), [push, names]);
121 const goUrbitID = useCallback(() => push(names.URBIT_ID), [push, names]);
118
122
119 const goResidents = useCallback(() => push(names.RESIDENTS), [push, names]);
123 const goResidents = useCallback(() => push(names.RESIDENTS), [push, names]);
120
124
121− const goIssuePoint = useCallback(() => push(names.ISSUE_CHILD), [
122− names.ISSUE_CHILD,
123− push,
124− ]);
125−
126 const planet = isPlanet(point);
125 const planet = isPlanet(point);
127
126
128 const [showInviteForm, setShowInviteForm] = useState(false);
127 const [showInviteForm, setShowInviteForm] = useState(false);
129
128
130− const senateButton = (() => {
131− if (azimuth.getPointSize(point) !== azimuth.PointSize.Galaxy) {
132− return null;
133− }
134− return (
135− <>
136− <Grid.Item
137− full
138− as={ForwardButton}
139− disabled={!canVote}
140− onClick={goSenate}>
141− Senate
142− </Grid.Item>
143− <Grid.Divider />
144− </>
145− );
146− })();
147 const address = need.addressFromWallet(wallet);
129 const address = need.addressFromWallet(wallet);
148 const spawnedPending = pendingTransactions.filter(
130 const spawnedPending = pendingTransactions.filter(
149 ({ rawTx }) => rawTx?.tx?.type === 'spawn'
131 ({ rawTx }) => rawTx?.tx?.type === 'spawn'
@@ -220,8 +202,12 @@ export default function Point() {
220 {isParent && (
202 {isParent && (
221 <Grid.Item full as={Flex} justify="between">
203 <Grid.Item full as={Flex} justify="between">
222 <Card
204 <Card
223− title="Residency"
205+ title={`${isStar(point) ? 'Star' : 'Galaxy'} Ops`}
224− subtitle="Manage peers that you service"
206+ subtitle={
207+ isStar(point)
208+ ? 'Manage planets you sponsor and spawn planets'
209+ : 'Manage stars you sponsor, spawn stars, and vote'
210+ }
225 onClick={goResidents}
211 onClick={goResidents}
226 />
212 />
227 </Grid.Item>
213 </Grid.Item>
@@ -263,25 +249,10 @@ export default function Point() {
263 disabled={!canManage}
249 disabled={!canManage}
264 />
250 />
265 </Grid.Item>
251 </Grid.Item>
266− {isParent && (
267− <>
268− <Grid.Item
269− full
270− as={ForwardButton}
271− disabled={!canSpawn}
272− onClick={goIssuePoint}>
273− Issue Point
274− </Grid.Item>
275− <Grid.Divider />
276− </>
277− )}
278− {senateButton}
279 </Grid>
252 </Grid>
280− <Modal
281− show={showModal && !seenMissingKeys}
253+ <Modal show={showModal} hide={hideModal}>
282− hide={() => setShowModal(false)}>
283 <Box className="network-keys-modal">
254 <Box className="network-keys-modal">
284− <Box className="close" onClick={() => setShowModal(false)}>
255+ <Box className="close" onClick={hideModal}>
285 &#215;
256 &#215;
286 </Box>
257 </Box>
287 <Box className="title">No Network Keys Found</Box>
258 <Box className="title">No Network Keys Found</Box>
@@ -297,7 +268,7 @@ export default function Point() {
297 <Box className="dont-show">Do not warn me again</Box>
268 <Box className="dont-show">Do not warn me again</Box>
298 </Row>
269 </Row>
299 <Row className="buttons">
270 <Row className="buttons">
300− <Button className="cancel" onClick={() => setShowModal(false)}>
271+ <Button className="cancel" onClick={hideModal}>
301 Cancel
272 Cancel
302 </Button>
273 </Button>
303 <Button className="migrate" onClick={goUrbitOS}>
274 <Button className="migrate" onClick={goUrbitOS}>
src/views/Point/Residents.js+39
@@ -2,6 +2,7 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react';
2import cn from 'classnames';
2import cn from 'classnames';
3import { Grid, H4, HelpText } from 'indigo-react';
3import { Grid, H4, HelpText } from 'indigo-react';
4import { Just, Nothing } from 'folktale/maybe';
4import { Just, Nothing } from 'folktale/maybe';
5+import { azimuth } from 'azimuth-js';
5import * as ob from 'urbit-ob';
6import * as ob from 'urbit-ob';
6
7
7import View from 'components/View';
8import View from 'components/View';
@@ -10,9 +11,11 @@ import Crumbs from 'components/Crumbs';
10import Sigil from 'components/Sigil';
11import Sigil from 'components/Sigil';
11import Blinky from 'components/Blinky';
12import Blinky from 'components/Blinky';
12import NavHeader from 'components/NavHeader';
13import NavHeader from 'components/NavHeader';
14+import { ForwardButton } from 'components/Buttons';
13
15
14import { useLocalRouter } from 'lib/LocalRouter';
16import { useLocalRouter } from 'lib/LocalRouter';
15import useCurrentPointName from 'lib/useCurrentPointName';
17import useCurrentPointName from 'lib/useCurrentPointName';
18+import useCurrentPermissions from 'lib/useCurrentPermissions';
16import * as need from 'lib/need';
19import * as need from 'lib/need';
17
20
18import { usePointCursor } from 'store/pointCursor';
21import { usePointCursor } from 'store/pointCursor';
@@ -168,6 +171,13 @@ export default function Residents() {
168 const [currentTab, _setCurrentTab] = useState(NAMES.ALL);
171 const [currentTab, _setCurrentTab] = useState(NAMES.ALL);
169 const [page, setPage] = useState(0);
172 const [page, setPage] = useState(0);
170
173
174+ const goSenate = useCallback(() => push(names.SENATE), [push, names]);
175+
176+ const goIssuePoint = useCallback(() => push(names.ISSUE_CHILD), [
177+ names.ISSUE_CHILD,
178+ push,
179+ ]);
180+
171 const setCurrentTab = useCallback(
181 const setCurrentTab = useCallback(
172 tab => {
182 tab => {
173 setPage(0);
183 setPage(0);
@@ -199,6 +209,25 @@ export default function Residents() {
199 const onDecline = isRequests && _onDecline;
209 const onDecline = isRequests && _onDecline;
200
210
201 const points = isRequests ? requests : residents;
211 const points = isRequests ? requests : residents;
212+ const { canSpawn, canVote } = useCurrentPermissions();
213+
214+ const senateButton = (() => {
215+ if (azimuth.getPointSize(point) !== azimuth.PointSize.Galaxy) {
216+ return null;
217+ }
218+ return (
219+ <>
220+ <Grid.Item
221+ full
222+ as={ForwardButton}
223+ disabled={!canVote}
224+ onClick={goSenate}>
225+ Senate
226+ </Grid.Item>
227+ <Grid.Divider />
228+ </>
229+ );
230+ })();
202
231
203 return (
232 return (
204 <View pop={pop} inset>
233 <View pop={pop} inset>
@@ -211,6 +240,16 @@ export default function Residents() {
211 <Grid.Item full as={H4} className="mt4">
240 <Grid.Item full as={H4} className="mt4">
212 Residents
241 Residents
213 </Grid.Item>
242 </Grid.Item>
243+ <Grid.Divider />
244+ <Grid.Item
245+ full
246+ as={ForwardButton}
247+ disabled={!canSpawn}
248+ onClick={goIssuePoint}>
249+ Issue Point
250+ </Grid.Item>
251+ <Grid.Divider />
252+ {senateButton}
214 <Grid.Item
253 <Grid.Item
215 full
254 full
216 className="mt1"
255 className="mt1"
src/views/UrbitID/Home.tsx+9−24
@@ -30,14 +30,12 @@ export default function UrbitIDHome() {
30 const { urbitWallet }: any = useWallet();
30 const { urbitWallet }: any = useWallet();
31 const { currentPoint } = useRollerStore();
31 const { currentPoint } = useRollerStore();
32
32
33− const goSigil = useCallback(() => push(names.SIGIL_GENERATOR), [push, names]);
34−
35 const goDownloadKeys = useCallback(() => push(names.DOWNLOAD_KEYS), [
33 const goDownloadKeys = useCallback(() => push(names.DOWNLOAD_KEYS), [
36 push,
34 push,
37 names,
35 names,
38 ]);
36 ]);
39
37
40− const goUrbitOS = useCallback(() => push(names.URBIT_OS), [push, names]);
38+ const goSigil = useCallback(() => push(names.SIGIL_GENERATOR), [push, names]);
41
39
42 const goResetKeys = useCallback(() => push(names.RESET_KEYS), [push, names]);
40 const goResetKeys = useCallback(() => push(names.RESET_KEYS), [push, names]);
43
41
@@ -68,7 +66,11 @@ export default function UrbitIDHome() {
68 <HeaderPane>
66 <HeaderPane>
69 <Row className="header-row">
67 <Row className="header-row">
70 <h5>ID</h5>
68 <h5>ID</h5>
69+ {isMasterTicket && (
71− <Button className="header-button">Download Passport</Button>
70+ <Button onClick={goDownloadKeys} className="header-button">
71+ Download Passport
72+ </Button>
73+ )}
72 </Row>
74 </Row>
73 </HeaderPane>
75 </HeaderPane>
74 <BodyPane>
76 <BodyPane>
@@ -98,7 +100,7 @@ export default function UrbitIDHome() {
98 <Box>Management Address</Box>
100 <Box>Management Address</Box>
99 <div className="mt1 mono black subtitle">
101 <div className="mt1 mono black subtitle">
100 {noManagement
102 {noManagement
101− ? 'Unset'
103+ ? 'Not set'
102 : abbreviateAddress(details.managementProxy)}
104 : abbreviateAddress(details.managementProxy)}
103 </div>
105 </div>
104 </Box>
106 </Box>
@@ -148,7 +150,7 @@ export default function UrbitIDHome() {
148 <Box>Master Ticket</Box>
150 <Box>Master Ticket</Box>
149 <Box className="subtitle">
151 <Box className="subtitle">
150 {isMasterTicket
152 {isMasterTicket
151− ? 'Reset Master Key and all other keys'
153+ ? 'Reset Master Ticket and all other keys'
152 : 'Transfer to Master Ticket, resetting all keys'}
154 : 'Transfer to Master Ticket, resetting all keys'}
153 </Box>
155 </Box>
154 </Box>
156 </Box>
@@ -163,26 +165,9 @@ export default function UrbitIDHome() {
163 <Box>
165 <Box>
164 <Box>Network Keys Required</Box>
166 <Box>Network Keys Required</Box>
165 <Box className="subtitle">
167 <Box className="subtitle">
166− Set your network keys to spawn points
168+ Set your network keys in "OS" to spawn points
167− </Box>
168− </Box>
169− <Button className="secondary" onClick={goUrbitOS}>
170− Set Keys
171− </Button>
172− </Row>
173− )}
174−
175− {isMasterTicket && (
176− <Row className="between-row management">
177− <Box>
178− <Box>Download Keys</Box>
179− <Box className="subtitle">
180− Download the keys to your master ticket.
181 </Box>
169 </Box>
182 </Box>
170 </Box>
183− <Button className="secondary" onClick={goDownloadKeys}>
184− Download
185− </Button>
186 </Row>
171 </Row>
187 )}
172 )}
188
173
src/views/UrbitID/ResetKeys/ResetExecute.js+2−2
@@ -194,7 +194,7 @@ export default function ResetExecute({ newWallet, setNewWallet }) {
194
194
195 return (
195 return (
196 <Grid.Item full as={WarningBox} className="mt4">
196 <Grid.Item full as={WarningBox} className="mt4">
197− Never give your Master Key to anyone
197+ Never give your Master Ticket to anyone
198 </Grid.Item>
198 </Grid.Item>
199 );
199 );
200 };
200 };
@@ -218,7 +218,7 @@ export default function ResetExecute({ newWallet, setNewWallet }) {
218 className="mt4"
218 className="mt4"
219 accessory="↺"
219 accessory="↺"
220 onClick={loginAndGoHome}>
220 onClick={loginAndGoHome}>
221− Login with New Master Key
221+ Login with New Master Ticket
222 </Grid.Item>
222 </Grid.Item>
223 </>
223 </>
224 ) : (
224 ) : (
src/views/UrbitOS/Home.tsx+17−18
@@ -59,13 +59,15 @@ export default function UrbitOSHome() {
59 <HeaderPane>
59 <HeaderPane>
60 <Row className="header-row">
60 <Row className="header-row">
61 <h5>OS</h5>
61 <h5>OS</h5>
62+ {hasSetNetworkingKeys && (
62− <Button
63+ <Button
63− className="header-button keyfile"
64+ className="header-button keyfile"
64− disabled={!hasSetNetworkingKeys}
65+ disabled={!hasSetNetworkingKeys}
65− onClick={keyBind.download}>
66+ onClick={keyBind.download}>
66− <KeyfileIcon />
67+ <KeyfileIcon />
67− Download Keyfile
68+ Download Keyfile
68− </Button>
69+ </Button>
70+ )}
69 </Row>
71 </Row>
70 </HeaderPane>
72 </HeaderPane>
71 <BodyPane>
73 <BodyPane>
@@ -90,14 +92,19 @@ export default function UrbitOSHome() {
90 <Row className="between-row management">
92 <Row className="between-row management">
91 <Box>
93 <Box>
92 <Box>Network Keys</Box>
94 <Box>Network Keys</Box>
95+ <Box
93− <Box className="subtitle">
96+ className={`subtitle ${
97+ !hasSetNetworkingKeys ? 'error-text' : ''
98+ }`}>
94 {hasSetNetworkingKeys
99 {hasSetNetworkingKeys
95 ? `Revision: ${details.keyRevisionNumber}`
100 ? `Revision: ${details.keyRevisionNumber}`
96− : 'Unset'}
101+ : 'No network keys found'}
97 </Box>
102 </Box>
98 </Box>
103 </Box>
99 <Row>
104 <Row>
105+ <Button
106+ className={hasSetNetworkingKeys ? 'secondary' : 'primary'}
100− <Button className="secondary" onClick={goNetworkingKeys}>
107+ onClick={goNetworkingKeys}>
101 {hasSetNetworkingKeys ? 'Reset' : 'Initialize'}
108 {hasSetNetworkingKeys ? 'Reset' : 'Initialize'}
102 </Button>
109 </Button>
103 {hasSetNetworkingKeys && (
110 {hasSetNetworkingKeys && (
@@ -110,14 +117,6 @@ export default function UrbitOSHome() {
110
117
111 {showKeys && <Grid.Item full as={NetworkingKeys} point={point} />}
118 {showKeys && <Grid.Item full as={NetworkingKeys} point={point} />}
112
119
113− {!hasSetNetworkingKeys && (
114− <>
115− <Grid.Item full as={AlertBox} className="mt4">
116− Networking Keys are required to generate a keyfile
117− </Grid.Item>
118− </>
119− )}
120−
121 {!!code && (
120 {!!code && (
122 <Row className="between-row management">
121 <Row className="between-row management">
123 <Box>
122 <Box>