mirror of
https://github.com/pezkuwichain/pezkuwi-ui.git
synced 2026-07-24 21:45:43 +00:00
Move in ui-qr as react-qr (from apps) (#164)
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
// Copyright 2017-2019 @polkadot/react-qr authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BaseProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import qrcode from 'qrcode-generator';
|
||||
import styled from 'styled-components';
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
import { xxhashAsHex } from '@polkadot/util-crypto';
|
||||
|
||||
import { createSize } from './constants';
|
||||
import { encodeNumber, decodeString } from './util';
|
||||
|
||||
interface Props extends BaseProps {
|
||||
size?: number;
|
||||
value: Uint8Array;
|
||||
withMulti?: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
frames: string[];
|
||||
frameIdx: number;
|
||||
image: string | null;
|
||||
timerId: NodeJS.Timeout | null;
|
||||
valueHash: string | null;
|
||||
}
|
||||
|
||||
const FRAME_DELAY = 2100;
|
||||
const FRAME_SIZE = 1716;
|
||||
const MULTIPART = new Uint8Array([0]);
|
||||
|
||||
function getDataUrl (value: string): string {
|
||||
const qr = qrcode(0, 'M');
|
||||
|
||||
qr.addData(value, 'Byte');
|
||||
qr.make();
|
||||
|
||||
return qr.createDataURL(16, 0);
|
||||
}
|
||||
|
||||
function createFrames (input: Uint8Array): string[] {
|
||||
const frames = [];
|
||||
let idx = 0;
|
||||
|
||||
while (idx < input.length) {
|
||||
frames.push(input.subarray(idx, idx + FRAME_SIZE));
|
||||
|
||||
idx += FRAME_SIZE;
|
||||
}
|
||||
|
||||
return frames.map((frame, index: number): string =>
|
||||
decodeString(u8aConcat(
|
||||
MULTIPART,
|
||||
encodeNumber(frames.length),
|
||||
encodeNumber(index),
|
||||
frame
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
class Display extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
frames: [],
|
||||
frameIdx: 0,
|
||||
image: null,
|
||||
timerId: null,
|
||||
valueHash: null
|
||||
};
|
||||
|
||||
public static getDerivedStateFromProps ({ value, withMulti = true }: Props, prevState: State): Pick<State, never> | null {
|
||||
const valueHash = xxhashAsHex(value);
|
||||
|
||||
if (valueHash === prevState.valueHash) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const frames: string[] = withMulti
|
||||
? createFrames(value)
|
||||
: [decodeString(value)];
|
||||
|
||||
return {
|
||||
frames,
|
||||
frameIdx: 0,
|
||||
image: getDataUrl(frames[0]),
|
||||
valueHash
|
||||
};
|
||||
}
|
||||
|
||||
public componentDidMount (): void {
|
||||
this.setState({
|
||||
timerId: setInterval(this.nextFrame, FRAME_DELAY)
|
||||
});
|
||||
}
|
||||
|
||||
public componentWillUnmount (): void {
|
||||
const { timerId } = this.state;
|
||||
|
||||
if (timerId) {
|
||||
clearInterval(timerId);
|
||||
}
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { className, size, style } = this.props;
|
||||
const { image } = this.state;
|
||||
|
||||
if (!image) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
style={createSize(size)}
|
||||
>
|
||||
<div
|
||||
className='ui--qr-Display'
|
||||
style={style}
|
||||
>
|
||||
<img src={image} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private nextFrame = (): void => {
|
||||
const { frames, frameIdx } = this.state;
|
||||
|
||||
if (!frames || frames.length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextIdx = frameIdx === frames.length - 1
|
||||
? 0
|
||||
: frameIdx + 1;
|
||||
|
||||
this.setState({
|
||||
frameIdx: nextIdx,
|
||||
image: getDataUrl(frames[nextIdx])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default styled(Display as React.ComponentClass<Props>)`
|
||||
.ui--qr-Display {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
img,
|
||||
svg {
|
||||
background: white;
|
||||
height: auto !important;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2017-2019 @polkadot/react-qr authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BaseProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
import { xxhashAsHex } from '@polkadot/util-crypto';
|
||||
|
||||
import { ADDRESS_PREFIX } from './constants';
|
||||
import { encodeString } from './util';
|
||||
import QrDisplay from './Display';
|
||||
|
||||
interface Props extends BaseProps {
|
||||
address: string;
|
||||
}
|
||||
|
||||
interface State {
|
||||
data: Uint8Array | null;
|
||||
dataHash: string | null;
|
||||
}
|
||||
|
||||
const PREFIX = encodeString(ADDRESS_PREFIX);
|
||||
|
||||
export default class DisplayExtrinsic extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
data: null,
|
||||
dataHash: null
|
||||
};
|
||||
|
||||
public getDerivedStateFromProps ({ address }: Props, prevState: State): State | null {
|
||||
const data = u8aConcat(
|
||||
PREFIX,
|
||||
encodeString(address)
|
||||
);
|
||||
const dataHash = xxhashAsHex(data);
|
||||
|
||||
if (dataHash === prevState.dataHash) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { data, dataHash };
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { className, style } = this.props;
|
||||
const { data } = this.state;
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<QrDisplay
|
||||
className={className}
|
||||
style={style}
|
||||
value={data}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2017-2019 @polkadot/react-qr authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BaseProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
import { decodeAddress, xxhashAsHex } from '@polkadot/util-crypto';
|
||||
|
||||
import QrDisplay from './Display';
|
||||
|
||||
interface Props extends BaseProps {
|
||||
address: string;
|
||||
payload: Uint8Array;
|
||||
}
|
||||
|
||||
interface State {
|
||||
data: Uint8Array | null;
|
||||
dataHash: string | null;
|
||||
}
|
||||
|
||||
const SUBSTRATE = new Uint8Array([0x53]);
|
||||
const CRYPTO_SR25519 = new Uint8Array([0x01]);
|
||||
const SIGN_TX = new Uint8Array([0x00]);
|
||||
|
||||
export default class DisplayPayload extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
data: null,
|
||||
dataHash: null
|
||||
};
|
||||
|
||||
public getDerivedStateFromProps ({ address, payload }: Props, prevState: State): State | null {
|
||||
const data = u8aConcat(
|
||||
SUBSTRATE,
|
||||
CRYPTO_SR25519,
|
||||
SIGN_TX,
|
||||
decodeAddress(address),
|
||||
payload
|
||||
);
|
||||
const dataHash = xxhashAsHex(data);
|
||||
|
||||
if (dataHash === prevState.dataHash) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { data, dataHash };
|
||||
}
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { className, style } = this.props;
|
||||
const { data } = this.state;
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<QrDisplay
|
||||
className={className}
|
||||
style={style}
|
||||
value={data}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright 2017-2019 @polkadot/react-qr authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BaseProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import Reader from 'react-qr-reader';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { createSize } from './constants';
|
||||
|
||||
interface Props extends BaseProps {
|
||||
delay?: number;
|
||||
onError?: (error: Error) => void;
|
||||
onScan?: (data: string) => void;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
const DEFAULT_DELAY = 150;
|
||||
const DEFAULT_ERROR = (error: Error): void => {
|
||||
console.error('@polkadot/react-qr:Scan', error.message);
|
||||
};
|
||||
|
||||
class Scan extends React.PureComponent<Props> {
|
||||
public render (): React.ReactNode {
|
||||
const { className, delay = DEFAULT_DELAY, size, style } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
style={createSize(size)}
|
||||
>
|
||||
<Reader
|
||||
className='ui--qr-Scan'
|
||||
delay={delay}
|
||||
onError={this.onError}
|
||||
onScan={this.onScan}
|
||||
style={style}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private onError = (error: Error): void => {
|
||||
const { onError = DEFAULT_ERROR } = this.props;
|
||||
|
||||
onError(error);
|
||||
}
|
||||
|
||||
private onScan = (data: string | null): void => {
|
||||
const { onScan } = this.props;
|
||||
|
||||
if (!data || !onScan) {
|
||||
return;
|
||||
}
|
||||
|
||||
onScan(data);
|
||||
}
|
||||
}
|
||||
|
||||
export default styled(Scan)`
|
||||
.ui--qr-Scan {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
video {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2017-2019 @polkadot/react-qr authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BaseProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { ADDRESS_PREFIX } from './constants';
|
||||
import QrScan from './Scan';
|
||||
import { decodeAddress } from '@polkadot/util-crypto';
|
||||
|
||||
interface Props extends BaseProps {
|
||||
onError?: (error: Error) => void;
|
||||
onScan?: (data: string) => void;
|
||||
}
|
||||
|
||||
export default class ScanAddress extends React.PureComponent<Props> {
|
||||
public render (): React.ReactNode {
|
||||
const { className, onError, style } = this.props;
|
||||
|
||||
return (
|
||||
<QrScan
|
||||
className={className}
|
||||
onError={onError}
|
||||
onScan={this.onScan}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private onScan = (data: string | null): void => {
|
||||
const { onScan } = this.props;
|
||||
|
||||
if (!data || !onScan || !data.startsWith(ADDRESS_PREFIX)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const address = data.substr(ADDRESS_PREFIX.length);
|
||||
|
||||
try {
|
||||
decodeAddress(address);
|
||||
onScan(address);
|
||||
} catch (error) {
|
||||
console.error('@polkadot/react-qr:QrScanAddress', error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2017-2019 @polkadot/react-qr authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BaseProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import QrScan from './Scan';
|
||||
import { encodeString } from './util';
|
||||
|
||||
interface Props extends BaseProps {
|
||||
onError?: (error: Error) => void;
|
||||
onScan?: (data: Uint8Array) => void;
|
||||
}
|
||||
|
||||
export default class ScanSignature extends React.PureComponent<Props> {
|
||||
public render (): React.ReactNode {
|
||||
const { className, onError, style } = this.props;
|
||||
|
||||
return (
|
||||
<QrScan
|
||||
className={className}
|
||||
onError={onError}
|
||||
onScan={this.onScan}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private onScan = (data: string | null): void => {
|
||||
const { onScan } = this.props;
|
||||
|
||||
if (!data || !onScan) {
|
||||
return;
|
||||
}
|
||||
|
||||
onScan(encodeString(data));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2017-2019 @polkadot/react-qr authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
const DEFAULT_SIZE = 300;
|
||||
const ADDRESS_PREFIX = 'substrate:';
|
||||
|
||||
function createSize (size: number = DEFAULT_SIZE): Record<string, string> {
|
||||
const height = `${size}px`;
|
||||
|
||||
return {
|
||||
height,
|
||||
width: height
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
ADDRESS_PREFIX,
|
||||
DEFAULT_SIZE,
|
||||
createSize
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2017-2019 @polkadot/react-qr authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
export { default as QrDisplayAddress } from './DisplayAddress';
|
||||
export { default as QrDisplayPayload } from './DisplayPayload';
|
||||
export { default as QrScanAddress } from './ScanAddress';
|
||||
export { default as QrScanSignature } from './ScanSignature';
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2017-2019 @polkadot/react-qr authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
export interface BaseProps {
|
||||
className?: string;
|
||||
style?: Record<string, string | number>;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2017-2019 @polkadot/react-qr authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
export function encodeNumber (value: number): Uint8Array {
|
||||
return new Uint8Array([value >> 8, value & 256]);
|
||||
}
|
||||
|
||||
export function encodeString (value: string): Uint8Array {
|
||||
const u8a = new Uint8Array(value.length);
|
||||
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
u8a[i] = value.charCodeAt(i);
|
||||
}
|
||||
|
||||
return u8a;
|
||||
}
|
||||
|
||||
export function decodeString (value: Uint8Array): string {
|
||||
return value.reduce((str, code): string => {
|
||||
return str + String.fromCharCode(code);
|
||||
}, '');
|
||||
}
|
||||
Reference in New Issue
Block a user