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