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