on da154dd help sign in
bridge/src/components/Passport.js
184 lines 5.4 kB history raw
1import React, { useMemo } from 'react';
2import cn from 'classnames';
3import { Just, Nothing } from 'folktale/maybe';
4import 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({
55 point,
56 className,
57 ticket = null,
58 address = Nothing(),
59 onClick,
60}) {
61 const birthday = usePointBirthday(point.getOrElse(null));
62 const visualBirthday = matchBlinkyDate(birthday);
64 const name = point.matchWith({
65 Nothing: () => Nothing(),
66 Just: p => Just(ob.patp(p.value)),
67 });
68 const visualName = name.matchWith({
69 Nothing: () => '...',
70 Just: p => p.value,
71 });
73 const showTicket = ticket !== null;
74 const isValidTicket = !!ticket;
75 const visibleTicket = isValidTicket ? VALID_TICKET : PENDING_TICKET;
76 const visibleAddress = `${address.matchWith({
77 Nothing: () => EMPTY_ADDRESS,
78 Just: p => p.value.slice(0, VISIBLE_ADDRESS_CHAR_COUNT),
79 })}${ADDRESS_BUFFER_CHARS}`;
81 return (
82 <Grid onClick={onClick} gap={4} className={cn('bg-black r8 p7', className)}>
83 <Grid.Item cols={[1, 4]}>
84 <MaybeSigil patp={name} size={64} />
85 </Grid.Item>
86 <Grid.Item as={Flex} cols={[4, 13]} col justify="between">
87 <Flex.Item className="mono white f5 pb2">{visualName}</Flex.Item>
88 <Flex.Item as={Flex} col>
89 <Flex.Item as={Text} className="mono f6 f5-ns gray4 uppercase">
90 Birth Time
91 </Flex.Item>
92 <Flex.Item as={Text} className="mono f6 f5-ns gray4 uppercase">
93 {visualBirthday}
94 </Flex.Item>
95 </Flex.Item>
96 </Grid.Item>
97 {showTicket && (
98 <Grid.Item full className="mv3" as={Flex} col>
99 <Flex.Item as={Text} className="mono f6 f5-ns gray4 uppercase">
100 Master Ticket
101 </Flex.Item>
102 <Flex.Item
103 as={Text}
104 className={cn('arial f6 f5-ns wrap', {
105 gray4: !isValidTicket,
106 green3: isValidTicket,
107 })}>
108 {visibleTicket}
109 </Flex.Item>
110 <Flex.Item as={Text} className="mono f6 f5-ns gray4 wrap">
111 {visibleAddress}
112 </Flex.Item>
113 </Grid.Item>
114 )}
115 </Grid>
116 );
119// TODO: Passport.Mini should probably accept Maybe<> for point
120// in order to display a pretty skeleton loading view
121// but that requires making usePermissionsForPoint accept a Maybe<point>
122// and I don't feel like that refactor is worth it right now, and the spec
123// doesn't call for it, hence the TODO
124Passport.Mini = function MiniPassport({ point, className, inverted, ...rest }) {
125 useSyncOwnedPoints([point]);
127 const { wallet } = useWallet();
128 const address = need.addressFromWallet(wallet);
130 const permissions = usePermissionsForPoint(address, point);
131 const keyType = buildKeyType(permissions);
132 const name = useMemo(() => ob.patp(point), [point]);
134 return (
135 <Grid
136 gap={4}
137 className={cn(
138 'r8 p4',
139 {
140 'bg-black': !inverted,
141 'bg-white b-gray4 b1': inverted,
142 },
143 className
144 )}
145 {...rest}>
146 <Grid.Item cols={[1, 4]}>
147 <Sigil
148 patp={name}
149 size={64}
150 colors={inverted ? INVERTED_COLORWAY : undefined}
151 />
152 </Grid.Item>
153 <Grid.Item as={Flex} cols={[4, 13]} col justify="between">
154 <Flex.Item
155 className={cn('mono f6 pb2', {
156 white: !inverted,
157 black: inverted,
158 })}>
159 {name}
160 </Flex.Item>
161 <Flex.Item as={Flex} col className={cn('mono f6 uppercase')}>
162 <Flex.Item
163 as={Text}
164 className={cn({
165 gray4: !inverted,
166 black: inverted,
167 })}>
168 Key Type
169 </Flex.Item>
170 <Flex.Item
171 as={Text}
172 className={cn({
173 white: !inverted,
174 black: inverted,
175 })}>
176 {keyType || <Blinky />}
177 </Flex.Item>
178 </Flex.Item>
179 </Grid.Item>
180 </Grid>
181 );
182};
184export default Passport;