Make icon generation more robust (#632)

* Make icon generation more robust

* Update packages/ui-shared/src/icons/polkadot.ts
This commit is contained in:
Jaco
2022-04-06 15:00:26 +02:00
committed by GitHub
parent fb360420e5
commit e598c7bb42
4 changed files with 39 additions and 12 deletions
@@ -4,15 +4,18 @@
import type { Props } from '../types'; import type { Props } from '../types';
import * as jdenticon from 'jdenticon'; import * as jdenticon from 'jdenticon';
import React from 'react'; import React, { useMemo } from 'react';
function Identicon ({ className = '', publicKey, size, style }: Props): React.ReactElement<Props> { function Identicon ({ className = '', publicKey, size, style }: Props): React.ReactElement<Props> {
const html = useMemo(
() => ({ __html: jdenticon.toSvg(publicKey.substr(2), size) }),
[publicKey, size]
);
return ( return (
<div <div
className={className} className={className}
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={html}
__html: jdenticon.toSvg(publicKey.substr(2), size)
}}
style={style} style={style}
/> />
); );
@@ -19,7 +19,7 @@
import type { Circle } from '@polkadot/ui-shared/icons/types'; import type { Circle } from '@polkadot/ui-shared/icons/types';
import type { Props } from '../types'; import type { Props } from '../types';
import React from 'react'; import React, { useMemo } from 'react';
import { polkadotIcon } from '@polkadot/ui-shared'; import { polkadotIcon } from '@polkadot/ui-shared';
@@ -36,6 +36,11 @@ function renderCircle ({ cx, cy, fill, r }: Circle, key: number): React.ReactNod
} }
function Identicon ({ address, className = '', isAlternative = false, size, style }: Props): React.ReactElement<Props> { function Identicon ({ address, className = '', isAlternative = false, size, style }: Props): React.ReactElement<Props> {
const circles = useMemo(
() => polkadotIcon(address, { isAlternative }),
[address, isAlternative]
);
return ( return (
<svg <svg
className={className} className={className}
@@ -46,7 +51,7 @@ function Identicon ({ address, className = '', isAlternative = false, size, styl
viewBox='0 0 64 64' viewBox='0 0 64 64'
width={size} width={size}
> >
{polkadotIcon(address, { isAlternative }).map(renderCircle)} {circles.map(renderCircle)}
</svg> </svg>
); );
} }
@@ -4,7 +4,7 @@
import type { Circle as CircleType } from '@polkadot/ui-shared/icons/types'; import type { Circle as CircleType } from '@polkadot/ui-shared/icons/types';
import type { Props } from '../types'; import type { Props } from '../types';
import React from 'react'; import React, { useMemo } from 'react';
import { View } from 'react-native'; import { View } from 'react-native';
import Svg, { Circle as SvgCircle } from 'react-native-svg'; import Svg, { Circle as SvgCircle } from 'react-native-svg';
@@ -23,6 +23,11 @@ function renderCircle ({ cx, cy, fill, r }: CircleType, key: number): React.Reac
} }
export default function Identicon ({ address, isAlternative = false, size }: Props): React.ReactElement<Props> { export default function Identicon ({ address, isAlternative = false, size }: Props): React.ReactElement<Props> {
const circles = useMemo(
() => polkadotIcon(address, { isAlternative }),
[address, isAlternative]
);
return ( return (
<View> <View>
<Svg <Svg
@@ -31,7 +36,7 @@ export default function Identicon ({ address, isAlternative = false, size }: Pro
viewBox='0 0 64 64' viewBox='0 0 64 64'
width={size} width={size}
> >
{polkadotIcon(address, { isAlternative }).map(renderCircle)} {circles.map(renderCircle)}
</Svg> </Svg>
</View> </View>
); );
+18 -4
View File
@@ -22,7 +22,6 @@ const blake2 = (value: Uint8Array): Uint8Array =>
const S = 64; const S = 64;
const C = S / 2; const C = S / 2;
const Z = S / 64 * 5; const Z = S / 64 * 5;
const ZERO = blake2(new Uint8Array(32));
/* eslint-disable sort-keys */ /* eslint-disable sort-keys */
const SCHEMA: { [index: string]: Scheme } = { const SCHEMA: { [index: string]: Scheme } = {
@@ -43,6 +42,8 @@ const OUTER_CIRCLE: Circle = {
r: C r: C
}; };
let zeroHash: Uint8Array = new Uint8Array();
function getRotation (isSixPoint: boolean): { r: number; ro2: number; r3o4: number; ro4: number; rroot3o2: number; rroot3o4: number } { function getRotation (isSixPoint: boolean): { r: number; ro2: number; r3o4: number; ro4: number; rroot3o2: number; rroot3o4: number } {
const r = isSixPoint const r = isSixPoint
? (C / 8 * 5) ? (C / 8 * 5)
@@ -98,7 +99,11 @@ function findScheme (d: number): Scheme {
} }
function addressToId (address: string): Uint8Array { function addressToId (address: string): Uint8Array {
return blake2(decodeAddress(address)).map((x, i): number => (x + 256 - ZERO[i]) % 256); if (!zeroHash.length) {
zeroHash = blake2(new Uint8Array(32));
}
return blake2(decodeAddress(address)).map((x, i) => (x + 256 - zeroHash[i]) % 256);
} }
function getColors (address: string): string[] { function getColors (address: string): string[] {
@@ -132,10 +137,19 @@ function getColors (address: string): string[] {
* @description Generate a array of the circles that make up an identicon * @description Generate a array of the circles that make up an identicon
*/ */
export function polkadotIcon (address: string, { isAlternative }: Options): Circle[] { export function polkadotIcon (address: string, { isAlternative }: Options): Circle[] {
const colors = getColors(address); const xy = getCircleXY(isAlternative);
let colors: string[];
try {
// in some cases, e.g. RN where crypto may not be initialized, chaos can
// happen when hashing, in these cases we just fill with a placeholder
colors = getColors(address);
} catch {
colors = new Array<string>(xy.length).fill('#ddd');
}
return [OUTER_CIRCLE].concat( return [OUTER_CIRCLE].concat(
getCircleXY(isAlternative).map(([cx, cy], index): Circle => ({ xy.map(([cx, cy], index): Circle => ({
cx, cy, fill: colors[index], r: Z cx, cy, fill: colors[index], r: Z
})) }))
); );