Home Reference Source

packages/polkadot-identicon/lib/index.js

const React = require('react');
const { ReactiveComponent } = require('oo7-react');
const { ss58Decode, ss58Encode } = require('oo7-substrate');
const { blake2b } = require('blakejs');

const zero = blake2b(new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]));

const copyToClipboard = str => {
	const el = document.createElement('textarea');
	el.value = str;
	document.body.appendChild(el);
	el.select();
	document.execCommand('copy');
	document.body.removeChild(el);
};

export default class Identicon extends ReactiveComponent {
	constructor() {
		super(["account"]);
	}
	render() {
		let s = 64;
		let c = s / 2;
		let r = this.props.sixPoint ? s / 2 / 8 * 5 : s / 2 / 4 * 3;
		let rroot3o2 = r * Math.sqrt(3) / 2;
		let ro2 = r / 2;
		let rroot3o4 = r * Math.sqrt(3) / 4;
		let ro4 = r / 4;
		let r3o4 = r * 3 / 4;

		let z = s / 64 * 5;
		let schema = {
			target: { freq: 1, colors: [0, 28, 0, 0, 28, 0, 0, 28, 0, 0, 28, 0, 0, 28, 0, 0, 28, 0, 1] },
			cube: { freq: 20, colors: [0, 1, 3, 2, 4, 3, 0, 1, 3, 2, 4, 3, 0, 1, 3, 2, 4, 3, 5] },
			quazar: { freq: 16, colors: [1, 2, 3, 1, 2, 4, 5, 5, 4, 1, 2, 3, 1, 2, 4, 5, 5, 4, 0] },
			flower: { freq: 32, colors: [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 3] },
			cyclic: { freq: 32, colors: [0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6] },
			vmirror: { freq: 128, colors: [0, 1, 2, 3, 4, 5, 3, 4, 2, 0, 1, 6, 7, 8, 9, 7, 8, 6, 10] },
			hmirror: { freq: 128, colors: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 8, 6, 7, 5, 3, 4, 2, 11] }
		};

		let total = Object.keys(schema).map(k => schema[k].freq).reduce((a, b) => a + b);
		let findScheme = d => {
			let cum = 0;
			let ks = Object.keys(schema);
			for (let i in ks) {
				let n = schema[ks[i]].freq;
				cum += n;
				if (d < cum) {
					return schema[ks[i]];
				}
			}
			throw "Impossible";
		};

		let id = typeof this.state.account == 'string' ? ss58Decode(this.state.account) : this.state.account;
		if (!(typeof id == 'object' && id && id instanceof Uint8Array && id.length == 32)) {
			return React.createElement('svg', {
				id: this.props.id,
				name: this.props.name,
				className: this.props.className,
				style: this.props.style,
				width: this.props.width || this.props.size,
				height: this.props.height || this.props.size,
				viewBox: '0 0 64 64'
			});
		}
		let ss58 = ss58Encode(id);
		id = Array.from(blake2b(id)).map((x, i) => (x + 256 - zero[i]) % 256);

		let sat = Math.floor(id[29] * 70 / 256 + 26) % 80 + 30;
		let d = Math.floor((id[30] + id[31] * 256) % total);
		let scheme = findScheme(d);
		let palette = Array.from(id).map((x, i) => {
			let b = (x + i % 28 * 58) % 256;
			if (b == 0) {
				return '#444';
			}
			if (b == 255) {
				return 'transparent';
			}
			let h = Math.floor(b % 64 * 360 / 64);
			let l = [53, 15, 35, 75][Math.floor(b / 64)];
			return `hsl(${h}, ${sat}%, ${l}%)`;
		});

		let rot = id[28] % 6 * 3;

		let colors = scheme.colors.map((_, i) => palette[scheme.colors[i < 18 ? (i + rot) % 18 : 18]]);

		let i = 0;
		return React.createElement(
			'svg',
			{
				id: this.props.id,
				name: this.props.name,
				className: this.props.className,
				style: this.props.style,
				width: this.props.width || this.props.size,
				height: this.props.height || this.props.size,
				viewBox: '0 0 64 64',
				onClick: () => {
					copyToClipboard(ss58);this.props.onCopied && this.props.onCopied(ss58);
				}
			},
			React.createElement('circle', { cx: s / 2, cy: s / 2, r: s / 2, fill: '#eee' }),
			React.createElement('circle', { cx: c, cy: c - r, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c, cy: c - ro2, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c - rroot3o4, cy: c - r3o4, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c - rroot3o2, cy: c - ro2, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c - rroot3o4, cy: c - ro4, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c - rroot3o2, cy: c, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c - rroot3o2, cy: c + ro2, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c - rroot3o4, cy: c + ro4, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c - rroot3o4, cy: c + r3o4, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c, cy: c + r, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c, cy: c + ro2, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c + rroot3o4, cy: c + r3o4, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c + rroot3o2, cy: c + ro2, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c + rroot3o4, cy: c + ro4, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c + rroot3o2, cy: c, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c + rroot3o2, cy: c - ro2, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c + rroot3o4, cy: c - ro4, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c + rroot3o4, cy: c - r3o4, r: z, fill: colors[i++] }),
			React.createElement('circle', { cx: c, cy: c, r: z, fill: colors[i++] })
		);
	}
}