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