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