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