on c19b2a8 help sign in
bridge/src/components/Passport.js
316 lines 8.2 kB history raw
1import React, { useMemo } from 'react';
2import { Just, Nothing } from 'folktale/maybe';
3import ob from 'urbit-ob';
4import BN from 'bn.js';
5import 'style/anim.css';
7import * as need from 'lib/need';
8import { chunkStr, Matrix, walk, rand } from 'lib/card';
9import usePermissionsForPoint from 'lib/usePermissionsForPoint';
10import { useSyncOwnedPoints } from 'lib/useSyncPoints';
11import { buildKeyType } from 'lib/point';
13import { useWallet } from 'store/wallet';
15import { sigil, reactRenderer } from 'urbit-sigil-js';
17function makeSigil(size, patp, colors) {
18 const config = {
19 patp: patp,
20 colors: colors,
21 margin: 0,
22 renderer: reactRenderer,
23 };
25 // Planet
26 if (patp.length === 14) {
27 return sigil({
28 patp: patp,
29 size: size,
30 ...config,
31 });
32 }
33 // Star
34 if (patp.length === 7) {
35 return sigil({
36 patp: patp,
37 width: size * 2,
38 height: size,
39 full: true,
40 ...config,
41 });
42 }
43 // Galaxy
44 if (patp.length === 4) {
45 return sigil({
46 patp: patp,
47 size: size,
48 full: true,
49 ...config,
50 });
51 }
54const symbols = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
56/**
57 * Address is Maybe<string>
58 * point is Maybe<string>
59 * animationMode is 'slide' | 'step' | 'none'
60 *
61 */
62function Passport({
63 address,
64 point,
65 inverted,
66 animationMode = 'none',
67 keyType,
68}) {
69 const [cols, rows, tile] = [35, 12, 12];
71 const loading = Nothing.hasInstance(address) || Nothing.hasInstance(point);
72 const defaultMatrix = () => {
73 return Matrix.new(rows, cols);
74 };
76 const patp = ob.patp(point.value);
78 const makeMatrix = addr => {
79 // remove the 0x
80 const onlyTheNumberPart = addr.substring(2);
81 // make bigNum from hex
82 const int = new BN(onlyTheNumberPart, 16);
83 // make a bigger number
84 const big = int.pow(new BN(10));
85 // parse that to a string and pad
86 const b10 = big.toString(10).padStart(32, '0');
87 // split the big number into parts
88 const chunkSize = Math.ceil(b10.length / 8);
89 const chunks = chunkStr(b10, chunkSize).map(c => new BN(c));
90 // generate an empty matrix
91 const startMatrix = Matrix.new(rows, cols);
92 // do a few random walks starting at diff coordinates
93 const [, matrix] = chunks.reduce((acc, chunk) => {
94 return walk(chunk, acc[1]);
95 // start with the 'random' points (not actually random, they are deterministic)
96 }, rand(big, startMatrix));
98 return matrix;
99 };
101 const matrix = address.matchWith({
102 Just: ({ value }) => makeMatrix(value),
103 Nothing: defaultMatrix,
104 });
106 const bgColor = inverted ? 'white' : 'black';
108 const fgColor = inverted ? 'black' : 'white';
110 const sigil = useMemo(
111 () => Just.hasInstance(point) && makeSigil(64, patp, [bgColor, fgColor]),
112 [patp, point, bgColor, fgColor]
113 );
114 return (
115 <div
116 style={{
117 fontFamily: 'Inter',
118 backgroundColor: bgColor,
119 border: inverted ? '2px solid #E6E6E6' : '2px solid black',
120 borderRadius: '20px',
121 marginBottom: '16px',
122 width: `${16 + 16 + cols * tile}px`,
123 minWidth: `${16 + 16 + cols * tile}px`,
124 display: 'flex',
125 flexDirection: 'column',
126 }}>
127 <div
128 style={{
129 height: '64px',
130 padding: '16px',
131 display: 'flex',
132 }}>
133 <div
134 style={{
135 width: '100%',
136 display: 'flex',
137 alignItems: 'center',
138 }}>
139 {sigil}
140 <div
141 style={{
142 marginLeft: '16px',
143 }}>
144 <div
145 style={{
146 fontWeight: '600',
147 color: fgColor,
148 fontSize: '16px',
149 fontFamily: 'Source Code Pro',
150 }}>
151 {Just.hasInstance(point) && patp}
152 </div>
153 {keyType !== '' ? (
154 <div
155 style={{
156 marginTop: '8px',
157 fontWeight: '500',
158 color: fgColor,
159 fontSize: '14px',
160 fontFamily: 'Source Code Pro',
161 }}>
162 {keyType}
163 </div>
164 ) : (
165 <div />
166 )}
167 </div>
168 </div>
169 </div>
170 <div
171 style={{
172 display: 'flex',
173 flexDirection: 'column',
174 padding: '16px',
175 paddingTop: '0px',
176 filter: inverted ? 'invert(1)' : '',
177 }}>
178 {matrix.map((row, _row) => {
179 return (
180 <div
181 key={`card:row:${_row}`}
182 style={{ display: 'flex', justifyContent: 'space-between' }}>
183 {row.map((v, _col) => {
184 return (
185 <Cell
186 row={_row}
187 col={_col}
188 value={typeof symbols[v] === 'undefined' ? 13 : v}
189 spriteSheet={'a'}
190 spriteSheetHeight={12}
191 spriteSheetWidth={168}
192 inverted={inverted}
193 loading={loading}
194 animationMode={animationMode}
195 />
196 );
197 })}
198 </div>
199 );
200 })}
201 </div>
202 </div>
203 );
206const Cell = props => {
207 return (
208 <div
209 key={`card:cell:${props.row}:${props.col}:${props.value}`}
210 className={
211 props.loading
212 ? 'blink'
213 : props.animationMode === 'none'
214 ? ''
215 : `${props.animationMode}-h-${props.value}`
216 }
217 style={{
218 backgroundImage: `url(/spritesheet_a.png)`,
219 backgroundPosition:
220 props.animationMode === 'none'
221 ? `-${props.spriteSheetHeight * props.value}px 0px`
222 : '',
223 backgroundSize: `${props.spriteSheetWidth}px ${props.spriteSheetHeight}px`,
224 width: `${props.spriteSheetHeight}px`,
225 maxWidth: `${props.spriteSheetHeight}px`,
226 height: `${props.spriteSheetHeight}px`,
227 maxHeight: `${props.spriteSheetHeight}px`,
228 animationDelay: `${props.value * 50}ms`,
229 }}
230 />
231 );
232};
234/**
235 * point is number
236 *
237 */
238function MiniPassport({ point, inverted, ...rest }) {
239 useSyncOwnedPoints([point]);
240 const { wallet } = useWallet();
241 const address = need.addressFromWallet(wallet);
243 const patp = ob.patp(point);
244 const permissions = usePermissionsForPoint(address, point);
245 const keyType = buildKeyType(permissions);
246 const sigil = useMemo(
247 () =>
248 makeSigil(44, patp, inverted ? ['white', 'black'] : ['black', 'white']),
249 [inverted, patp]
250 );
251 return (
252 <div
253 style={{
254 fontFamily: 'Inter',
255 backgroundColor: inverted ? 'white' : 'black',
256 border: inverted ? '2px solid #E6E6E6' : '2px solid black',
257 borderRadius: '20px',
258 marginBottom: '16px',
259 maxWidth: `${230}px`,
260 display: 'flex',
261 flexDirection: 'column',
262 }}
263 {...rest}>
264 <div
265 style={{
266 height: '44px',
267 padding: '16px',
268 display: 'flex',
269 }}>
270 <div
271 style={{
272 width: '100%',
273 display: 'flex',
274 alignItems: 'center',
275 }}>
276 {sigil}
277 <div
278 style={{
279 marginLeft: '16px',
280 display: 'flex',
281 flexDirection: 'column',
282 justifyContent: 'space-between',
283 }}>
284 <div
285 style={{
286 fontWeight: '600',
287 color: inverted ? 'black' : 'white',
288 fontSize: '14px',
289 fontFamily: 'Source Code Pro',
290 }}>
291 {patp}
292 </div>
293 {keyType !== '' ? (
294 <div
295 style={{
296 marginTop: '8px',
297 fontWeight: '500',
298 color: inverted ? 'black' : 'white',
299 fontSize: '12px',
300 fontFamily: 'Source Code Pro',
301 }}>
302 {keyType}
303 </div>
304 ) : (
305 <div />
306 )}
307 </div>
308 </div>
309 </div>
310 </div>
311 );
314Passport.Mini = MiniPassport;
316export default Passport;