on 64aee13 help sign in
bridge/src/components/Passport.js
178 lines 5.4 kB history raw
1import React, { useMemo } from 'react';
2import cn from 'classnames';
3import { Just, Nothing } from 'folktale/maybe';
4import * as ob from 'urbit-ob';
5import { Grid, Flex, Text } from 'indigo-react';
6import { times } from 'lodash';
8import * as need from 'lib/need';
9import usePointBirthday from 'lib/usePointBirthday';
11import Blinky, {
12 LOADING_CHARACTER,
13 INTERSTITIAL_CHARACTER,
14 matchBlinkyDate,
15} from './Blinky';
16import Sigil from './Sigil';
17import MaybeSigil from './MaybeSigil';
18import usePermissionsForPoint from 'lib/usePermissionsForPoint';
19import { useWallet } from 'store/wallet';
20import { useSyncOwnedPoints } from 'lib/useSyncPoints';
22const TICKET_CHAR_LENGTH = 30;
23const TICKET = times(TICKET_CHAR_LENGTH, i =>
24 i % 2 === 0 ? LOADING_CHARACTER : INTERSTITIAL_CHARACTER
25).join('');
26const PENDING_TICKET = TICKET;
27const VALID_TICKET = `${TICKET} ◆`;
29const VISIBLE_ADDRESS_CHAR_COUNT = 20;
30const EMPTY_ADDRESS = times(VISIBLE_ADDRESS_CHAR_COUNT, () => '<').join('');
31const ADDRESS_BUFFER_CHARS = times(40, () => '<').join('');
33const INVERTED_COLORWAY = ['#fff', '#000'];
35const buildKeyType = permissions => {
36 if (permissions.isOwner) {
37 return 'Ownership';
38 } else if (permissions.isManagementProxy) {
39 return 'Management';
40 } else if (permissions.isSpawnProxy) {
41 return 'Spawn';
42 } else if (permissions.isVotingProxy) {
43 return 'Voting';
44 } else if (permissions.isTransferProxy) {
45 return 'Transfer';
46 }
47};
49/**
50 * point is Maybe<number>
51 * ticket is null | boolean
52 * address is null | Maybe<string>
53 */
54function Passport({ point, className, ticket = null, address = Nothing() }) {
55 const birthday = usePointBirthday(point.getOrElse(null));
56 const visualBirthday = matchBlinkyDate(birthday);
58 const name = point.matchWith({
59 Nothing: () => Nothing(),
60 Just: p => Just(ob.patp(p.value)),
61 });
62 const visualName = name.matchWith({
63 Nothing: () => '...',
64 Just: p => p.value,
65 });
67 const showTicket = ticket !== null;
68 const isValidTicket = !!ticket;
69 const visibleTicket = isValidTicket ? VALID_TICKET : PENDING_TICKET;
70 const visibleAddress = `${address.matchWith({
71 Nothing: () => EMPTY_ADDRESS,
72 Just: p => p.value.slice(0, VISIBLE_ADDRESS_CHAR_COUNT),
73 })}${ADDRESS_BUFFER_CHARS}`;
75 return (
76 <Grid gap={4} className={cn('bg-black r8 p7', className)}>
77 <Grid.Item cols={[1, 4]}>
78 <MaybeSigil patp={name} size={64} />
79 </Grid.Item>
80 <Grid.Item as={Flex} cols={[4, 13]} col justify="between">
81 <Flex.Item className="mono white f5 pb2">{visualName}</Flex.Item>
82 <Flex.Item as={Flex} col>
83 <Flex.Item as={Text} className="mono f6 f5-ns gray4 uppercase">
84 Birth Time
85 </Flex.Item>
86 <Flex.Item as={Text} className="mono f6 f5-ns gray4 uppercase">
87 {visualBirthday}
88 </Flex.Item>
89 </Flex.Item>
90 </Grid.Item>
91 {showTicket && (
92 <Grid.Item full className="mv3" as={Flex} col>
93 <Flex.Item as={Text} className="mono f6 f5-ns gray4 uppercase">
94 Master Ticket
95 </Flex.Item>
96 <Flex.Item
97 as={Text}
98 className={cn('arial f6 f5-ns wrap', {
99 gray4: !isValidTicket,
100 green3: isValidTicket,
101 })}>
102 {visibleTicket}
103 </Flex.Item>
104 <Flex.Item as={Text} className="mono f6 f5-ns gray4 wrap">
105 {visibleAddress}
106 </Flex.Item>
107 </Grid.Item>
108 )}
109 </Grid>
110 );
113// TODO: Passport.Mini should probably accept Maybe<> for point
114// in order to display a pretty skeleton loading view
115// but that requires making usePermissionsForPoint accept a Maybe<point>
116// and I don't feel like that refactor is worth it right now, and the spec
117// doesn't call for it, hence the TODO
118Passport.Mini = function MiniPassport({ point, className, inverted, ...rest }) {
119 useSyncOwnedPoints([point]);
121 const { wallet } = useWallet();
122 const address = need.addressFromWallet(wallet);
124 const permissions = usePermissionsForPoint(address, point);
125 const keyType = buildKeyType(permissions);
126 const name = useMemo(() => ob.patp(point), [point]);
128 return (
129 <Grid
130 gap={4}
131 className={cn(
132 'r8 p4',
133 {
134 'bg-black': !inverted,
135 'bg-white b-gray4 b1': inverted,
136 },
137 className
138 )}
139 {...rest}>
140 <Grid.Item cols={[1, 4]}>
141 <Sigil
142 patp={name}
143 size={64}
144 colors={inverted ? INVERTED_COLORWAY : undefined}
145 />
146 </Grid.Item>
147 <Grid.Item as={Flex} cols={[4, 13]} col justify="between">
148 <Flex.Item
149 className={cn('mono f6 pb2', {
150 white: !inverted,
151 black: inverted,
152 })}>
153 {name}
154 </Flex.Item>
155 <Flex.Item as={Flex} col className={cn('mono f6 uppercase')}>
156 <Flex.Item
157 as={Text}
158 className={cn({
159 gray4: !inverted,
160 black: inverted,
161 })}>
162 Key Type
163 </Flex.Item>
164 <Flex.Item
165 as={Text}
166 className={cn({
167 white: !inverted,
168 black: inverted,
169 })}>
170 {keyType || <Blinky />}
171 </Flex.Item>
172 </Flex.Item>
173 </Grid.Item>
174 </Grid>
175 );
176};
178export default Passport;