Mobile signer support (via Qr) (#134)
* Add typings to messages * Fix eslint (after merge) * Mobile signer support (via Qr) * Bump common * Pass through genesisHash * Ok, well, it does something... not recognized * Bump ui * Exclude build from lint * ** * Display accounts with prefix from genesisHash * Adapth for object via scan interface * Bump deps * Split address recoding * Cleanup decoding readability * Display chain affinity overlay * -1 prefix dropdown description * Add generator command * Fix TypeScript error * Lint * Consistent naming * Better promise error handling * Small consistency updates * Constraint handle typings as much as tsc will allow * State naming * Typo in cheain descriptions * Add signature handler (unused) * e2e roundtrip * remove (useless) dev 0.5.1 chain * Insert ActionBar * Revert to empty Props interfaces * SeedLengths type * Add type to transport subscription message * nit * Typecheck passing callback to sendMessage when none expected * Bump api * Small lint fixes * Bump api * Not sure it works... but linting passes * Split public/private * Alphabetical * Don't show button on no media * Remove unneeded resolve - NOT hacking around linting * Pass edited name through to accounts component * genesisHash alignments * Remove KeyringJson * SignRequest with account * Align with master naming * Some manual merges * Manual (small) conflict resolution * Fixup extension account mapping * Remove ununsed messages (less code = better) * Update comment around generation * Add copy-to-apps script (debugging)
This commit is contained in:
parent
bf92e522f2
commit
4945417982
@ -1,3 +1,7 @@
|
|||||||
|
# 0.10.0-beta.x
|
||||||
|
|
||||||
|
- Support for external accounts, as presented by mobile signers, e.g. the Parity Signer
|
||||||
|
|
||||||
# 0.9.1
|
# 0.9.1
|
||||||
|
|
||||||
- Fix an initialization error in extension-dapp
|
- Fix an initialization error in extension-dapp
|
||||||
|
|||||||
@ -77,6 +77,8 @@ interface Injected {
|
|||||||
interface Account = {
|
interface Account = {
|
||||||
// ss-58 encoded address
|
// ss-58 encoded address
|
||||||
readonly address: string;
|
readonly address: string;
|
||||||
|
// the genesisHash for this account (empty if applicable to all)
|
||||||
|
readonly genesisHash?: string;
|
||||||
// (optional) name for display
|
// (optional) name for display
|
||||||
readonly name?: string;
|
readonly name?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
16
copy-to-apps.sh
Executable file
16
copy-to-apps.sh
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function copy_folder () {
|
||||||
|
SRC="packages/$1/build"
|
||||||
|
DST="../apps/node_modules/@polkadot/$1"
|
||||||
|
|
||||||
|
echo "** Copying $SRC to $DST"
|
||||||
|
|
||||||
|
rm -rf $DST
|
||||||
|
cp -r $SRC $DST
|
||||||
|
}
|
||||||
|
|
||||||
|
yarn polkadot-dev-build-ts
|
||||||
|
|
||||||
|
copy_folder "extension-dapp"
|
||||||
|
copy_folder "extension-inject"
|
||||||
@ -3,7 +3,7 @@ const config = require('@polkadot/dev/config/jest');
|
|||||||
|
|
||||||
module.exports = Object.assign({}, config, {
|
module.exports = Object.assign({}, config, {
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'@polkadot/extension-(ui)(.*)$': '<rootDir>/packages/extension-$1/src/$2',
|
'@polkadot/extension-(chains|dapp|inject|ui)(.*)$': '<rootDir>/packages/extension-$1/src/$2',
|
||||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 'empty/object',
|
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 'empty/object',
|
||||||
'\\.(css|less)$': 'empty/object'
|
'\\.(css|less)$': 'empty/object'
|
||||||
},
|
},
|
||||||
|
|||||||
201
packages/extension-chains/LICENSE
Normal file
201
packages/extension-chains/LICENSE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
3
packages/extension-chains/README.md
Normal file
3
packages/extension-chains/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# @polkadot/extension-chains
|
||||||
|
|
||||||
|
Definitions for chains that are supported by this extension. It contains the bare definitions as well as a stripped-down (call-only) metadata format.
|
||||||
13
packages/extension-chains/package.json
Normal file
13
packages/extension-chains/package.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "@polkadot/extension-chains",
|
||||||
|
"description": "Definitions for all known chains as exposed by the extension.",
|
||||||
|
"version": "0.10.0-beta.1",
|
||||||
|
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||||
|
"license": "Apache-2",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.5.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@polkadot/api": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,9 @@
|
|||||||
|
// Copyright 2019 @polkadot/extension-chains authors & contributors
|
||||||
|
// This software may be modified and distributed under the terms
|
||||||
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
|
// Generated via (in api) `yarn chain:info --ws wss://poc3-rpc.polkadot.io`
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
chain: 'Alexander',
|
chain: 'Alexander',
|
||||||
genesisHash: '0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b',
|
genesisHash: '0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b',
|
||||||
40
packages/extension-chains/src/index.ts
Normal file
40
packages/extension-chains/src/index.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2019 @polkadot/extension-chains 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 { Metadata } from '@polkadot/types';
|
||||||
|
import { Chain } from './types';
|
||||||
|
|
||||||
|
// imports chain details, generally metadata. For the generation of these,
|
||||||
|
// inside the api, run `yarn chain:info [--ws <url>]`
|
||||||
|
import alexander from './alexander';
|
||||||
|
import kusama from './kusama';
|
||||||
|
|
||||||
|
const chains: Map<string, Chain> = new Map(
|
||||||
|
[alexander, kusama].map(
|
||||||
|
({ chain, genesisHash, metaCalls, ss58Format, tokenDecimals, tokenSymbol }): [string, Chain] => [
|
||||||
|
genesisHash,
|
||||||
|
{
|
||||||
|
genesisHash,
|
||||||
|
meta: metaCalls
|
||||||
|
? new Metadata(Buffer.from(metaCalls, 'base64'))
|
||||||
|
: undefined,
|
||||||
|
name: chain,
|
||||||
|
ss58Format,
|
||||||
|
tokenDecimals,
|
||||||
|
tokenSymbol
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const UNKNOWN_CHAIN: Chain = {
|
||||||
|
name: 'Any',
|
||||||
|
ss58Format: 42,
|
||||||
|
tokenDecimals: 0,
|
||||||
|
tokenSymbol: 'UNIT'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function findChain (genesisHash?: string | null): Chain {
|
||||||
|
return chains.get(genesisHash || '') || UNKNOWN_CHAIN;
|
||||||
|
}
|
||||||
@ -1,3 +1,9 @@
|
|||||||
|
// Copyright 2019 @polkadot/extension-chains authors & contributors
|
||||||
|
// This software may be modified and distributed under the terms
|
||||||
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
|
// Generated via (in api) `yarn chain:info --ws wss://kusama-rpc.polkadot.io`
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
chain: 'Kusama CC1',
|
chain: 'Kusama CC1',
|
||||||
genesisHash: '0x3fd7b9eb6a00376e5be61f01abb429ffb0b104be05eaff4d458da48fcd425baf',
|
genesisHash: '0x3fd7b9eb6a00376e5be61f01abb429ffb0b104be05eaff4d458da48fcd425baf',
|
||||||
14
packages/extension-chains/src/types.ts
Normal file
14
packages/extension-chains/src/types.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Copyright 2019 @polkadot/extension-chains 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 { Metadata } from '@polkadot/types';
|
||||||
|
|
||||||
|
export interface Chain {
|
||||||
|
name: string;
|
||||||
|
genesisHash?: string;
|
||||||
|
meta?: Metadata;
|
||||||
|
ss58Format: number;
|
||||||
|
tokenDecimals: number;
|
||||||
|
tokenSymbol: string;
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"@polkadot/react-identicon": "^0.43.0-beta.11",
|
"@polkadot/react-identicon": "^0.43.0-beta.11",
|
||||||
|
"@polkadot/react-qr": "^0.43.0-beta.10",
|
||||||
"@types/react-router": "^5.0.3",
|
"@types/react-router": "^5.0.3",
|
||||||
"@types/react-router-dom": "^4.3.5",
|
"@types/react-router-dom": "^4.3.5",
|
||||||
"react-router": "^5.0.1",
|
"react-router": "^5.0.1",
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
|
import { AccountJson } from '@polkadot/extension/background/types';
|
||||||
|
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
@ -9,7 +11,7 @@ import { ActionBar, ActionContext, Address, Link } from '../../components';
|
|||||||
import { editAccount } from '../../messaging';
|
import { editAccount } from '../../messaging';
|
||||||
import { Name } from '../../partials';
|
import { Name } from '../../partials';
|
||||||
|
|
||||||
interface Props {
|
interface Props extends AccountJson {
|
||||||
address: string;
|
address: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
@ -19,22 +21,22 @@ function Account ({ address, className }: Props): React.ReactElement<Props> {
|
|||||||
const [isEditing, setEditing] = useState(false);
|
const [isEditing, setEditing] = useState(false);
|
||||||
const [editedName, setName] = useState<string | null>(null);
|
const [editedName, setName] = useState<string | null>(null);
|
||||||
|
|
||||||
const toggleEdit = (): void =>
|
const _toggleEdit = (): void => setEditing(!isEditing);
|
||||||
setEditing(!isEditing);
|
const _saveChanges = (): void => {
|
||||||
const saveChanges = (): void => {
|
|
||||||
if (editedName && editedName !== name) {
|
if (editedName && editedName !== name) {
|
||||||
editAccount(address, editedName)
|
editAccount(address, editedName)
|
||||||
.then((): void => onAction())
|
.then((): void => onAction())
|
||||||
.catch((error: Error) => console.error(error));
|
.catch((error: Error) => console.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleEdit();
|
_toggleEdit();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Address
|
<Address
|
||||||
address={address}
|
address={address}
|
||||||
className={className}
|
className={className}
|
||||||
|
name={editedName}
|
||||||
>
|
>
|
||||||
{isEditing && (
|
{isEditing && (
|
||||||
<Name
|
<Name
|
||||||
@ -42,12 +44,12 @@ function Account ({ address, className }: Props): React.ReactElement<Props> {
|
|||||||
className='edit-name'
|
className='edit-name'
|
||||||
isFocussed
|
isFocussed
|
||||||
label={null}
|
label={null}
|
||||||
onBlur={saveChanges}
|
onBlur={_saveChanges}
|
||||||
onChange={setName}
|
onChange={setName}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ActionBar>
|
<ActionBar>
|
||||||
<Link onClick={toggleEdit}>Edit</Link>
|
<Link onClick={_toggleEdit}>Edit</Link>
|
||||||
<Link to={`/account/forget/${address}`}>Forget</Link>
|
<Link to={`/account/forget/${address}`}>Forget</Link>
|
||||||
</ActionBar>
|
</ActionBar>
|
||||||
</Address>
|
</Address>
|
||||||
@ -56,7 +58,7 @@ function Account ({ address, className }: Props): React.ReactElement<Props> {
|
|||||||
|
|
||||||
export default styled(Account)`
|
export default styled(Account)`
|
||||||
.edit-name {
|
.edit-name {
|
||||||
left: 4.75rem;
|
left: 3rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0.75rem;
|
right: 0.75rem;
|
||||||
top: -0.5rem;
|
top: -0.5rem;
|
||||||
|
|||||||
@ -4,11 +4,14 @@
|
|||||||
|
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
|
||||||
import { AccountContext, Button, Header, Link, Tip } from '../../components';
|
import { AccountContext, Button, Header, Link, MediaContext, Tip } from '../../components';
|
||||||
import Account from './Account';
|
import Account from './Account';
|
||||||
|
|
||||||
export default function Accounts (): React.ReactElement<{}> {
|
type Props = {};
|
||||||
|
|
||||||
|
export default function Accounts (): React.ReactElement<Props> {
|
||||||
const accounts = useContext(AccountContext);
|
const accounts = useContext(AccountContext);
|
||||||
|
const mediaAllowed = useContext(MediaContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -19,10 +22,10 @@ export default function Accounts (): React.ReactElement<{}> {
|
|||||||
{
|
{
|
||||||
(accounts.length === 0)
|
(accounts.length === 0)
|
||||||
? <Tip header='add accounts' type='warn'>You currently don't have any accounts. Either create a new account or if you have an existing account you wish to use, import it with the seed phrase</Tip>
|
? <Tip header='add accounts' type='warn'>You currently don't have any accounts. Either create a new account or if you have an existing account you wish to use, import it with the seed phrase</Tip>
|
||||||
: accounts.map(({ address }): React.ReactNode => (
|
: accounts.map((json, index): React.ReactNode => (
|
||||||
<Account
|
<Account
|
||||||
address={address}
|
{...json}
|
||||||
key={address}
|
key={`${index}:${json.address}`}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -32,8 +35,14 @@ export default function Accounts (): React.ReactElement<{}> {
|
|||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
label='I have a pre-existing seed, import the account'
|
label='I have a pre-existing seed, import the account'
|
||||||
to='/account/import'
|
to='/account/import-seed'
|
||||||
/>
|
/>
|
||||||
|
{mediaAllowed && (
|
||||||
|
<Button
|
||||||
|
label='I have an external account, add it via QR'
|
||||||
|
to='/account/import-qr'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,11 +20,11 @@ interface Props {
|
|||||||
|
|
||||||
function Request ({ authId, className, isFirst, request: { origin }, url }: Props): React.ReactElement<Props> {
|
function Request ({ authId, className, isFirst, request: { origin }, url }: Props): React.ReactElement<Props> {
|
||||||
const onAction = useContext(ActionContext);
|
const onAction = useContext(ActionContext);
|
||||||
const onApprove = (): Promise<void> =>
|
const _onApprove = (): Promise<void> =>
|
||||||
approveAuthRequest(authId)
|
approveAuthRequest(authId)
|
||||||
.then((): void => onAction())
|
.then((): void => onAction())
|
||||||
.catch((error: Error) => console.error(error));
|
.catch((error: Error) => console.error(error));
|
||||||
const onReject = (): Promise<void> =>
|
const _onReject = (): Promise<void> =>
|
||||||
rejectAuthRequest(authId)
|
rejectAuthRequest(authId)
|
||||||
.then((): void => onAction())
|
.then((): void => onAction())
|
||||||
.catch((error: Error) => console.error(error));
|
.catch((error: Error) => console.error(error));
|
||||||
@ -35,7 +35,7 @@ function Request ({ authId, className, isFirst, request: { origin }, url }: Prop
|
|||||||
icon={
|
icon={
|
||||||
<Icon
|
<Icon
|
||||||
icon='X'
|
icon='X'
|
||||||
onClick={onReject}
|
onClick={_onReject}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
intro={
|
intro={
|
||||||
@ -43,14 +43,14 @@ function Request ({ authId, className, isFirst, request: { origin }, url }: Prop
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ActionBar>
|
<ActionBar>
|
||||||
<Link isDanger onClick={onReject}>Reject</Link>
|
<Link isDanger onClick={_onReject}>Reject</Link>
|
||||||
</ActionBar>
|
</ActionBar>
|
||||||
{isFirst && (
|
{isFirst && (
|
||||||
<>
|
<>
|
||||||
<Tip header='access' type='warn'>Only approve this request if you trust the application. Approving gives the application access to the addresses of your accounts.</Tip>
|
<Tip header='access' type='warn'>Only approve this request if you trust the application. Approving gives the application access to the addresses of your accounts.</Tip>
|
||||||
<Button
|
<Button
|
||||||
label='Yes, allow this application access'
|
label='Yes, allow this application access'
|
||||||
onClick={onApprove}
|
onClick={_onApprove}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export default function Create (): React.ReactElement<Props> {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// FIXME Duplicated between here and Import.tsx
|
// FIXME Duplicated between here and Import.tsx
|
||||||
const onCreate = (): void => {
|
const _onCreate = (): void => {
|
||||||
// this should always be the case
|
// this should always be the case
|
||||||
if (name && password && account) {
|
if (name && password && account) {
|
||||||
createAccountSuri(name, password, account.seed)
|
createAccountSuri(name, password, account.seed)
|
||||||
@ -55,7 +55,7 @@ export default function Create (): React.ReactElement<Props> {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
label='Add the account with the generated seed'
|
label='Add the account with the generated seed'
|
||||||
onClick={onCreate}
|
onClick={_onCreate}
|
||||||
/>
|
/>
|
||||||
</Address>
|
</Address>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -13,7 +13,8 @@ type Props = RouteComponentProps<{ address: string }>;
|
|||||||
|
|
||||||
function Forget ({ match: { params: { address } } }: Props): React.ReactElement<Props> {
|
function Forget ({ match: { params: { address } } }: Props): React.ReactElement<Props> {
|
||||||
const onAction = useContext(ActionContext);
|
const onAction = useContext(ActionContext);
|
||||||
const onClick = (): Promise<void> =>
|
|
||||||
|
const _onClick = (): Promise<void> =>
|
||||||
forgetAccount(address)
|
forgetAccount(address)
|
||||||
.then((): void => onAction('/'))
|
.then((): void => onAction('/'))
|
||||||
.catch((error: Error) => console.error(error));
|
.catch((error: Error) => console.error(error));
|
||||||
@ -27,7 +28,7 @@ function Forget ({ match: { params: { address } } }: Props): React.ReactElement<
|
|||||||
<Button
|
<Button
|
||||||
isDanger
|
isDanger
|
||||||
label='I want to forget this account'
|
label='I want to forget this account'
|
||||||
onClick={onClick}
|
onClick={_onClick}
|
||||||
/>
|
/>
|
||||||
</Address>
|
</Address>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
54
packages/extension-ui/src/Popup/ImportQr.tsx
Normal file
54
packages/extension-ui/src/Popup/ImportQr.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2019 @polkadot/extension-ui 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 React, { useContext, useState } from 'react';
|
||||||
|
import { QrScanAddress } from '@polkadot/react-qr';
|
||||||
|
|
||||||
|
import { ActionContext, Address, Button, Header } from '../components';
|
||||||
|
import { createAccountExternal } from '../messaging';
|
||||||
|
import { Back, Name } from '../partials';
|
||||||
|
|
||||||
|
type Props = {};
|
||||||
|
|
||||||
|
export default function ImportQr (): React.ReactElement<Props> {
|
||||||
|
const onAction = useContext(ActionContext);
|
||||||
|
const [account, setAccount] = useState<null | { address: string; genesisHash: string }>(null);
|
||||||
|
const [name, setName] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// FIXME Duplicated between here and Create.tsx
|
||||||
|
const _onCreate = (): void => {
|
||||||
|
if (account && name) {
|
||||||
|
createAccountExternal(name, account.address, account.genesisHash)
|
||||||
|
.then((): void => onAction('/'))
|
||||||
|
.catch((error: Error) => console.error(error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Header label='import external' />
|
||||||
|
<Back />
|
||||||
|
{!account && <QrScanAddress onScan={setAccount} />}
|
||||||
|
{account && (
|
||||||
|
<>
|
||||||
|
<Name
|
||||||
|
isFocussed
|
||||||
|
onChange={setName}
|
||||||
|
/>
|
||||||
|
<Address
|
||||||
|
{...account}
|
||||||
|
name={name}
|
||||||
|
>
|
||||||
|
{name && (
|
||||||
|
<Button
|
||||||
|
label='Add the account with identified address'
|
||||||
|
onClick={_onCreate}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Address>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -16,13 +16,13 @@ export default function Import (): React.ReactElement<Props> {
|
|||||||
const [name, setName] = useState<string | null>(null);
|
const [name, setName] = useState<string | null>(null);
|
||||||
const [password, setPassword] = useState<string | null>(null);
|
const [password, setPassword] = useState<string | null>(null);
|
||||||
|
|
||||||
const onChangeSeed = (suri: string): Promise<void> =>
|
const _onChangeSeed = (suri: string): Promise<void> =>
|
||||||
validateSeed(suri)
|
validateSeed(suri)
|
||||||
.then(setAccount)
|
.then(setAccount)
|
||||||
.catch((): void => setAccount(null));
|
.catch((): void => setAccount(null));
|
||||||
|
|
||||||
// FIXME Duplicated between here and Create.tsx
|
// FIXME Duplicated between here and Create.tsx
|
||||||
const onCreate = (): void => {
|
const _onCreate = (): void => {
|
||||||
// this should always be the case
|
// this should always be the case
|
||||||
if (name && password && account) {
|
if (name && password && account) {
|
||||||
createAccountSuri(name, password, account.suri)
|
createAccountSuri(name, password, account.suri)
|
||||||
@ -39,7 +39,7 @@ export default function Import (): React.ReactElement<Props> {
|
|||||||
isError={!account}
|
isError={!account}
|
||||||
isFocussed
|
isFocussed
|
||||||
label='existing 12 or 24-word mnemonic seed'
|
label='existing 12 or 24-word mnemonic seed'
|
||||||
onChange={onChangeSeed}
|
onChange={_onChangeSeed}
|
||||||
/>
|
/>
|
||||||
{account && <Name onChange={setName} />}
|
{account && <Name onChange={setName} />}
|
||||||
{account && name && <Password onChange={setPassword} />}
|
{account && name && <Password onChange={setPassword} />}
|
||||||
@ -50,7 +50,7 @@ export default function Import (): React.ReactElement<Props> {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
label='Add the account with the supplied seed'
|
label='Add the account with the supplied seed'
|
||||||
onClick={onCreate}
|
onClick={_onCreate}
|
||||||
/>
|
/>
|
||||||
</Address>
|
</Address>
|
||||||
)}
|
)}
|
||||||
@ -13,7 +13,7 @@ import { Back } from '../../partials';
|
|||||||
|
|
||||||
const options = settings.availablePrefixes.map(({ text, value }): { text: string; value: string } => ({
|
const options = settings.availablePrefixes.map(({ text, value }): { text: string; value: string } => ({
|
||||||
text: value === -1
|
text: value === -1
|
||||||
? 'Substrate (default)'
|
? 'Default (Substrate or as specified)'
|
||||||
: text,
|
: text,
|
||||||
value: `${value}`
|
value: `${value}`
|
||||||
}));
|
}));
|
||||||
|
|||||||
@ -3,12 +3,13 @@
|
|||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { BlockNumber, ExtrinsicEra, ExtrinsicPayload } from '@polkadot/types/interfaces';
|
import { BlockNumber, ExtrinsicEra, ExtrinsicPayload } from '@polkadot/types/interfaces';
|
||||||
|
import { SignerPayloadJSON } from '@polkadot/types/types';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import fromMetadata from '@polkadot/api-metadata/extrinsics/fromMetadata';
|
import fromMetadata from '@polkadot/api-metadata/extrinsics/fromMetadata';
|
||||||
import findChain from '@polkadot/extension/chains';
|
import findChain from '@polkadot/extension-chains';
|
||||||
import { GenericCall, Metadata } from '@polkadot/types';
|
import { createType, GenericCall, Metadata } from '@polkadot/types';
|
||||||
import { formatNumber } from '@polkadot/util';
|
import { formatNumber } from '@polkadot/util';
|
||||||
|
|
||||||
interface MethodJson {
|
interface MethodJson {
|
||||||
@ -17,12 +18,10 @@ interface MethodJson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
blockNumber: BlockNumber;
|
|
||||||
className?: string;
|
className?: string;
|
||||||
genesisHash: string;
|
|
||||||
isDecoded: boolean;
|
isDecoded: boolean;
|
||||||
method: string;
|
|
||||||
payload: ExtrinsicPayload;
|
payload: ExtrinsicPayload;
|
||||||
|
request: SignerPayloadJSON;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +74,10 @@ function renderMortality (era: ExtrinsicEra, blockNumber: BlockNumber): string {
|
|||||||
return `mortal, valid from #${formatNumber(mortal.birth(blockNumber))} to #${formatNumber(mortal.death(blockNumber))}`;
|
return `mortal, valid from #${formatNumber(mortal.birth(blockNumber))} to #${formatNumber(mortal.death(blockNumber))}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Details ({ blockNumber, className, genesisHash, isDecoded, method, payload: { era, nonce, tip }, url }: Props): React.ReactElement<Props> {
|
function Details ({ className, isDecoded, payload, request, url }: Props): React.ReactElement<Props> {
|
||||||
|
const blockNumber = createType('BlockNumber', request.blockNumber);
|
||||||
|
const { genesisHash, method } = request;
|
||||||
|
const { era, nonce, tip } = payload;
|
||||||
const chain = findChain(genesisHash);
|
const chain = findChain(genesisHash);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -99,7 +101,7 @@ function Details ({ blockNumber, className, genesisHash, isDecoded, method, payl
|
|||||||
<td className='data'>{formatNumber(tip)}</td>
|
<td className='data'>{formatNumber(tip)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
{renderMethod(method, (chain && isDecoded) ? chain.meta : null)}
|
{renderMethod(method, isDecoded ? chain.meta : null)}
|
||||||
<tr>
|
<tr>
|
||||||
<td className='label'>lifetime</td>
|
<td className='label'>lifetime</td>
|
||||||
<td className='data'>{renderMortality(era, blockNumber)}</td>
|
<td className='data'>{renderMortality(era, blockNumber)}</td>
|
||||||
|
|||||||
54
packages/extension-ui/src/Popup/Signing/Qr.tsx
Normal file
54
packages/extension-ui/src/Popup/Signing/Qr.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2019 @polkadot/extension-ui 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 { ExtrinsicPayload } from '@polkadot/types/interfaces';
|
||||||
|
import { SignerPayloadJSON } from '@polkadot/types/types';
|
||||||
|
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { QrDisplayPayload, QrScanSignature } from '@polkadot/react-qr';
|
||||||
|
|
||||||
|
import { Button } from '../../components';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
onSignature: ({ signature }: { signature: string }) => void;
|
||||||
|
payload: ExtrinsicPayload;
|
||||||
|
request: SignerPayloadJSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CMD_MORTAL = 2;
|
||||||
|
|
||||||
|
function Qr ({ className, onSignature, payload, request }: Props): React.ReactElement<Props> {
|
||||||
|
const [isScanning, setIsScanning] = useState(false);
|
||||||
|
const [payloadU8a, setPayloadU8a] = useState(new Uint8Array());
|
||||||
|
|
||||||
|
useEffect((): void => setPayloadU8a(payload.toU8a()), [payload]);
|
||||||
|
|
||||||
|
const _onShowQr = (): void => setIsScanning(true);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
{isScanning
|
||||||
|
? <QrScanSignature onScan={onSignature} />
|
||||||
|
: <QrDisplayPayload
|
||||||
|
address={request.address}
|
||||||
|
cmd={CMD_MORTAL}
|
||||||
|
payload={payloadU8a}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{!isScanning && (
|
||||||
|
<Button
|
||||||
|
label='Scan signature via camera'
|
||||||
|
onClick={_onShowQr}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default styled(Qr)`
|
||||||
|
padding: 0.75rem 1.5rem 0 1.5rem;
|
||||||
|
`;
|
||||||
@ -2,49 +2,70 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { RequestExtrinsicSign } from '@polkadot/extension/background/types';
|
import { ExtrinsicPayload } from '@polkadot/types/interfaces';
|
||||||
|
import { AccountJson, RequestExtrinsicSign } from '@polkadot/extension/background/types';
|
||||||
|
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext, useState, useEffect } from 'react';
|
||||||
import { createType } from '@polkadot/types';
|
import { createType } from '@polkadot/types';
|
||||||
|
|
||||||
import { ActionBar, ActionContext, Address, Link } from '../../components';
|
import { ActionBar, ActionContext, Address, Link } from '../../components';
|
||||||
import { approveSignPassword, cancelSignRequest } from '../../messaging';
|
import { approveSignPassword, approveSignSignature, cancelSignRequest } from '../../messaging';
|
||||||
import Details from './Details';
|
import Details from './Details';
|
||||||
|
import Qr from './Qr';
|
||||||
import Unlock from './Unlock';
|
import Unlock from './Unlock';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
account: AccountJson;
|
||||||
isFirst: boolean;
|
isFirst: boolean;
|
||||||
request: RequestExtrinsicSign;
|
request: RequestExtrinsicSign;
|
||||||
signId: string;
|
signId: string;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Request ({ isFirst, request, signId, url }: Props): React.ReactElement<Props> {
|
export default function Request ({ account: { isExternal }, isFirst, request, signId, url }: Props): React.ReactElement<Props> | null {
|
||||||
const onAction = useContext(ActionContext);
|
const onAction = useContext(ActionContext);
|
||||||
const onCancel = (): Promise<void> =>
|
const [payload, setPayload] = useState<ExtrinsicPayload | null>(null);
|
||||||
|
|
||||||
|
useEffect((): void => {
|
||||||
|
setPayload(createType('ExtrinsicPayload', request, { version: request.version }));
|
||||||
|
}, [request]);
|
||||||
|
|
||||||
|
if (!payload) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _onCancel = (): Promise<void> =>
|
||||||
cancelSignRequest(signId)
|
cancelSignRequest(signId)
|
||||||
.then((): void => onAction())
|
.then((): void => onAction())
|
||||||
.catch((error: Error) => console.error(error));
|
.catch((error: Error) => console.error(error));
|
||||||
const onSign = (password: string): Promise<void> =>
|
const _onSign = (password: string): Promise<void> =>
|
||||||
approveSignPassword(signId, password)
|
approveSignPassword(signId, password)
|
||||||
.then((): void => onAction());
|
.then((): void => onAction())
|
||||||
const blockNumber = createType('BlockNumber', request.blockNumber);
|
.catch((error: Error) => console.error(error));
|
||||||
const payload = createType('ExtrinsicPayload', request, { version: request.version });
|
const _onSignature = ({ signature }: { signature: string }): Promise<void> =>
|
||||||
|
approveSignSignature(signId, signature)
|
||||||
|
.then((): void => onAction())
|
||||||
|
.catch((error: Error) => console.error(error));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Address address={request.address}>
|
<Address address={request.address}>
|
||||||
<Details
|
{isExternal && isFirst
|
||||||
blockNumber={blockNumber}
|
? <Qr
|
||||||
genesisHash={request.genesisHash}
|
payload={payload}
|
||||||
isDecoded={isFirst}
|
request={request}
|
||||||
method={request.method}
|
onSignature={_onSignature}
|
||||||
payload={payload}
|
/>
|
||||||
url={url}
|
: <Details
|
||||||
/>
|
isDecoded={isFirst}
|
||||||
|
payload={payload}
|
||||||
|
request={request}
|
||||||
|
url={url}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{isFirst && !isExternal && <Unlock onSign={_onSign} />}
|
||||||
<ActionBar>
|
<ActionBar>
|
||||||
<Link isDanger onClick={onCancel}>Cancel</Link>
|
<Link isDanger onClick={_onCancel}>Cancel</Link>
|
||||||
</ActionBar>
|
</ActionBar>
|
||||||
{isFirst && <Unlock onSign={onSign} />}
|
|
||||||
</Address>
|
</Address>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,16 +15,16 @@ export default function Unlock ({ className, onSign }: Props): React.ReactElemen
|
|||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
|
|
||||||
const onClick = (): Promise<void> =>
|
|
||||||
onSign(password)
|
|
||||||
.catch((error): void => setError(error.message));
|
|
||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
if (error) {
|
if (error) {
|
||||||
setError('');
|
setError('');
|
||||||
}
|
}
|
||||||
}, [password]);
|
}, [password]);
|
||||||
|
|
||||||
|
const _onClick = (): Promise<void> =>
|
||||||
|
onSign(password)
|
||||||
|
.catch((error): void => setError(error.message));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<Input
|
<Input
|
||||||
@ -36,7 +36,7 @@ export default function Unlock ({ className, onSign }: Props): React.ReactElemen
|
|||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
label='Sign the transaction'
|
label='Sign the transaction'
|
||||||
onClick={onClick}
|
onClick={_onClick}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -13,8 +13,9 @@ export default function Signing (): React.ReactElement<{}> {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header label='transactions' />
|
<Header label='transactions' />
|
||||||
{requests.map(({ id, request, url }, index): React.ReactNode => (
|
{requests.map(({ account, id, request, url }, index): React.ReactNode => (
|
||||||
<Request
|
<Request
|
||||||
|
account={account}
|
||||||
isFirst={index === 0}
|
isFirst={index === 0}
|
||||||
key={id}
|
key={id}
|
||||||
request={request}
|
request={request}
|
||||||
|
|||||||
@ -10,7 +10,8 @@ type Props = {};
|
|||||||
|
|
||||||
export default function Welcome (): React.ReactElement<Props> {
|
export default function Welcome (): React.ReactElement<Props> {
|
||||||
const onAction = useContext(ActionContext);
|
const onAction = useContext(ActionContext);
|
||||||
const onClick = (): void => {
|
|
||||||
|
const _onClick = (): void => {
|
||||||
window.localStorage.setItem('welcome_read', 'ok');
|
window.localStorage.setItem('welcome_read', 'ok');
|
||||||
onAction();
|
onAction();
|
||||||
};
|
};
|
||||||
@ -28,7 +29,7 @@ export default function Welcome (): React.ReactElement<Props> {
|
|||||||
... we are not in the information collection business (even anonymized).
|
... we are not in the information collection business (even anonymized).
|
||||||
<Button
|
<Button
|
||||||
label='Understood, let me continue'
|
label='Understood, let me continue'
|
||||||
onClick={onClick}
|
onClick={_onClick}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -11,17 +11,31 @@ import settings from '@polkadot/ui-settings';
|
|||||||
import { setAddressPrefix } from '@polkadot/util-crypto';
|
import { setAddressPrefix } from '@polkadot/util-crypto';
|
||||||
|
|
||||||
import { Loading } from '../components';
|
import { Loading } from '../components';
|
||||||
import { AccountContext, ActionContext, AuthorizeContext, SigningContext } from '../components/contexts';
|
import { AccountContext, ActionContext, AuthorizeContext, MediaContext, SigningContext } from '../components/contexts';
|
||||||
import { subscribeAccounts, subscribeAuthorize, subscribeSigning } from '../messaging';
|
import { subscribeAccounts, subscribeAuthorize, subscribeSigning } from '../messaging';
|
||||||
import Accounts from './Accounts';
|
import Accounts from './Accounts';
|
||||||
import Authorize from './Authorize';
|
import Authorize from './Authorize';
|
||||||
import Create from './Create';
|
import Create from './Create';
|
||||||
import Forget from './Forget';
|
import Forget from './Forget';
|
||||||
import Import from './Import';
|
import ImportQr from './ImportQr';
|
||||||
|
import ImportSeed from './ImportSeed';
|
||||||
import Settings from './Settings';
|
import Settings from './Settings';
|
||||||
import Signing from './Signing';
|
import Signing from './Signing';
|
||||||
import Welcome from './Welcome';
|
import Welcome from './Welcome';
|
||||||
|
|
||||||
|
// Request permission for video, based on access we can hide/show import
|
||||||
|
async function requestMediaAccess (): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await navigator.mediaDevices.getUserMedia({ video: true });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Permission for video declined', error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// load the ui settings, actually only used for address prefix atm
|
// load the ui settings, actually only used for address prefix atm
|
||||||
// probably overkill (however can replace once we have actual others)
|
// probably overkill (however can replace once we have actual others)
|
||||||
const { prefix } = settings.get();
|
const { prefix } = settings.get();
|
||||||
@ -32,10 +46,11 @@ setAddressPrefix((prefix === -1 ? 42 : prefix) as Prefix);
|
|||||||
export default function Popup (): React.ReactElement<{}> {
|
export default function Popup (): React.ReactElement<{}> {
|
||||||
const [accounts, setAccounts] = useState<null | AccountJson[]>(null);
|
const [accounts, setAccounts] = useState<null | AccountJson[]>(null);
|
||||||
const [authRequests, setAuthRequests] = useState<null | AuthorizeRequest[]>(null);
|
const [authRequests, setAuthRequests] = useState<null | AuthorizeRequest[]>(null);
|
||||||
|
const [mediaAllowed, setMediaAllowed] = useState(false);
|
||||||
const [signRequests, setSignRequests] = useState<null | SigningRequest[]>(null);
|
const [signRequests, setSignRequests] = useState<null | SigningRequest[]>(null);
|
||||||
const [isWelcomeDone, setWelcomeDone] = useState(false);
|
const [isWelcomeDone, setWelcomeDone] = useState(false);
|
||||||
|
|
||||||
const onAction = (to?: string): void => {
|
const _onAction = (to?: string): void => {
|
||||||
setWelcomeDone(window.localStorage.getItem('welcome_read') === 'ok');
|
setWelcomeDone(window.localStorage.getItem('welcome_read') === 'ok');
|
||||||
|
|
||||||
if (to) {
|
if (to) {
|
||||||
@ -45,11 +60,12 @@ export default function Popup (): React.ReactElement<{}> {
|
|||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
|
requestMediaAccess().then(setMediaAllowed),
|
||||||
subscribeAccounts(setAccounts),
|
subscribeAccounts(setAccounts),
|
||||||
subscribeAuthorize(setAuthRequests),
|
subscribeAuthorize(setAuthRequests),
|
||||||
subscribeSigning(setSignRequests)
|
subscribeSigning(setSignRequests)
|
||||||
]).catch((error: Error) => console.error(error));
|
]).catch((error: Error) => console.error(error));
|
||||||
onAction();
|
_onAction();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const Root = isWelcomeDone
|
const Root = isWelcomeDone
|
||||||
@ -62,18 +78,21 @@ export default function Popup (): React.ReactElement<{}> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Loading>{accounts && authRequests && signRequests && (
|
<Loading>{accounts && authRequests && signRequests && (
|
||||||
<ActionContext.Provider value={onAction}>
|
<ActionContext.Provider value={_onAction}>
|
||||||
<AccountContext.Provider value={accounts}>
|
<AccountContext.Provider value={accounts}>
|
||||||
<AuthorizeContext.Provider value={authRequests}>
|
<AuthorizeContext.Provider value={authRequests}>
|
||||||
<SigningContext.Provider value={signRequests}>
|
<MediaContext.Provider value={mediaAllowed}>
|
||||||
<Switch>
|
<SigningContext.Provider value={signRequests}>
|
||||||
<Route path='/account/create' component={Create} />
|
<Switch>
|
||||||
<Route path='/account/forget/:address' component={Forget} />
|
<Route path='/account/create' component={Create} />
|
||||||
<Route path='/account/import' component={Import} />
|
<Route path='/account/forget/:address' component={Forget} />
|
||||||
<Route path='/settings' component={Settings} />
|
<Route path='/account/import-qr' component={ImportQr} />
|
||||||
<Route exact path='/' component={Root} />
|
<Route path='/account/import-seed' component={ImportSeed} />
|
||||||
</Switch>
|
<Route path='/settings' component={Settings} />
|
||||||
</SigningContext.Provider>
|
<Route exact path='/' component={Root} />
|
||||||
|
</Switch>
|
||||||
|
</SigningContext.Provider>
|
||||||
|
</MediaContext.Provider>
|
||||||
</AuthorizeContext.Provider>
|
</AuthorizeContext.Provider>
|
||||||
</AccountContext.Provider>
|
</AccountContext.Provider>
|
||||||
</ActionContext.Provider>
|
</ActionContext.Provider>
|
||||||
|
|||||||
@ -3,10 +3,12 @@
|
|||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { AccountJson } from '@polkadot/extension/background/types';
|
import { AccountJson } from '@polkadot/extension/background/types';
|
||||||
|
import { Chain } from '@polkadot/extension-chains/types';
|
||||||
import { Prefix } from '@polkadot/util-crypto/address/types';
|
import { Prefix } from '@polkadot/util-crypto/address/types';
|
||||||
|
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import findChain from '@polkadot/extension-chains';
|
||||||
import Identicon from '@polkadot/react-identicon';
|
import Identicon from '@polkadot/react-identicon';
|
||||||
import settings from '@polkadot/ui-settings';
|
import settings from '@polkadot/ui-settings';
|
||||||
import { decodeAddress, encodeAddress } from '@polkadot/util-crypto';
|
import { decodeAddress, encodeAddress } from '@polkadot/util-crypto';
|
||||||
@ -19,12 +21,40 @@ interface Props {
|
|||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
name?: React.ReactNode | null;
|
name?: React.ReactNode | null;
|
||||||
|
genesisHash?: string | null;
|
||||||
theme?: 'polkadot' | 'substrate';
|
theme?: 'polkadot' | 'substrate';
|
||||||
}
|
}
|
||||||
|
|
||||||
function Address ({ address, children, className, name, theme = 'polkadot' }: Props): React.ReactElement<Props> {
|
// find an account in our list
|
||||||
|
function findAccount (accounts: AccountJson[], publicKey: Uint8Array): AccountJson | null {
|
||||||
|
const pkStr = publicKey.toString();
|
||||||
|
|
||||||
|
return accounts.find(({ address }): boolean =>
|
||||||
|
decodeAddress(address).toString() === pkStr
|
||||||
|
) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recodes an supplied address using the prefix/genesisHash, include the actual saved account & chain
|
||||||
|
function recodeAddress (address: string, accounts: AccountJson[], genesisHash?: string | null): [string, AccountJson | null, Chain] {
|
||||||
|
// decode and create a shortcut for the encoded address
|
||||||
|
const publicKey = decodeAddress(address);
|
||||||
|
|
||||||
|
// find our account using the actual publicKey, and then find the associated chain
|
||||||
|
const account = findAccount(accounts, publicKey);
|
||||||
|
const chain = findChain((account && account.genesisHash) || genesisHash);
|
||||||
|
|
||||||
|
return [
|
||||||
|
// always allow the actual settings to override the display
|
||||||
|
encodeAddress(publicKey, (settings.prefix === -1 ? chain.ss58Format : settings.prefix) as Prefix),
|
||||||
|
account,
|
||||||
|
chain
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function Address ({ address, children, className, genesisHash, name, theme = 'polkadot' }: Props): React.ReactElement<Props> {
|
||||||
const accounts = useContext(AccountContext);
|
const accounts = useContext(AccountContext);
|
||||||
const [account, setAccount] = useState<AccountJson | null>(null);
|
const [account, setAccount] = useState<AccountJson | null>(null);
|
||||||
|
const [chain, setChain] = useState<Chain | null>(null);
|
||||||
const [formatted, setFormatted] = useState<string | null>(null);
|
const [formatted, setFormatted] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
@ -32,21 +62,16 @@ function Address ({ address, children, className, name, theme = 'polkadot' }: Pr
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addrU8a = decodeAddress(address);
|
const [formatted, account, chain] = recodeAddress(address, accounts, genesisHash);
|
||||||
const addrU8aStr = addrU8a.toString();
|
|
||||||
|
|
||||||
setFormatted(
|
setFormatted(formatted);
|
||||||
encodeAddress(addrU8a, (settings.prefix === -1 ? 42 : settings.prefix) as Prefix)
|
setChain(chain);
|
||||||
);
|
setAccount(account);
|
||||||
setAccount(
|
|
||||||
accounts.find((account): boolean =>
|
|
||||||
decodeAddress(account.address).toString() === addrU8aStr
|
|
||||||
) || null
|
|
||||||
);
|
|
||||||
}, [address]);
|
}, [address]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconBox
|
<IconBox
|
||||||
|
banner={chain && chain.genesisHash && chain.name}
|
||||||
className={className}
|
className={className}
|
||||||
icon={
|
icon={
|
||||||
<Identicon
|
<Identicon
|
||||||
|
|||||||
@ -8,14 +8,16 @@ import styled from 'styled-components';
|
|||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
banner?: React.ReactNode;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Box ({ children, className }: Props): React.ReactElement<Props> {
|
function Box ({ banner, children, className }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<article className={className}>
|
<article className={className}>
|
||||||
{children}
|
{children}
|
||||||
|
{banner && <div className='banner'>{banner}</div>}
|
||||||
</article>
|
</article>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -30,4 +32,16 @@ export default styled(Box)`
|
|||||||
font-size: ${defaults.fontSize};
|
font-size: ${defaults.fontSize};
|
||||||
margin: ${defaults.boxMargin};
|
margin: ${defaults.boxMargin};
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.banner {
|
||||||
|
background: darkorange;
|
||||||
|
border-radius: 0 ${defaults.borderRadius} 0 ${defaults.borderRadius};
|
||||||
|
color: white;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import Box from './Box';
|
|||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
banner?: React.ReactNode;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
@ -17,10 +18,13 @@ interface Props {
|
|||||||
theme?: 'polkadot' | 'substrate';
|
theme?: 'polkadot' | 'substrate';
|
||||||
}
|
}
|
||||||
|
|
||||||
function IconBox ({ children, className, icon, intro }: Props): React.ReactElement<Props> {
|
function IconBox ({ banner, children, className, icon, intro }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<Box className='details'>
|
<Box
|
||||||
|
banner={banner}
|
||||||
|
className='details'
|
||||||
|
>
|
||||||
<div className='intro'>{intro}</div>
|
<div className='intro'>{intro}</div>
|
||||||
<div className='children'>{children}</div>
|
<div className='children'>{children}</div>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -12,11 +12,13 @@ const noop = (to?: string): void => {};
|
|||||||
const AccountContext = React.createContext<AccountJson[]>([]);
|
const AccountContext = React.createContext<AccountJson[]>([]);
|
||||||
const ActionContext = React.createContext<(to?: string) => void>(noop);
|
const ActionContext = React.createContext<(to?: string) => void>(noop);
|
||||||
const AuthorizeContext = React.createContext<AuthorizeRequest[]>([]);
|
const AuthorizeContext = React.createContext<AuthorizeRequest[]>([]);
|
||||||
|
const MediaContext = React.createContext<boolean>(false);
|
||||||
const SigningContext = React.createContext<SigningRequest[]>([]);
|
const SigningContext = React.createContext<SigningRequest[]>([]);
|
||||||
|
|
||||||
export {
|
export {
|
||||||
AccountContext,
|
AccountContext,
|
||||||
ActionContext,
|
ActionContext,
|
||||||
AuthorizeContext,
|
AuthorizeContext,
|
||||||
|
MediaContext,
|
||||||
SigningContext
|
SigningContext
|
||||||
};
|
};
|
||||||
|
|||||||
@ -66,14 +66,6 @@ export async function forgetAccount (address: string): Promise<boolean> {
|
|||||||
return sendMessage('pri(accounts.forget)', { address });
|
return sendMessage('pri(accounts.forget)', { address });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAccounts (): Promise<AccountJson[]> {
|
|
||||||
return sendMessage('pri(accounts.list)');
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getAuthRequests (): Promise<AuthorizeRequest[]> {
|
|
||||||
return sendMessage('pri(authorize.requests)');
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function rejectAuthRequest (id: string): Promise<boolean> {
|
export async function rejectAuthRequest (id: string): Promise<boolean> {
|
||||||
return sendMessage('pri(authorize.reject)', { id });
|
return sendMessage('pri(authorize.reject)', { id });
|
||||||
}
|
}
|
||||||
@ -82,10 +74,6 @@ export async function approveAuthRequest (id: string): Promise<boolean> {
|
|||||||
return sendMessage('pri(authorize.approve)', { id });
|
return sendMessage('pri(authorize.approve)', { id });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSignRequests (): Promise<SigningRequest[]> {
|
|
||||||
return sendMessage('pri(signing.requests)');
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function cancelSignRequest (id: string): Promise<boolean> {
|
export async function cancelSignRequest (id: string): Promise<boolean> {
|
||||||
return sendMessage('pri(signing.cancel)', { id });
|
return sendMessage('pri(signing.cancel)', { id });
|
||||||
}
|
}
|
||||||
@ -94,6 +82,14 @@ export async function approveSignPassword (id: string, password: string): Promis
|
|||||||
return sendMessage('pri(signing.approve.password)', { id, password });
|
return sendMessage('pri(signing.approve.password)', { id, password });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function approveSignSignature (id: string, signature: string): Promise<boolean> {
|
||||||
|
return sendMessage('pri(signing.approve.signature)', { id, signature });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createAccountExternal (name: string, address: string, genesisHash: string): Promise<boolean> {
|
||||||
|
return sendMessage('pri(accounts.create.external)', { address, genesisHash, name });
|
||||||
|
}
|
||||||
|
|
||||||
export async function createAccountSuri (name: string, password: string, suri: string, type?: KeypairType): Promise<boolean> {
|
export async function createAccountSuri (name: string, password: string, suri: string, type?: KeypairType): Promise<boolean> {
|
||||||
return sendMessage('pri(accounts.create.suri)', { name, password, suri, type });
|
return sendMessage('pri(accounts.create.suri)', { name, password, suri, type });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>polkadot{.js}</title>
|
<title>polkadot{.js}</title>
|
||||||
</head>
|
</head>
|
||||||
<body style="background:#f5f6f7; box-sizing:border-box; height:580px; margin:0; overflow-x: hidden; padding:0 1rem; width:480px;">
|
<body style="background:#f5f6f7; height:580px; margin:0; overflow-x:hidden; text-align:center">
|
||||||
<div id="root"></div>
|
<div id="root" style="box-sizing:border-box; margin:0 auto; padding:0 1rem; text-align:left; width:480px"></div>
|
||||||
<script src='./popup.js'></script>
|
<script src='./popup.js'></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
||||||
import { AccountJson, AuthorizeRequest, RequestAccountCreateSuri, RequestAccountEdit, RequestAuthorizeApprove, RequestAuthorizeReject, RequestSigningApprovePassword, RequestSigningCancel, RequestSeedCreate, ResponseSeedCreate, RequestSeedValidate, ResponseSeedValidate, RequestAccountForget, SigningRequest, RequestTypes, ResponseTypes, MessageTypes } from '../types';
|
import { AccountJson, AuthorizeRequest, RequestAccountCreateExternal, RequestAccountCreateSuri, RequestAccountEdit, RequestAuthorizeApprove, RequestAuthorizeReject, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSeedCreate, ResponseSeedCreate, RequestSeedValidate, ResponseSeedValidate, RequestAccountForget, SigningRequest, RequestTypes, ResponseTypes, MessageTypes } from '../types';
|
||||||
|
|
||||||
import keyring from '@polkadot/ui-keyring';
|
import keyring from '@polkadot/ui-keyring';
|
||||||
import accountsObservable from '@polkadot/ui-keyring/observable/accounts';
|
import accountsObservable from '@polkadot/ui-keyring/observable/accounts';
|
||||||
@ -18,7 +18,10 @@ const SEED_DEFAULT_LENGTH = 12;
|
|||||||
const SEED_LENGTHS = [12, 24];
|
const SEED_LENGTHS = [12, 24];
|
||||||
|
|
||||||
function transformAccounts (accounts: SubjectInfo): AccountJson[] {
|
function transformAccounts (accounts: SubjectInfo): AccountJson[] {
|
||||||
return Object.values(accounts).map(({ json }): AccountJson => json);
|
return Object.values(accounts).map(({ json: { address, meta } }): AccountJson => ({
|
||||||
|
address,
|
||||||
|
...meta
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Extension {
|
export default class Extension {
|
||||||
@ -28,6 +31,12 @@ export default class Extension {
|
|||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private accountsCreateExternal ({ address, genesisHash, name }: RequestAccountCreateExternal): boolean {
|
||||||
|
keyring.addExternal(address, { name, genesisHash });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private accountsCreateSuri ({ genesisHash, name, password, suri, type }: RequestAccountCreateSuri): boolean {
|
private accountsCreateSuri ({ genesisHash, name, password, suri, type }: RequestAccountCreateSuri): boolean {
|
||||||
keyring.addUri(suri, password, { genesisHash, name }, type);
|
keyring.addUri(suri, password, { genesisHash, name }, type);
|
||||||
|
|
||||||
@ -50,10 +59,6 @@ export default class Extension {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private accountsList (): AccountJson[] {
|
|
||||||
return transformAccounts(accountsObservable.subject.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME This looks very much like what we have in Tabs
|
// FIXME This looks very much like what we have in Tabs
|
||||||
private accountsSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
private accountsSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
||||||
const cb = createSubscription<'pri(accounts.subscribe)'>(id, port);
|
const cb = createSubscription<'pri(accounts.subscribe)'>(id, port);
|
||||||
@ -93,10 +98,6 @@ export default class Extension {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private authorizeRequests (): AuthorizeRequest[] {
|
|
||||||
return this.state.allAuthRequests;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME This looks very much like what we have in accounts
|
// FIXME This looks very much like what we have in accounts
|
||||||
private authorizeSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
private authorizeSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
||||||
const cb = createSubscription<'pri(authorize.subscribe)'>(id, port);
|
const cb = createSubscription<'pri(authorize.subscribe)'>(id, port);
|
||||||
@ -162,6 +163,18 @@ export default class Extension {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private signingApproveSignature ({ id, signature }: RequestSigningApproveSignature): boolean {
|
||||||
|
const queued = this.state.getSignRequest(id);
|
||||||
|
|
||||||
|
assert(queued, 'Unable to find request');
|
||||||
|
|
||||||
|
const { resolve } = queued;
|
||||||
|
|
||||||
|
resolve({ id, signature });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private signingCancel ({ id }: RequestSigningCancel): boolean {
|
private signingCancel ({ id }: RequestSigningCancel): boolean {
|
||||||
const queued = this.state.getSignRequest(id);
|
const queued = this.state.getSignRequest(id);
|
||||||
|
|
||||||
@ -174,10 +187,6 @@ export default class Extension {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private signingRequests (): SigningRequest[] {
|
|
||||||
return this.state.allSignRequests;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME This looks very much like what we have in authorization
|
// FIXME This looks very much like what we have in authorization
|
||||||
private signingSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
private signingSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
||||||
const cb = createSubscription<'pri(signing.subscribe)'>(id, port);
|
const cb = createSubscription<'pri(signing.subscribe)'>(id, port);
|
||||||
@ -203,12 +212,12 @@ export default class Extension {
|
|||||||
case 'pri(authorize.reject)':
|
case 'pri(authorize.reject)':
|
||||||
return this.authorizeReject(request as RequestAuthorizeApprove);
|
return this.authorizeReject(request as RequestAuthorizeApprove);
|
||||||
|
|
||||||
case 'pri(authorize.requests)':
|
|
||||||
return this.authorizeRequests();
|
|
||||||
|
|
||||||
case 'pri(authorize.subscribe)':
|
case 'pri(authorize.subscribe)':
|
||||||
return this.authorizeSubscribe(id, port);
|
return this.authorizeSubscribe(id, port);
|
||||||
|
|
||||||
|
case 'pri(accounts.create.external)':
|
||||||
|
return this.accountsCreateExternal(request as RequestAccountCreateExternal);
|
||||||
|
|
||||||
case 'pri(accounts.create.suri)':
|
case 'pri(accounts.create.suri)':
|
||||||
return this.accountsCreateSuri(request as RequestAccountCreateSuri);
|
return this.accountsCreateSuri(request as RequestAccountCreateSuri);
|
||||||
|
|
||||||
@ -218,9 +227,6 @@ export default class Extension {
|
|||||||
case 'pri(accounts.edit)':
|
case 'pri(accounts.edit)':
|
||||||
return this.accountsEdit(request as RequestAccountEdit);
|
return this.accountsEdit(request as RequestAccountEdit);
|
||||||
|
|
||||||
case 'pri(accounts.list)':
|
|
||||||
return this.accountsList();
|
|
||||||
|
|
||||||
case 'pri(accounts.subscribe)':
|
case 'pri(accounts.subscribe)':
|
||||||
return this.accountsSubscribe(id, port);
|
return this.accountsSubscribe(id, port);
|
||||||
|
|
||||||
@ -233,12 +239,12 @@ export default class Extension {
|
|||||||
case 'pri(signing.approve.password)':
|
case 'pri(signing.approve.password)':
|
||||||
return this.signingApprovePassword(request as RequestSigningApprovePassword);
|
return this.signingApprovePassword(request as RequestSigningApprovePassword);
|
||||||
|
|
||||||
|
case 'pri(signing.approve.signature)':
|
||||||
|
return this.signingApproveSignature(request as RequestSigningApproveSignature);
|
||||||
|
|
||||||
case 'pri(signing.cancel)':
|
case 'pri(signing.cancel)':
|
||||||
return this.signingCancel(request as RequestSigningCancel);
|
return this.signingCancel(request as RequestSigningCancel);
|
||||||
|
|
||||||
case 'pri(signing.requests)':
|
|
||||||
return this.signingRequests();
|
|
||||||
|
|
||||||
case 'pri(signing.subscribe)':
|
case 'pri(signing.subscribe)':
|
||||||
return this.signingSubscribe(id, port);
|
return this.signingSubscribe(id, port);
|
||||||
|
|
||||||
|
|||||||
@ -92,7 +92,7 @@ export default class State {
|
|||||||
extension.windows.create({
|
extension.windows.create({
|
||||||
// This is not allowed on FF, only on Chrome - disable completely
|
// This is not allowed on FF, only on Chrome - disable completely
|
||||||
// focused: true,
|
// focused: true,
|
||||||
height: 581,
|
height: 621,
|
||||||
left: 150,
|
left: 150,
|
||||||
top: 150,
|
top: 150,
|
||||||
type: 'popup',
|
type: 'popup',
|
||||||
|
|||||||
@ -43,20 +43,19 @@ export interface SigningRequest {
|
|||||||
// [MessageType]: [RequestType, ResponseType, SubscriptionMessageType?]
|
// [MessageType]: [RequestType, ResponseType, SubscriptionMessageType?]
|
||||||
export interface RequestSignatures {
|
export interface RequestSignatures {
|
||||||
// private/internal requests, i.e. from a popup
|
// private/internal requests, i.e. from a popup
|
||||||
|
'pri(accounts.create.external)': [RequestAccountCreateExternal, boolean];
|
||||||
'pri(accounts.create.suri)': [RequestAccountCreateSuri, boolean];
|
'pri(accounts.create.suri)': [RequestAccountCreateSuri, boolean];
|
||||||
'pri(accounts.edit)': [RequestAccountEdit, boolean];
|
'pri(accounts.edit)': [RequestAccountEdit, boolean];
|
||||||
'pri(accounts.forget)': [RequestAccountForget, boolean];
|
'pri(accounts.forget)': [RequestAccountForget, boolean];
|
||||||
'pri(accounts.list)': [RequestAccountList, AccountJson[]];
|
|
||||||
'pri(accounts.subscribe)': [RequestAccountSubscribe, boolean, AccountJson[]];
|
'pri(accounts.subscribe)': [RequestAccountSubscribe, boolean, AccountJson[]];
|
||||||
'pri(authorize.approve)': [RequestAuthorizeApprove, boolean];
|
'pri(authorize.approve)': [RequestAuthorizeApprove, boolean];
|
||||||
'pri(authorize.reject)': [RequestAuthorizeReject, boolean];
|
'pri(authorize.reject)': [RequestAuthorizeReject, boolean];
|
||||||
'pri(authorize.requests)': [RequestAuthorizeRequests, AuthorizeRequest[]];
|
|
||||||
'pri(authorize.subscribe)': [RequestAuthorizeSubscribe, boolean, AuthorizeRequest[]];
|
'pri(authorize.subscribe)': [RequestAuthorizeSubscribe, boolean, AuthorizeRequest[]];
|
||||||
'pri(seed.create)': [RequestSeedCreate, ResponseSeedCreate];
|
'pri(seed.create)': [RequestSeedCreate, ResponseSeedCreate];
|
||||||
'pri(seed.validate)': [RequestSeedValidate, ResponseSeedValidate];
|
'pri(seed.validate)': [RequestSeedValidate, ResponseSeedValidate];
|
||||||
'pri(signing.approve.password)': [RequestSigningApprovePassword, boolean];
|
'pri(signing.approve.password)': [RequestSigningApprovePassword, boolean];
|
||||||
|
'pri(signing.approve.signature)': [RequestSigningApproveSignature, boolean];
|
||||||
'pri(signing.cancel)': [RequestSigningCancel, boolean];
|
'pri(signing.cancel)': [RequestSigningCancel, boolean];
|
||||||
'pri(signing.requests)': [RequestSigningRequests, SigningRequest[]];
|
|
||||||
'pri(signing.subscribe)': [RequestSigningSubscribe, boolean, SigningRequest[]];
|
'pri(signing.subscribe)': [RequestSigningSubscribe, boolean, SigningRequest[]];
|
||||||
// public/external requests, i.e. from a page
|
// public/external requests, i.e. from a page
|
||||||
'pub(accounts.list)': [RequestAccountList, InjectedAccount[]];
|
'pub(accounts.list)': [RequestAccountList, InjectedAccount[]];
|
||||||
@ -94,10 +93,14 @@ export interface RequestAuthorizeReject {
|
|||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RequestAuthorizeRequests = null;
|
|
||||||
|
|
||||||
export type RequestAuthorizeSubscribe = null;
|
export type RequestAuthorizeSubscribe = null;
|
||||||
|
|
||||||
|
export interface RequestAccountCreateExternal {
|
||||||
|
address: string;
|
||||||
|
genesisHash?: string | null;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface RequestAccountCreateSuri {
|
export interface RequestAccountCreateSuri {
|
||||||
name: string;
|
name: string;
|
||||||
genesisHash?: string | null;
|
genesisHash?: string | null;
|
||||||
@ -127,12 +130,15 @@ export interface RequestSigningApprovePassword {
|
|||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RequestSigningApproveSignature {
|
||||||
|
id: string;
|
||||||
|
signature: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface RequestSigningCancel {
|
export interface RequestSigningCancel {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RequestSigningRequests = null;
|
|
||||||
|
|
||||||
export type RequestSigningSubscribe = null;
|
export type RequestSigningSubscribe = null;
|
||||||
|
|
||||||
export interface RequestSeedCreate {
|
export interface RequestSeedCreate {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -1,41 +0,0 @@
|
|||||||
// Copyright 2019 @polkadot/extension 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 { Metadata } from '@polkadot/types';
|
|
||||||
|
|
||||||
// imports chain details, generally metadata. For the generation of these,
|
|
||||||
// inside the api, run `yarn chain:info [--ws <url>]`
|
|
||||||
import alexander from './alexander';
|
|
||||||
import devPolka051 from './dev-polkadot-051';
|
|
||||||
import kusama from './kusama';
|
|
||||||
|
|
||||||
interface Chain {
|
|
||||||
name: string;
|
|
||||||
genesisHash: string;
|
|
||||||
meta?: Metadata;
|
|
||||||
ss58Format: number;
|
|
||||||
tokenDecimals: number;
|
|
||||||
tokenSymbol: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type Chains = Record<string, Chain>;
|
|
||||||
|
|
||||||
const chains: Chains = [alexander, devPolka051, kusama].reduce((chains: Chains, { chain, genesisHash, metaCalls, ss58Format, tokenDecimals, tokenSymbol }): Chains => {
|
|
||||||
chains[genesisHash] = {
|
|
||||||
genesisHash,
|
|
||||||
meta: metaCalls
|
|
||||||
? new Metadata(Buffer.from(metaCalls, 'base64'))
|
|
||||||
: undefined,
|
|
||||||
name: chain,
|
|
||||||
ss58Format,
|
|
||||||
tokenDecimals,
|
|
||||||
tokenSymbol
|
|
||||||
};
|
|
||||||
|
|
||||||
return chains;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
export default function findChain (genesisHash: string): Chain | null {
|
|
||||||
return chains[genesisHash] || null;
|
|
||||||
}
|
|
||||||
@ -2,10 +2,11 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
|
import { ResponseTypes, TransportRequestMessage, TransportResponseMessage, MessageTypes, RequestTypes, MessageTypesWithNullRequest, SubscriptionMessageTypes, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions } from '../background/types';
|
||||||
|
|
||||||
import { injectExtension } from '@polkadot/extension-inject';
|
import { injectExtension } from '@polkadot/extension-inject';
|
||||||
|
|
||||||
import Injected from './Injected';
|
import Injected from './Injected';
|
||||||
import { ResponseTypes, TransportRequestMessage, TransportResponseMessage, MessageTypes, RequestTypes, MessageTypesWithNullRequest, SubscriptionMessageTypes, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions } from '../background/types';
|
|
||||||
|
|
||||||
// when sending a message from the injector to the extension, we
|
// when sending a message from the injector to the extension, we
|
||||||
// - create an event - this we send to the loader
|
// - create an event - this we send to the loader
|
||||||
|
|||||||
@ -14,6 +14,7 @@ const manifest = require('./manifest.json');
|
|||||||
|
|
||||||
const packages = [
|
const packages = [
|
||||||
'extension',
|
'extension',
|
||||||
|
'extension-chains',
|
||||||
'extension-inject',
|
'extension-inject',
|
||||||
'extension-ui'
|
'extension-ui'
|
||||||
];
|
];
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"@polkadot/extension": ["packages/extension/src"],
|
"@polkadot/extension": ["packages/extension/src"],
|
||||||
"@polkadot/extension/*": ["packages/extension/src/*"],
|
"@polkadot/extension/*": ["packages/extension/src/*"],
|
||||||
|
"@polkadot/extension-chains": ["packages/extension-chains/src"],
|
||||||
|
"@polkadot/extension-chains/*": ["packages/extension-chains/src/*"],
|
||||||
"@polkadot/extension-dapp": ["packages/extension-dapp/src"],
|
"@polkadot/extension-dapp": ["packages/extension-dapp/src"],
|
||||||
"@polkadot/extension-dapp/*": ["packages/extension-dapp/src/*"],
|
"@polkadot/extension-dapp/*": ["packages/extension-dapp/src/*"],
|
||||||
"@polkadot/extension-inject": ["packages/extension-inject/src"],
|
"@polkadot/extension-inject": ["packages/extension-inject/src"],
|
||||||
|
|||||||
56
yarn.lock
56
yarn.lock
@ -2061,6 +2061,16 @@
|
|||||||
react-copy-to-clipboard "^5.0.1"
|
react-copy-to-clipboard "^5.0.1"
|
||||||
styled-components "^4.3.1"
|
styled-components "^4.3.1"
|
||||||
|
|
||||||
|
"@polkadot/react-qr@^0.43.0-beta.10":
|
||||||
|
version "0.43.0-beta.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/@polkadot/react-qr/-/react-qr-0.43.0-beta.10.tgz#ab050288473d04822a5b85e0eb6d184c224d0656"
|
||||||
|
integrity sha512-2PQn3QRfv7CTT1Xl5JvLmZatk1IIFnaCXaLMWWY54M3JlrwVSAHn3OMKlmHw/kwS8bYoSDKbYL/Qhrtustalrw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.5.5"
|
||||||
|
"@types/react-qr-reader" "^2.1.1"
|
||||||
|
qrcode-generator "^1.4.3"
|
||||||
|
react-qr-reader "^2.2.1"
|
||||||
|
|
||||||
"@polkadot/rpc-core@^0.91.0-beta.22":
|
"@polkadot/rpc-core@^0.91.0-beta.22":
|
||||||
version "0.91.0-beta.22"
|
version "0.91.0-beta.22"
|
||||||
resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-0.91.0-beta.22.tgz#886855d016eea3143f993d81ee3ed6b8b7a59899"
|
resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-0.91.0-beta.22.tgz#886855d016eea3143f993d81ee3ed6b8b7a59899"
|
||||||
@ -2406,6 +2416,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-qr-reader@^2.1.1":
|
||||||
|
version "2.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-qr-reader/-/react-qr-reader-2.1.2.tgz#da7a674cf9e3ceb1c026e89d5aa41fb1587fc66b"
|
||||||
|
integrity sha512-tDLqqiQMZlTKBKtWedcf7QZ3L9fyz8nPHNq/j+1mcJoEvrdM3Y89i3/FKwdmZ4H7G6uUk4JqShpWXLCuYpZK+w==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react-router-dom@^4.3.5":
|
"@types/react-router-dom@^4.3.5":
|
||||||
version "4.3.5"
|
version "4.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.5.tgz#72f229967690c890d00f96e6b85e9ee5780db31f"
|
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.5.tgz#72f229967690c890d00f96e6b85e9ee5780db31f"
|
||||||
@ -8767,6 +8784,11 @@ jsprim@^1.2.2:
|
|||||||
json-schema "0.2.3"
|
json-schema "0.2.3"
|
||||||
verror "1.10.0"
|
verror "1.10.0"
|
||||||
|
|
||||||
|
jsqr@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/jsqr/-/jsqr-1.2.0.tgz#f93fc65fa7d1ded78b1bcb020fa044352b04261a"
|
||||||
|
integrity sha512-wKcQS9QC2VHGk7aphWCp1RrFyC0CM6fMgC5prZZ2KV/Lk6OKNoCod9IR6bao+yx3KPY0gZFC5dc+h+KFzCI0Wg==
|
||||||
|
|
||||||
jsx-ast-utils@^2.1.0:
|
jsx-ast-utils@^2.1.0:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz#4d4973ebf8b9d2837ee91a8208cc66f3a2776cfb"
|
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz#4d4973ebf8b9d2837ee91a8208cc66f3a2776cfb"
|
||||||
@ -11583,6 +11605,11 @@ q@^1.1.2, q@^1.5.1:
|
|||||||
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
||||||
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
|
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
|
||||||
|
|
||||||
|
qrcode-generator@^1.4.3:
|
||||||
|
version "1.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/qrcode-generator/-/qrcode-generator-1.4.3.tgz#4876e8f280e65b6c94615f4c19c484f6b964b199"
|
||||||
|
integrity sha512-++rVRvMRq5BlHfmAafl8a4ppUntzUxCCUTT2t0siUgqKwdnqRzY8IH6f6WSX5dZUhD2Ul5/MIKuTJddflwrGzw==
|
||||||
|
|
||||||
qs@6.7.0:
|
qs@6.7.0:
|
||||||
version "6.7.0"
|
version "6.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||||
@ -11741,6 +11768,15 @@ react-lifecycles-compat@^3.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||||
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||||
|
|
||||||
|
react-qr-reader@^2.2.1:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-qr-reader/-/react-qr-reader-2.2.1.tgz#dc89046d1c1a1da837a683dd970de5926817d55b"
|
||||||
|
integrity sha512-EL5JEj53u2yAOgtpAKAVBzD/SiKWn0Bl7AZy6ZrSf1lub7xHwtaXe6XSx36Wbhl1VMGmvmrwYMRwO1aSCT2fwA==
|
||||||
|
dependencies:
|
||||||
|
jsqr "^1.2.0"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
webrtc-adapter "^7.2.1"
|
||||||
|
|
||||||
react-router-dom@^5.0.1:
|
react-router-dom@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be"
|
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be"
|
||||||
@ -12363,6 +12399,13 @@ rsvp@^4.8.4:
|
|||||||
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
|
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
|
||||||
integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
|
integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
|
||||||
|
|
||||||
|
rtcpeerconnection-shim@^1.2.15:
|
||||||
|
version "1.2.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz#e7cc189a81b435324c4949aa3dfb51888684b243"
|
||||||
|
integrity sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==
|
||||||
|
dependencies:
|
||||||
|
sdp "^2.6.0"
|
||||||
|
|
||||||
run-async@^2.2.0:
|
run-async@^2.2.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
|
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
|
||||||
@ -12464,6 +12507,11 @@ schema-utils@^2.0.0, schema-utils@^2.0.1:
|
|||||||
ajv "^6.10.2"
|
ajv "^6.10.2"
|
||||||
ajv-keywords "^3.4.1"
|
ajv-keywords "^3.4.1"
|
||||||
|
|
||||||
|
sdp@^2.10.0, sdp@^2.6.0:
|
||||||
|
version "2.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/sdp/-/sdp-2.10.0.tgz#643fff1a43cdba54a739c7b202f56bd854474af2"
|
||||||
|
integrity sha512-H+VjfyQpRz9GezhshJmkXTtCAT9/2g9az3GFDPYfGOz0eAOQU1fCrL3S9Dq/eUT9FtOyLi/czdR9PzK3fKUYOQ==
|
||||||
|
|
||||||
secp256k1@^3.7.0:
|
secp256k1@^3.7.0:
|
||||||
version "3.7.1"
|
version "3.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.7.1.tgz#12e473e0e9a7c2f2d4d4818e722ad0e14cc1e2f1"
|
resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.7.1.tgz#12e473e0e9a7c2f2d4d4818e722ad0e14cc1e2f1"
|
||||||
@ -14454,6 +14502,14 @@ webpackbar@3.2.0:
|
|||||||
text-table "^0.2.0"
|
text-table "^0.2.0"
|
||||||
wrap-ansi "^5.1.0"
|
wrap-ansi "^5.1.0"
|
||||||
|
|
||||||
|
webrtc-adapter@^7.2.1:
|
||||||
|
version "7.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/webrtc-adapter/-/webrtc-adapter-7.3.0.tgz#a65e18aad42759bab6ced7f8cfff11b051321e2a"
|
||||||
|
integrity sha512-pKcwt6IR6RLCD6jlcdOOi88iVwdzppHlkOhtgTSuZHtYTxdD09t5fA1Di7GJU7je8oHcCBlNfb7zwBsetERnmQ==
|
||||||
|
dependencies:
|
||||||
|
rtcpeerconnection-shim "^1.2.15"
|
||||||
|
sdp "^2.10.0"
|
||||||
|
|
||||||
websocket-driver@>=0.5.1:
|
websocket-driver@>=0.5.1:
|
||||||
version "0.7.3"
|
version "0.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9"
|
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user