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