├── .123trigger ├── .codeclimate.yml ├── .editorconfig ├── .eslintrc.js ├── .github └── workflows │ ├── pr-any.yml │ ├── push-master.yml │ └── test-push.yaml ├── .gitignore ├── .npmignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.cjs ├── .yarn ├── plugins │ └── @yarnpkg │ │ ├── plugin-interactive-tools.cjs │ │ └── plugin-version.cjs └── releases │ └── yarn-berry.cjs ├── .yarnrc.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── Screenshot 2024-01-07 at 23.14.20.png ├── __mocks__ ├── chrome.ts ├── fileMock.js └── react-i18next.ts ├── babel.config.cjs ├── bumpVersion.js ├── i18next-scanner.config.js ├── jest.config.cjs ├── package.json ├── packages ├── extension-base │ ├── LICENSE │ ├── README.md │ ├── package.json │ └── src │ │ ├── background │ │ ├── RequestBytesSign.ts │ │ ├── RequestExtrinsicSign.ts │ │ ├── handlers │ │ │ ├── Extension.test.ts │ │ │ ├── Extension.ts │ │ │ ├── State.ts │ │ │ ├── Tabs.ts │ │ │ ├── index.ts │ │ │ └── subscriptions.ts │ │ └── types.ts │ │ ├── defaults.ts │ │ ├── index.ts │ │ ├── packageInfo.ts │ │ ├── page │ │ ├── Accounts.ts │ │ ├── Injected.ts │ │ ├── Metadata.ts │ │ ├── PostMessageProvider.ts │ │ ├── Signer.ts │ │ ├── index.ts │ │ └── types.ts │ │ ├── stores │ │ ├── Accounts.ts │ │ ├── Base.ts │ │ ├── Metadata.ts │ │ └── index.ts │ │ ├── types.ts │ │ └── utils │ │ ├── canDerive.ts │ │ └── index.ts ├── extension-chains │ ├── LICENSE │ ├── README.md │ ├── package.json │ └── src │ │ ├── index.ts │ │ ├── packageInfo.ts │ │ └── types.ts ├── extension-compat-metamask │ ├── LICENSE │ ├── README.md │ ├── package.json │ └── src │ │ ├── index.ts │ │ └── packageInfo.ts ├── extension-dapp │ ├── LICENSE │ ├── README.md │ ├── package.json │ └── src │ │ ├── index.ts │ │ ├── packageInfo.ts │ │ ├── util.ts │ │ ├── wrapBytes.spec.ts │ │ └── wrapBytes.ts ├── extension-inject │ ├── LICENSE │ ├── README.md │ ├── package.json │ └── src │ │ ├── chrome.ts │ │ ├── index.ts │ │ ├── packageInfo.ts │ │ └── types.ts ├── extension-ui │ ├── .skip-build │ ├── .skip-npm │ ├── LICENSE │ ├── README.md │ ├── package.json │ └── src │ │ ├── MetadataCache.ts │ │ ├── Popup │ │ ├── Accounts │ │ │ ├── Account.test.tsx │ │ │ ├── Account.tsx │ │ │ ├── AccountsTree.tsx │ │ │ ├── AddAccount.tsx │ │ │ ├── AddAccountImage.tsx │ │ │ └── index.tsx │ │ ├── AuthManagement │ │ │ ├── WebsiteEntry.tsx │ │ │ └── index.tsx │ │ ├── Authorize │ │ │ ├── Authorize.test.tsx │ │ │ ├── Request.tsx │ │ │ └── index.tsx │ │ ├── CreateAccount │ │ │ ├── CreateAccount.test.tsx │ │ │ ├── Mnemonic.tsx │ │ │ └── index.tsx │ │ ├── Derive │ │ │ ├── AddressDropdown.tsx │ │ │ ├── DerivationPath.tsx │ │ │ ├── Derive.test.tsx │ │ │ ├── SelectParent.tsx │ │ │ └── index.tsx │ │ ├── Export.test.tsx │ │ ├── Export.tsx │ │ ├── ExportAll.tsx │ │ ├── ExportQr.tsx │ │ ├── Forget.tsx │ │ ├── ImportLedger.tsx │ │ ├── ImportQr.test.tsx │ │ ├── ImportQr.tsx │ │ ├── ImportSeed │ │ │ ├── ImportSeed.test.tsx │ │ │ ├── SeedAndPath.tsx │ │ │ └── index.tsx │ │ ├── Metadata │ │ │ ├── Request.tsx │ │ │ └── index.tsx │ │ ├── PhishingDetected.tsx │ │ ├── RestoreJson.tsx │ │ ├── Signing │ │ │ ├── Bytes.tsx │ │ │ ├── Extrinsic.tsx │ │ │ ├── LedgerSign.tsx │ │ │ ├── Qr.tsx │ │ │ ├── Request │ │ │ │ ├── SignArea.tsx │ │ │ │ └── index.tsx │ │ │ ├── Signing.test.tsx │ │ │ ├── TransactionIndex.tsx │ │ │ ├── Unlock.tsx │ │ │ ├── index.tsx │ │ │ └── metadataMock.ts │ │ ├── Welcome.tsx │ │ └── index.tsx │ │ ├── adapter.d.ts │ │ ├── assets │ │ ├── arrow-down.svg │ │ ├── checkmark.svg │ │ ├── details.svg │ │ ├── images.d.ts │ │ ├── pjs.svg │ │ ├── spinner-white.png │ │ └── spinner.png │ │ ├── components │ │ ├── AccountNamePasswordCreation.test.tsx │ │ ├── AccountNamePasswordCreation.tsx │ │ ├── ActionBar.tsx │ │ ├── ActionText.tsx │ │ ├── Address.test.tsx │ │ ├── Address.tsx │ │ ├── BackButton.tsx │ │ ├── Box.tsx │ │ ├── Button.tsx │ │ ├── ButtonArea.tsx │ │ ├── ButtonWithSubtitle.tsx │ │ ├── Checkbox.tsx │ │ ├── Dropdown.tsx │ │ ├── ErrorBoundary.tsx │ │ ├── Icon.tsx │ │ ├── Identicon.tsx │ │ ├── InputFileWithLabel.tsx │ │ ├── InputFilter.tsx │ │ ├── InputWithLabel.tsx │ │ ├── Label.tsx │ │ ├── Link.tsx │ │ ├── List.tsx │ │ ├── Loading.tsx │ │ ├── Main.tsx │ │ ├── Menu.tsx │ │ ├── MenuDivider.tsx │ │ ├── MenuItem.tsx │ │ ├── MnemonicSeed.tsx │ │ ├── NextStepButton.tsx │ │ ├── Spinner.tsx │ │ ├── Svg.tsx │ │ ├── Switch.tsx │ │ ├── Table.tsx │ │ ├── TextAreaWithLabel.tsx │ │ ├── TextInputs.ts │ │ ├── Toast │ │ │ ├── Toast.tsx │ │ │ └── ToastProvider.tsx │ │ ├── ValidatedInput.tsx │ │ ├── VerticalSpace.tsx │ │ ├── View.tsx │ │ ├── Warning.tsx │ │ ├── contexts.tsx │ │ ├── index.ts │ │ ├── themes.ts │ │ ├── translate.ts │ │ └── types.ts │ │ ├── createView.tsx │ │ ├── hooks │ │ ├── useGenesisHashOptions.ts │ │ ├── useIsMounted.ts │ │ ├── useIsPopup.ts │ │ ├── useLedger.ts │ │ ├── useMetadata.ts │ │ ├── useOutsideClick.ts │ │ ├── useToast.tsx │ │ └── useTranslation.ts │ │ ├── i18n │ │ ├── Backend.ts │ │ ├── cache.ts │ │ └── i18n.ts │ │ ├── index.ts │ │ ├── messaging.test.ts │ │ ├── messaging.ts │ │ ├── partials │ │ ├── Header.test.tsx │ │ ├── Header.tsx │ │ ├── HeaderWithSteps.tsx │ │ ├── MenuAdd.tsx │ │ ├── MenuSettings.tsx │ │ ├── Name.tsx │ │ ├── Password.tsx │ │ ├── QrCodeComponent.tsx │ │ └── index.ts │ │ ├── testHelpers.ts │ │ ├── types.ts │ │ └── util │ │ ├── buildHierarchy.test.ts │ │ ├── buildHierarchy.ts │ │ ├── chains.ts │ │ ├── defaultType.ts │ │ ├── getLanguageOptions.ts │ │ ├── getNetworkMap.ts │ │ ├── getParentNameSuri.ts │ │ ├── legerChains.ts │ │ ├── nextDerivationPath.test.ts │ │ ├── nextDerivationPath.ts │ │ ├── typeGuards.ts │ │ └── validators.ts ├── extension │ ├── .skip-npm │ ├── LICENSE │ ├── README.md │ ├── manifest.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── fonts │ │ │ ├── fonts.css │ │ │ ├── poppins-black.ttf │ │ │ ├── poppins-bold.ttf │ │ │ ├── poppins-extrabold.ttf │ │ │ ├── poppins-medium.ttf │ │ │ ├── poppins-regular.ttf │ │ │ └── poppins-semibold.ttf │ │ ├── images │ │ │ ├── icon-128.png │ │ │ ├── icon-16.png │ │ │ ├── icon-32.png │ │ │ ├── icon-48.png │ │ │ ├── icon-64.png │ │ │ └── pjs.svg │ │ ├── img │ │ │ └── token-icons │ │ │ │ ├── token-icon-0.png │ │ │ │ ├── token-icon-1.png │ │ │ │ ├── token-icon-2.png │ │ │ │ ├── token-icon-3.png │ │ │ │ ├── token-icon-4.png │ │ │ │ ├── token-icon-5.png │ │ │ │ ├── token-icon-6.png │ │ │ │ ├── token-icon-7.png │ │ │ │ ├── token-icon-8.png │ │ │ │ └── token-icon-9.png │ │ ├── index.html │ │ ├── locales │ │ │ ├── en │ │ │ │ └── translation.json │ │ │ ├── fr │ │ │ │ └── translation.json │ │ │ ├── pl │ │ │ │ └── translation.json │ │ │ ├── th │ │ │ │ └── translation.json │ │ │ ├── tr │ │ │ │ └── translation.json │ │ │ ├── ur │ │ │ │ └── translation.json │ │ │ └── zh │ │ │ │ └── translation.json │ │ ├── notification.html │ │ └── style │ │ │ ├── accounts.css │ │ │ ├── dropdowns.css │ │ │ ├── main.css │ │ │ ├── navigation.css │ │ │ ├── reef-styles-extension.css │ │ │ ├── reef-styles.css │ │ │ ├── token-balances.css │ │ │ ├── token-card.css │ │ │ ├── uik │ │ │ ├── button.css │ │ │ ├── card.css │ │ │ ├── cta.css │ │ │ ├── fish-animation.css │ │ │ ├── icon.css │ │ │ ├── index.css │ │ │ ├── loading.css │ │ │ ├── notifications.css │ │ │ └── text.css │ │ │ └── variables.css │ ├── src │ │ ├── background.ts │ │ ├── content.ts │ │ ├── extension.ts │ │ ├── packageInfo.ts │ │ └── page.ts │ ├── updateExtensionSrc.cjs │ ├── webpack.chunking.cjs │ ├── webpack.config.cjs │ ├── webpack.extension.cjs │ ├── webpack.shared.cjs │ └── webpack.watch.cjs └── reef │ ├── extension-base │ ├── background │ │ └── handlers │ │ │ ├── ReefExtension.ts │ │ │ └── ReefTabs.ts │ └── src │ │ └── page │ │ ├── ReefProvider.ts │ │ └── ReefSigner.ts │ └── extension-ui │ ├── components │ ├── Bind.tsx │ ├── HeaderComponent.tsx │ ├── Logos.tsx │ ├── ReefContext.tsx │ ├── SigningOrChildren.tsx │ ├── Swap.tsx │ ├── Transfer.tsx │ ├── dashboard │ │ ├── TokenBalances.tsx │ │ ├── TokenMenu.tsx │ │ └── TokenPill.tsx │ └── utils.ts │ ├── hooks │ └── useReefSigners.ts │ ├── messaging.ts │ ├── notify │ ├── Alert.tsx │ ├── Icon.tsx │ ├── Notifications.tsx │ └── index.tsx │ ├── state │ ├── selectedExtensionAccount$.ts │ └── util.ts │ ├── uik │ ├── Button.tsx │ ├── CTA.tsx │ ├── Card.tsx │ ├── FishAnimation.tsx │ ├── Icon.tsx │ ├── Loading.tsx │ ├── UikText.tsx │ └── index.tsx │ └── validated-tokens-mainnet.json ├── rollup.config.mjs ├── source.zip ├── test-bundle.html ├── tsconfig.json └── yarn.lock /.123trigger: -------------------------------------------------------------------------------- 1 | 0.33.20.33.20.33.30.33.4 2 | 0.37.2 3 | 0.38.2 4 | 0.38.3 5 | 0.38.4 6 | 0.38.5 7 | 0.38.6 8 | 0.38.7 9 | 0.38.8 10 | 0.39.2 11 | 0.39.3 12 | 0.40.2 13 | 0.40.3 14 | 0.40.4 15 | 0.41.2 16 | 0.41.3 17 | 0.41.4 18 | 0.41.5 19 | 1.0.1 20 | 1.0.2 21 | 1.0.3 22 | 1.0.4 23 | 1.0.5 24 | 1.0.6 25 | 1.0.7 26 | 1.0.8 27 | 1.0.9 28 | 1.0.10 29 | 1.0.11 30 | 1.0.12 31 | 1.0.13 32 | 1.0.14 33 | 1.0.15 34 | 1.0.16 35 | 1.0.17 36 | 1.0.18 37 | 1.0.19 38 | 39 | 1.0.20 40 | 1.0.21 41 | 1.0.22 -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | exclude_patterns: 2 | - "**/*.spec.js" 3 | - "**/*.spec.ts" 4 | - "**/*.test.js" 5 | - "**/*.test.ts" 6 | - "docs/**/*.js" 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | indent_style=space 4 | indent_size=2 5 | tab_width=2 6 | end_of_line=lf 7 | charset=utf-8 8 | trim_trailing_whitespace=true 9 | max_line_length=120 10 | insert_final_newline=true 11 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | const base = require("@reef-defi/dev/config/eslint.cjs"); 5 | 6 | module.exports = { 7 | ...base, 8 | ignorePatterns: [ 9 | ".eslintrc.js", 10 | ".github/**", 11 | ".vscode/**", 12 | ".yarn/**", 13 | "**/build/*", 14 | "**/coverage/*", 15 | "**/node_modules/*", 16 | ], 17 | parserOptions: { 18 | ...base.parserOptions, 19 | project: ["./tsconfig.json"], 20 | }, 21 | rules: { 22 | ...base.rules, 23 | "sort-keys": "off", 24 | "react/jsx-sort-props": "off", 25 | // this seems very broken atm, false positives 26 | "@typescript-eslint/unbound-method": "off", 27 | "@typescript-eslint/no-explicit-any": "off", 28 | 29 | "@typescript-eslint/explicit-function-return-type": "off", 30 | "@typescript-eslint/no-var-requires": "off", 31 | "@typescript-eslint/no-unsafe-argument": "off", 32 | "@typescript-eslint/no-unsafe-assignment": "off", 33 | "@typescript-eslint/no-unsafe-call": "off", 34 | "@typescript-eslint/no-unsafe-member-access": "off", 35 | "@typescript-eslint/no-unsafe-return": "off", 36 | "@typescript-eslint/restrict-plus-operands": "off", 37 | "@typescript-eslint/restrict-template-expressions": "off", 38 | "@typescript-eslint/ban-ts-comment": "off", 39 | "no-void": "off", 40 | "@typescript-eslint/no-misused-promises": [ 41 | "error", 42 | { checksVoidReturn: false }, 43 | ], 44 | "react/jsx-no-bind": "off", 45 | "react/jsx-closing-bracket-location": "off", 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /.github/workflows/pr-any.yml: -------------------------------------------------------------------------------- 1 | name: PR 2 | on: [pull_request] 3 | 4 | jobs: 5 | pr: 6 | strategy: 7 | matrix: 8 | step: ['lint', 'test', 'build'] 9 | name: ${{ matrix.step }} 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | with: 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | name: checkout 16 | - name: Setup node v16 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: 16 20 | - name: ${{ matrix.step }} 21 | run: | 22 | yarn install --immutable | grep -v 'YN0013' 23 | yarn ${{ matrix.step }} 24 | -------------------------------------------------------------------------------- /.github/workflows/push-master.yml: -------------------------------------------------------------------------------- 1 | name: Master 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | master: 9 | if: "! startsWith(github.event.head_commit.message, '[CI Skip]')" 10 | strategy: 11 | matrix: 12 | step: ['build:release'] 13 | name: ${{ matrix.step }} 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | name: checkout 20 | - name: Setup node v16 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: 16 24 | - name: ${{ matrix.step }} 25 | env: 26 | GH_RELEASE_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | GH_RELEASE_FILES: master-build.zip,master-src.zip 28 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 29 | run: | 30 | node -v 31 | yarn install 32 | yarn ${{ matrix.step }} 33 | -------------------------------------------------------------------------------- /.github/workflows/test-push.yaml: -------------------------------------------------------------------------------- 1 | name: Master 2 | on: 3 | push: 4 | branches: 5 | - test 6 | 7 | jobs: 8 | master: 9 | if: "! startsWith(github.event.head_commit.message, '[CI Skip]')" 10 | strategy: 11 | matrix: 12 | step: ['build:release'] 13 | name: ${{ matrix.step }} 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | with: 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | - name: ${{ matrix.step }} 20 | env: 21 | GH_RELEASE_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | GH_RELEASE_FILES: master-build.zip,master-src.zip 23 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 24 | run: | 25 | rm -rf node_modules 26 | yarn install --immutable --check-cache 27 | node -v 28 | yarn ${{ matrix.step }} 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | coverage/ 3 | node_modules/ 4 | tmp/ 5 | .DS_Store 6 | .env.local 7 | .env.development.local 8 | .env.test.local 9 | .env.production.local 10 | .npmrc 11 | .pnp.* 12 | cc-test-reporter 13 | lerna-debug.log* 14 | master-build.zip 15 | master-src.zip 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | package-lock.json 20 | _book 21 | docs/html 22 | .idea 23 | .yarn/* 24 | !.yarn/releases 25 | !.yarn/plugins 26 | /.yalc/ 27 | yalc.lock 28 | /*.iml 29 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 12 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | coverage 3 | packages 4 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = require("@polkadot/dev/config/prettier.cjs"); 5 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | plugins: 4 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs 5 | spec: "@yarnpkg/plugin-interactive-tools" 6 | - path: .yarn/plugins/@yarnpkg/plugin-version.cjs 7 | spec: "@yarnpkg/plugin-version" 8 | 9 | yarnPath: .yarn/releases/yarn-berry.cjs 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## What? 4 | 5 | Individuals making significant and valuable contributions are given commit-access to a project to contribute as they see fit. 6 | A project is more like an open wiki than a standard guarded open source project. 7 | 8 | ## Rules 9 | 10 | There are a few basic ground-rules for contributors (including the maintainer(s) of the project): 11 | 12 | 1. **No `--force` pushes** or modifying the Git history in any way. If you need to rebase, ensure you do it in your own repo. 13 | 2. **Non-master branches**, prefixed with a short name moniker (e.g. `-`) must be used for ongoing work. 14 | 3. **All modifications** must be made in a **pull-request** to solicit feedback from other contributors. 15 | 4. A pull-request *must not be merged until CI* has finished successfully. 16 | 17 | #### Merging pull requests once CI is successful: 18 | - A pull request with no large change to logic that is an urgent fix may be merged after a non-author contributor has reviewed it well. 19 | - No PR should be merged until all reviews' comments are addressed. 20 | 21 | #### Reviewing pull requests: 22 | When reviewing a pull request, the end-goal is to suggest useful changes to the author. Reviews should finish with approval unless there are issues that would result in: 23 | 24 | - Buggy behaviour. 25 | - Undue maintenance burden. 26 | - Breaking with house coding style. 27 | - Pessimisation (i.e. reduction of speed as measured in the projects benchmarks). 28 | - Feature reduction (i.e. it removes some aspect of functionality that a significant minority of users rely on). 29 | - Uselessness (i.e. it does not strictly add a feature or fix a known issue). 30 | 31 | #### Reviews may not be used as an effective veto for a PR because: 32 | - There exists a somewhat cleaner/better/faster way of accomplishing the same feature/fix. 33 | - It does not fit well with some other contributors' longer-term vision for the project. 34 | 35 | ## Releases 36 | 37 | Declaring formal releases remains the prerogative of the project maintainer(s). 38 | 39 | ## Changes to this arrangement 40 | 41 | This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change. 42 | 43 | ## Heritage 44 | 45 | These contributing guidelines are modified from the "OPEN Open Source Project" guidelines for the Level project: [https://github.com/Level/community/blob/master/CONTRIBUTING.md](https://github.com/Level/community/blob/master/CONTRIBUTING.md) 46 | -------------------------------------------------------------------------------- /Screenshot 2024-01-07 at 23.14.20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reef-defi/browser-extension/3514bd2f1c4868de07f4a50b9365d0f1083c127a/Screenshot 2024-01-07 at 23.14.20.png -------------------------------------------------------------------------------- /__mocks__/chrome.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /* eslint-disable @typescript-eslint/no-explicit-any */ 5 | import chrome from 'sinon-chrome'; 6 | 7 | class MessagingFake { 8 | private listeners: ((...params: unknown[]) => unknown)[] = []; 9 | 10 | get onMessage (): any { 11 | return { 12 | addListener: (cb: (...params: unknown[]) => unknown) => this.listeners.push(cb) 13 | }; 14 | } 15 | 16 | get onDisconnect (): any { 17 | return { 18 | addListener: (): any => undefined 19 | }; 20 | } 21 | 22 | postMessage (data: unknown): void { 23 | this.listeners.forEach((cb) => cb.call(this, data)); 24 | } 25 | } 26 | 27 | const messagingFake = new MessagingFake(); 28 | 29 | chrome.runtime.connect.returns(messagingFake); 30 | 31 | // eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-member-access 32 | (window as any).chrome = chrome; 33 | 34 | export default chrome; 35 | -------------------------------------------------------------------------------- /__mocks__/fileMock.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // eslint-disable-line 5 | module.exports = ''; 6 | -------------------------------------------------------------------------------- /__mocks__/react-i18next.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React from 'react'; 5 | 6 | interface useTranslationReturnObj { 7 | i18n: { changeLanguage: () => Promise; }; 8 | t: (str: string) => string; 9 | } 10 | 11 | export const useTranslation = (): useTranslationReturnObj => { 12 | return { 13 | i18n: { 14 | changeLanguage: () => new Promise(() => { /**/ }) 15 | }, 16 | t: (str: string) => str 17 | }; 18 | }; 19 | 20 | export const withTranslation = () => (component: React.ReactElement): React.ReactElement => component; 21 | 22 | export const Trans = ({ children }: { children: React.ReactElement }): React.ReactElement => children; 23 | 24 | export default withTranslation; 25 | -------------------------------------------------------------------------------- /babel.config.cjs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module.exports = require('@reef-defi/dev/config/babel-config-cjs.cjs'); 5 | -------------------------------------------------------------------------------- /bumpVersion.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const NEW_VERSION = '1.0.23'; 3 | 4 | function getPackageJson (filePath) { 5 | return { json: JSON.parse(fs.readFileSync(filePath, 'utf8')), filePath: filePath }; 6 | } 7 | 8 | function setPackageJson (filePath, json) { 9 | fs.writeFileSync(filePath, JSON.stringify(json, null, 2)); 10 | } 11 | 12 | const packageNames = [ 13 | 'extension', 14 | 'extension-base', 15 | 'extension-chains', 16 | 'extension-compat-metamask', 17 | 'extension-dapp', 18 | 'extension-inject', 19 | 'extension-ui' 20 | ]; 21 | 22 | function getPackageJsonPaths (pkgNames) { 23 | const packages = pkgNames.map((name) => `./packages/${name}/package.json`); 24 | 25 | return ['package.json', ...packages]; 26 | } 27 | 28 | function updateVersions (json, pkgNames, ver) { 29 | console.log('IN PACKAGE=', json.name); 30 | json.version = ver; 31 | pkgNames.forEach((name) => { 32 | const depName = `@reef-defi/${name}`; 33 | const toVersion = `^${ver}`; 34 | const dependency = json.dependencies && json.dependencies[depName]; 35 | 36 | if (dependency)console.log('bumping=', depName, ' to ', toVersion); 37 | 38 | if (dependency) { 39 | json.dependencies[depName] = toVersion; 40 | } 41 | }); 42 | 43 | return json; 44 | } 45 | 46 | getPackageJsonPaths(packageNames).map(getPackageJson).forEach((jsonFileObj) => { 47 | updateVersions(jsonFileObj.json, packageNames, NEW_VERSION); 48 | setPackageJson(jsonFileObj.filePath, jsonFileObj.json); 49 | }); 50 | -------------------------------------------------------------------------------- /i18next-scanner.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const typescript = require('typescript'); 7 | 8 | function transform (file, enc, done) { 9 | const { ext } = path.parse(file.path); 10 | 11 | if (ext === '.tsx') { 12 | const content = fs.readFileSync(file.path, enc); 13 | 14 | const { outputText } = typescript.transpileModule(content, { 15 | compilerOptions: { 16 | target: 'es2018' 17 | }, 18 | fileName: path.basename(file.path) 19 | }); 20 | 21 | this.parser.parseFuncFromString(outputText); 22 | } 23 | 24 | done(); 25 | } 26 | 27 | module.exports = { 28 | input: [ 29 | 'packages/extension-ui/src/**/*.{ts,tsx}', 30 | // Use ! to filter out files or directories 31 | '!packages/*/src/**/*.spec.{ts,tsx}', 32 | '!packages/*/src/i18n/**', 33 | '!**/node_modules/**' 34 | ], 35 | options: { 36 | debug: true, 37 | defaultLng: 'en', 38 | func: { 39 | extensions: ['.tsx', '.ts'], 40 | list: ['t', 'i18next.t', 'i18n.t'] 41 | }, 42 | keySeparator: false, // key separator 43 | lngs: ['en'], 44 | nsSeparator: false, // namespace separator 45 | resource: { 46 | jsonIndent: 2, 47 | lineEnding: '\n', 48 | loadPath: 'packages/extension/public/locales/{{lng}}/{{ns}}.json', 49 | savePath: 'packages/extension/public/locales/{{lng}}/{{ns}}.json' 50 | }, 51 | trans: { 52 | component: 'Trans' 53 | } 54 | }, 55 | output: './', 56 | transform 57 | }; 58 | -------------------------------------------------------------------------------- /jest.config.cjs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | const config = require('@reef-defi/dev/config/jest.cjs'); 5 | 6 | module.exports = { 7 | ...config, 8 | moduleNameMapper: { 9 | '@reef-defi/extension-(base|chains|compat-metamask|dapp|inject|ui)(.*)$': '/packages/extension-$1/src/$2', 10 | // eslint-disable-next-line sort-keys 11 | '@reef-defi/extension(.*)$': '/packages/extension/src/$1', 12 | '\\.(css|less)$': 'empty/object', 13 | '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 14 | '/__mocks__/fileMock.js' 15 | }, 16 | testEnvironment: 'jsdom' 17 | }; 18 | -------------------------------------------------------------------------------- /packages/extension-base/README.md: -------------------------------------------------------------------------------- 1 | # @reef-defi/extension-base 2 | 3 | Functions, classes and other utilities used in `@reef-defi/extension`. These include: 4 | - background script handlers, 5 | - message passing, 6 | - scripts injected inside pages. 7 | 8 | They are primarily meant to be used in `@reef-defi/extension`, and can be broken without any notice to cater for `@reef-defi/extension`'s needs. They are exported here if you wish to use part of them in the development of your own extension. 9 | -------------------------------------------------------------------------------- /packages/extension-base/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Reef Chain", 3 | "bugs": "https://github.com/reef-defi/browser-extension/issues", 4 | "contributors": [], 5 | "description": "Functions, classes and other utilities used in @reef-defi/extension", 6 | "homepage": "https://github.com/reef-defi/browser-extension/tree/master/packages/extension-base#readme", 7 | "license": "Apache-2", 8 | "maintainers": [], 9 | "name": "@reef-defi/extension-base", 10 | "repository": { 11 | "directory": "packages/extension-base", 12 | "type": "git", 13 | "url": "https://github.com/reef-defi/browser-extension.git" 14 | }, 15 | "sideEffects": false, 16 | "type": "module", 17 | "version": "1.0.23", 18 | "main": "index.js", 19 | "dependencies": { 20 | "@babel/runtime": "^7.16.0", 21 | "@polkadot/api": "^6.7.1", 22 | "@polkadot/phishing": "^0.6.407", 23 | "@polkadot/ui-keyring": "^0.86.5", 24 | "@reef-chain/util-lib": "^0.4.0", 25 | "@reef-defi/extension-dapp": "^1.0.23", 26 | "@reef-defi/extension-inject": "^1.0.23", 27 | "@reef-defi/keyring": "^7.8.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/extension-base/src/background/RequestBytesSign.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { KeyringPair } from '@reef-defi/keyring/types'; 5 | import type { HexString } from '@reef-defi/util/types'; 6 | import type { SignerPayloadRaw } from '@polkadot/types/types'; 7 | import type { RequestSign } from './types'; 8 | 9 | import { wrapBytes } from '@reef-defi/extension-dapp/wrapBytes'; 10 | import { u8aToHex } from '@reef-defi/util'; 11 | 12 | import { TypeRegistry } from '@polkadot/types'; 13 | 14 | export default class RequestBytesSign implements RequestSign { 15 | public readonly payload: SignerPayloadRaw; 16 | 17 | constructor (payload: SignerPayloadRaw) { 18 | this.payload = payload; 19 | } 20 | 21 | sign (_registry: TypeRegistry, pair: KeyringPair): { signature: HexString } { 22 | return { 23 | signature: u8aToHex( 24 | pair.sign( 25 | wrapBytes(this.payload.data) 26 | ) 27 | ) 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/extension-base/src/background/RequestExtrinsicSign.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { KeyringPair } from '@reef-defi/keyring/types'; 5 | import type { HexString } from '@reef-defi/util/types'; 6 | import type { SignerPayloadJSON } from '@polkadot/types/types'; 7 | import type { RequestSign } from './types'; 8 | 9 | import { TypeRegistry } from '@polkadot/types'; 10 | 11 | export default class RequestExtrinsicSign implements RequestSign { 12 | public readonly payload: SignerPayloadJSON; 13 | 14 | constructor (payload: SignerPayloadJSON) { 15 | this.payload = payload; 16 | } 17 | 18 | sign (registry: TypeRegistry, pair: KeyringPair): { signature: HexString } { 19 | return (registry 20 | .createType('ExtrinsicPayload', this.payload, { version: this.payload.version }) 21 | .sign(pair) as { signature: HexString }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/extension-base/src/background/handlers/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { MessageTypes, TransportRequestMessage } from '../types'; 5 | 6 | import { assert } from '@reef-defi/util'; 7 | 8 | import ReefExtension from '../../../../reef/extension-base/background/handlers/ReefExtension'; 9 | import { ReefTabs } from '../../../../reef/extension-base/background/handlers/ReefTabs'; 10 | import { PORT_EXTENSION } from '../../defaults'; 11 | import State from './State'; 12 | 13 | const state = new State(); 14 | const extension = new ReefExtension(state); 15 | const tabs = new ReefTabs(state); 16 | 17 | export default function handler ({ id, message, request }: TransportRequestMessage, port: chrome.runtime.Port, extensionPortName = PORT_EXTENSION): void { 18 | const isExtension = port.name === extensionPortName; 19 | const sender = port.sender as chrome.runtime.MessageSender; 20 | const from = isExtension 21 | ? extensionPortName 22 | : (sender.tab && sender.tab.url) || sender.url || ''; 23 | const source = `${from}: ${id}: ${message}`; 24 | 25 | console.log(` [in] ${source}`); // :: ${JSON.stringify(request)}`); 26 | const promise = isExtension && message !== 'pub(extrinsic.sign)' && message !== 'pub(bytes.sign)' 27 | ? extension.handle(id, message, request, port) 28 | : tabs.handle(id, message, request, from, port); 29 | 30 | promise 31 | .then((response): void => { 32 | console.log(`[out] ${source}`); // :: ${JSON.stringify(response)}`); 33 | 34 | // between the start and the end of the promise, the user may have closed 35 | // the tab, in which case port will be undefined 36 | assert(port, 'Port has been disconnected'); 37 | 38 | port.postMessage({ id, response }); 39 | }) 40 | .catch((error: Error): void => { 41 | console.log(`[err] ${source}:: ${error.message}`); 42 | 43 | // only send message back to port if it's still connected 44 | if (port) { 45 | port.postMessage({ error: error.message, id }); 46 | } 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /packages/extension-base/src/background/handlers/subscriptions.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { MessageTypesWithSubscriptions, SubscriptionMessageTypes } from '../types'; 5 | 6 | type Subscriptions = Record; 7 | 8 | const subscriptions: Subscriptions = {}; 9 | 10 | // return a subscription callback, that will send the data to the caller via the port 11 | export function createSubscription (id: string, port: chrome.runtime.Port): (data: SubscriptionMessageTypes[TMessageType]) => void { 12 | subscriptions[id] = port; 13 | 14 | return (subscription: unknown): void => { 15 | if (subscriptions[id]) { 16 | port.postMessage({ id, subscription }); 17 | } 18 | }; 19 | } 20 | 21 | // clear a previous subscriber 22 | export function unsubscribe (id: string): void { 23 | if (subscriptions[id]) { 24 | console.log(`Unsubscribing from ${id}`); 25 | 26 | delete subscriptions[id]; 27 | } else { 28 | console.error(`Unable to unsubscribe from ${id}`); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/extension-base/src/defaults.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-base authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | const ALLOWED_PATH = ['/', '/account/import-ledger', '/account/restore-json'] as const; 5 | const PORT_CONTENT = 'reef_content'; 6 | const PHISHING_PAGE_REDIRECT = '/phishing-page-detected'; 7 | const PORT_EXTENSION = 'reef_extension'; 8 | const PORT_PAGE = 'reef_page'; 9 | const PASSWORD_EXPIRY_MIN = 15; 10 | const PASSWORD_EXPIRY_MS = PASSWORD_EXPIRY_MIN * 60 * 1000; 11 | 12 | export { 13 | ALLOWED_PATH, 14 | PASSWORD_EXPIRY_MIN, 15 | PASSWORD_EXPIRY_MS, 16 | PHISHING_PAGE_REDIRECT, 17 | PORT_CONTENT, 18 | PORT_EXTENSION, 19 | PORT_PAGE 20 | }; 21 | -------------------------------------------------------------------------------- /packages/extension-base/src/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-base authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // no direct exports 5 | -------------------------------------------------------------------------------- /packages/extension-base/src/packageInfo.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 @reef-defi/extension-base authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Auto-generated by @polkadot/dev, do not edit 5 | 6 | export const packageInfo = { name: '@reef-defi/extension-base', version: '1.0.22' }; 7 | -------------------------------------------------------------------------------- /packages/extension-base/src/page/Accounts.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { InjectedAccount, InjectedAccounts, Unsubcall } from '@reef-defi/extension-inject/types'; 5 | import type { SendRequest } from './types'; 6 | 7 | // External to class, this.# is not private enough (yet) 8 | let sendRequest: SendRequest; 9 | 10 | export default class Accounts implements InjectedAccounts { 11 | constructor (_sendRequest: SendRequest) { 12 | sendRequest = _sendRequest; 13 | } 14 | 15 | public get (anyType?: boolean): Promise { 16 | return sendRequest('pub(accounts.list)', { anyType }); 17 | } 18 | 19 | public subscribe (cb: (accounts: InjectedAccount[]) => unknown): Unsubcall { 20 | let unsubs = false; 21 | 22 | sendRequest('pub(accounts.subscribe)', null, (val) => { 23 | if (!unsubs) { 24 | cb(val); 25 | } 26 | }) 27 | .catch((error: Error) => console.error(error)); 28 | 29 | return (): void => { 30 | unsubs = true; 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/extension-base/src/page/Injected.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ReefInjected } from '@reef-defi/extension-inject/types'; 5 | import type { SendRequest } from './types'; 6 | 7 | import Accounts from '@reef-defi/extension-base/page/Accounts'; 8 | 9 | import { ReefProvider } from '../../../reef/extension-base/src/page/ReefProvider'; 10 | import { ReefSigner } from '../../../reef/extension-base/src/page/ReefSigner'; 11 | import Metadata from './Metadata'; 12 | import PostMessageProvider from './PostMessageProvider'; 13 | import SigningKey from './Signer'; 14 | 15 | export default class implements ReefInjected { 16 | public readonly accounts: Accounts; 17 | 18 | public readonly metadata: Metadata; 19 | 20 | public readonly provider: PostMessageProvider; 21 | 22 | public readonly signer: SigningKey; 23 | 24 | public readonly reefSigner: ReefSigner; 25 | 26 | public readonly reefProvider: ReefProvider; 27 | 28 | constructor (sendRequest: SendRequest) { 29 | this.accounts = new Accounts(sendRequest); 30 | this.metadata = new Metadata(sendRequest); 31 | this.provider = new PostMessageProvider(sendRequest); 32 | this.signer = new SigningKey(sendRequest); 33 | // REEF update 34 | this.reefProvider = new ReefProvider(sendRequest); 35 | this.reefSigner = new ReefSigner(this.accounts, this.signer, this.reefProvider); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/extension-base/src/page/Metadata.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { InjectedMetadata, InjectedMetadataKnown, MetadataDef } from '@reef-defi/extension-inject/types'; 5 | import type { SendRequest } from './types'; 6 | 7 | // External to class, this.# is not private enough (yet) 8 | let sendRequest: SendRequest; 9 | 10 | export default class Metadata implements InjectedMetadata { 11 | constructor (_sendRequest: SendRequest) { 12 | sendRequest = _sendRequest; 13 | } 14 | 15 | public get (): Promise { 16 | return sendRequest('pub(metadata.list)'); 17 | } 18 | 19 | public provide (definition: MetadataDef): Promise { 20 | return sendRequest('pub(metadata.provide)', definition); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/extension-base/src/page/Signer.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-base authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { Signer as SignerInterface, SignerResult } from '@polkadot/api/types'; 5 | import type { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; 6 | import type { SendRequest } from './types'; 7 | 8 | // External to class, this.# is not private enough (yet) 9 | let sendRequest: SendRequest; 10 | let nextId = 0; 11 | 12 | export default class Signer implements SignerInterface { 13 | constructor (_sendRequest: SendRequest) { 14 | sendRequest = _sendRequest; 15 | } 16 | 17 | public async signPayload (payload: SignerPayloadJSON): Promise { 18 | const id = ++nextId; 19 | let result; 20 | 21 | try { 22 | result = await sendRequest('pub(extrinsic.sign)', payload); 23 | 24 | // we add an internal id (number) - should have a mapping from the 25 | // extension id (string) -> internal id (number) if we wish to provide 26 | // updated via the update functionality (noop at this point) 27 | return { 28 | ...result, 29 | id 30 | }; 31 | } catch (e) { 32 | return Promise.reject(new Error('_canceled')); 33 | } 34 | } 35 | 36 | public async signRaw (payload: SignerPayloadRaw): Promise { 37 | const id = ++nextId; 38 | const result = await sendRequest('pub(bytes.sign)', payload); 39 | 40 | try { 41 | return { 42 | ...result, 43 | id 44 | }; 45 | } catch (e) { 46 | return Promise.reject(new Error('_canceled')); 47 | } 48 | } 49 | 50 | // NOTE We don't listen to updates at all, if we do we can interpret the 51 | // resuklt as provided by the API here 52 | // public update (id: number, status: Hash | SubmittableResult): void { 53 | // // ignore 54 | // } 55 | } 56 | -------------------------------------------------------------------------------- /packages/extension-base/src/page/types.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-base authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { MessageTypesWithNoSubscriptions, MessageTypesWithNullRequest, MessageTypesWithSubscriptions, RequestTypes, ResponseTypes, SubscriptionMessageTypes } from '../background/types'; 5 | 6 | export interface SendRequest { 7 | (message: TMessageType): Promise; 8 | (message: TMessageType, request: RequestTypes[TMessageType]): Promise; 9 | (message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise; 10 | } 11 | -------------------------------------------------------------------------------- /packages/extension-base/src/stores/Accounts.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-base authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { KeyringJson, KeyringStore } from '@polkadot/ui-keyring/types'; 5 | 6 | import BaseStore from './Base'; 7 | 8 | export default class AccountsStore extends BaseStore implements KeyringStore { 9 | constructor () { 10 | super(null); 11 | } 12 | 13 | public override set (key: string, value: KeyringJson, update?: () => void): void { 14 | // shortcut, don't save testing accounts in extension storage 15 | if (key.startsWith('account:') && value.meta && value.meta.isTesting) { 16 | update && update(); 17 | 18 | return; 19 | } 20 | 21 | super.set(key, value, update); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/extension-base/src/stores/Base.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-base authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import chrome from '@reef-defi/extension-inject/chrome'; 5 | 6 | type StoreValue = Record; 7 | 8 | const lastError = (type: string): void => { 9 | const error = chrome.runtime.lastError; 10 | 11 | if (error) { 12 | console.error(`BaseStore.${type}:: runtime.lastError:`, error); 13 | } 14 | }; 15 | 16 | export default abstract class BaseStore { 17 | #prefix: string; 18 | 19 | constructor (prefix: string | null) { 20 | this.#prefix = prefix ? `${prefix}:` : ''; 21 | } 22 | 23 | public all (update: (key: string, value: T) => void): void { 24 | chrome.storage.local.get(null, (result: StoreValue): void => { 25 | lastError('all'); 26 | 27 | Object 28 | .entries(result) 29 | .filter(([key]) => key.startsWith(this.#prefix)) 30 | .forEach(([key, value]): void => { 31 | update(key.replace(this.#prefix, ''), value as T); 32 | }); 33 | }); 34 | } 35 | 36 | public get (_key: string, update: (value: T) => void): void { 37 | const key = `${this.#prefix}${_key}`; 38 | 39 | chrome.storage.local.get([key], (result: StoreValue): void => { 40 | lastError('get'); 41 | 42 | update(result[key] as T); 43 | }); 44 | } 45 | 46 | public remove (_key: string, update?: () => void): void { 47 | const key = `${this.#prefix}${_key}`; 48 | 49 | chrome.storage.local.remove(key, (): void => { 50 | lastError('remove'); 51 | 52 | update && update(); 53 | }); 54 | } 55 | 56 | public set (_key: string, value: T, update?: () => void): void { 57 | const key = `${this.#prefix}${_key}`; 58 | 59 | chrome.storage.local.set({ [key]: value }, (): void => { 60 | lastError('set'); 61 | 62 | update && update(); 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/extension-base/src/stores/Metadata.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-base authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { MetadataDef } from '@reef-defi/extension-inject/types'; 5 | 6 | import BaseStore from './Base'; 7 | 8 | export default class MetadataStore extends BaseStore { 9 | constructor () { 10 | super('metadata'); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/extension-base/src/stores/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-base authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { default as AccountsStore } from './Accounts'; 5 | export { default as MetadataStore } from './Metadata'; 6 | -------------------------------------------------------------------------------- /packages/extension-base/src/types.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export interface Message extends MessageEvent { 5 | data: { 6 | error?: string; 7 | id: string; 8 | origin: string; 9 | response?: string; 10 | subscription?: string; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/extension-base/src/utils/canDerive.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { KeypairType } from '@reef-defi/util-crypto/types'; 5 | 6 | export function canDerive (type?: KeypairType): boolean { 7 | return !!type && ['ed25519', 'sr25519', 'ecdsa', 'ethereum'].includes(type); 8 | } 9 | -------------------------------------------------------------------------------- /packages/extension-base/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export { canDerive } from './canDerive'; 5 | -------------------------------------------------------------------------------- /packages/extension-chains/README.md: -------------------------------------------------------------------------------- 1 | # @reef-defi/extension-chains 2 | 3 | Definitions for chains that are supported by this extension. It contains the bare definitions as well as a stripped-down (call-only) metadata format. 4 | -------------------------------------------------------------------------------- /packages/extension-chains/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Reef Chain", 3 | "bugs": "https://github.com/reef-defi/browser-extension/issues", 4 | "contributors": [], 5 | "description": "Definitions for all known chains as exposed by the extension.", 6 | "homepage": "https://github.com/reef-defi/browser-extension/tree/master/packages/extension-chains#readme", 7 | "license": "Apache-2", 8 | "maintainers": [], 9 | "name": "@reef-defi/extension-chains", 10 | "repository": { 11 | "directory": "packages/extension-chains", 12 | "type": "git", 13 | "url": "https://github.com/reef-defi/browser-extension.git" 14 | }, 15 | "sideEffects": false, 16 | "type": "module", 17 | "version": "1.0.23", 18 | "main": "index.js", 19 | "dependencies": { 20 | "@babel/runtime": "^7.16.0", 21 | "@reef-defi/networks": "^7.8.2" 22 | }, 23 | "peerDependencies": { 24 | "@polkadot/api": "*" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/extension-chains/src/packageInfo.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 @reef-defi/extension-chains authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Auto-generated by @polkadot/dev, do not edit 5 | 6 | export const packageInfo = { name: '@reef-defi/extension-chains', version: '1.0.22' }; 7 | -------------------------------------------------------------------------------- /packages/extension-chains/src/types.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-chains authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { MetadataDef } from '@reef-defi/extension-inject/types'; 5 | import type { Registry } from '@polkadot/types/types'; 6 | 7 | export interface Chain { 8 | definition: MetadataDef; 9 | genesisHash?: string; 10 | hasMetadata: boolean; 11 | icon: string; 12 | isUnknown?: boolean; 13 | name: string; 14 | registry: Registry; 15 | specVersion: number; 16 | ss58Format: number; 17 | tokenDecimals: number; 18 | tokenSymbol: string; 19 | } 20 | -------------------------------------------------------------------------------- /packages/extension-compat-metamask/README.md: -------------------------------------------------------------------------------- 1 | # @reef-defi/extension-metamask-compat 2 | 3 | An optional metamask-compatible layer 4 | -------------------------------------------------------------------------------- /packages/extension-compat-metamask/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Reef Chain", 3 | "bugs": "https://github.com/reef-defi/browser-extension/issues", 4 | "contributors": [], 5 | "description": "Metamask compatibility layer", 6 | "homepage": "https://github.com/reef-defi/browser-extension/tree/master/packages/extension-compat-metamask#readme", 7 | "license": "Apache-2", 8 | "maintainers": [], 9 | "name": "@reef-defi/extension-compat-metamask", 10 | "repository": { 11 | "directory": "packages/extension-compat-metamask", 12 | "type": "git", 13 | "url": "https://github.com/reef-defi/browser-extension.git" 14 | }, 15 | "sideEffects": false, 16 | "type": "module", 17 | "version": "1.0.23", 18 | "main": "index.js", 19 | "dependencies": { 20 | "@babel/runtime": "^7.16.0", 21 | "@metamask/detect-provider": "^1.2.0", 22 | "@reef-defi/extension-inject": "^1.0.23", 23 | "@reef-defi/util": "^7.8.2", 24 | "web3": "^1.6.0" 25 | }, 26 | "peerDependencies": { 27 | "@reef-defi/util": "*" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/extension-compat-metamask/src/packageInfo.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 @reef-defi/extension-compat-metamask authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Auto-generated by @polkadot/dev, do not edit 5 | 6 | export const packageInfo = { name: '@reef-defi/extension-compat-metamask', version: '1.0.22' }; 7 | -------------------------------------------------------------------------------- /packages/extension-dapp/README.md: -------------------------------------------------------------------------------- 1 | # @reef-defi/extension-dapp 2 | 3 | Documentation available [in the polkadot-js doc](https://polkadot.js.org/docs/extension). 4 | -------------------------------------------------------------------------------- /packages/extension-dapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Reef Chain", 3 | "bugs": "https://github.com/reef-defi/browser-extension/issues", 4 | "contributors": [], 5 | "description": "Provides an interfaces around the injected globals for ease of access by dapp developers.", 6 | "homepage": "https://github.com/reef-defi/browser-extension/tree/master/packages/extension-dapp#readme", 7 | "license": "Apache-2", 8 | "maintainers": [], 9 | "name": "@reef-defi/extension-dapp", 10 | "repository": { 11 | "directory": "packages/extension-dapp", 12 | "type": "git", 13 | "url": "https://github.com/reef-defi/browser-extension.git" 14 | }, 15 | "sideEffects": false, 16 | "type": "module", 17 | "version": "1.0.23", 18 | "main": "index.js", 19 | "dependencies": { 20 | "@babel/runtime": "^7.16.0", 21 | "@reef-defi/extension-inject": "^1.0.23", 22 | "@reef-defi/util": "^7.8.2", 23 | "@reef-defi/util-crypto": "^7.8.2" 24 | }, 25 | "peerDependencies": { 26 | "@polkadot/api": "*", 27 | "@reef-defi/util": "*", 28 | "@reef-defi/util-crypto": "*" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/extension-dapp/src/packageInfo.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 @reef-defi/extension-dapp authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Auto-generated by @polkadot/dev, do not edit 5 | 6 | export const packageInfo = { name: '@reef-defi/extension-dapp', version: '1.0.22' }; 7 | -------------------------------------------------------------------------------- /packages/extension-dapp/src/util.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-dapp authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | export function documentReadyPromise (creator: () => Promise): Promise { 5 | return new Promise((resolve): void => { 6 | if (document.readyState === 'complete') { 7 | resolve(creator()); 8 | } else { 9 | window.addEventListener('load', () => resolve(creator())); 10 | } 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /packages/extension-dapp/src/wrapBytes.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { U8A_WRAP_ETHEREUM, U8A_WRAP_POSTFIX, U8A_WRAP_PREFIX, u8aIsWrapped, u8aUnwrapBytes, u8aWrapBytes } from '@reef-defi/util'; 5 | 6 | export const ETHEREUM = U8A_WRAP_ETHEREUM; 7 | export const POSTFIX = U8A_WRAP_POSTFIX; 8 | export const PREFIX = U8A_WRAP_PREFIX; 9 | 10 | export const isWrapped = u8aIsWrapped; 11 | export const unwrapBytes = u8aUnwrapBytes; 12 | export const wrapBytes = u8aWrapBytes; 13 | -------------------------------------------------------------------------------- /packages/extension-inject/README.md: -------------------------------------------------------------------------------- 1 | # @reef-defi/extension-inject 2 | 3 | This is a basic extension injector that manages access to the global objects available. As an extension developer, you don't need to manage access to the window object manually, by just calling enable here, the global object is setup and managed properly. From here any dapp can access it with the `@reef-defi/extension-dapp` package; 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { injectExtension } from '@reef-defi/extension-inject'; 9 | 10 | // this is the function that will be exposed to be callable by the dapp. It resolves a promise 11 | // with the injected interface, (see `Injected`) when the dapp at `originName` (url) is allowed 12 | // to access functionality 13 | function enableFn (originName: string): Promise { 14 | ... 15 | } 16 | 17 | // injects the extension into the page 18 | injectExtension(enableFn, { name: 'myExtension', version: '1.0.1' }); 19 | ``` 20 | -------------------------------------------------------------------------------- /packages/extension-inject/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Reef Chain", 3 | "bugs": "https://github.com/reef-defi/browser-extension/issues", 4 | "contributors": [], 5 | "description": "A generic injector (usable to any extension), that populates the base exposed interfaces to be used by dapps.", 6 | "homepage": "https://github.com/reef-defi/browser-extension/tree/master/packages/extension-inject#readme", 7 | "license": "Apache-2", 8 | "maintainers": [], 9 | "name": "@reef-defi/extension-inject", 10 | "repository": { 11 | "directory": "packages/extension-inject", 12 | "type": "git", 13 | "url": "https://github.com/reef-defi/browser-extension.git" 14 | }, 15 | "sideEffects": false, 16 | "type": "module", 17 | "version": "1.0.23", 18 | "main": "index.js", 19 | "dependencies": { 20 | "@babel/runtime": "^7.16.0" 21 | }, 22 | "devDependencies": { 23 | "@types/chrome": "^0.0.161", 24 | "@types/firefox-webext-browser": "^94.0.0" 25 | }, 26 | "peerDependencies": { 27 | "@polkadot/api": "*" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/extension-inject/src/chrome.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-inject authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | const extension = typeof chrome !== 'undefined' 5 | ? chrome 6 | : typeof browser !== 'undefined' 7 | ? browser 8 | : null; 9 | 10 | export default extension as typeof chrome; 11 | -------------------------------------------------------------------------------- /packages/extension-inject/src/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-inject authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { Injected, InjectedWindow, InjectOptions } from './types'; 5 | 6 | export const REEF_EXTENSION_IDENT = 'reef'; 7 | export const REEF_INJECTED_EVENT = 'reef-injected'; 8 | 9 | // It is recommended to always use the function below to shield the extension and dapp from 10 | // any future changes. The exposed interface will manage access between the 2 environments, 11 | // be it via window (current), postMessage (under consideration) or any other mechanism 12 | export function injectExtension (enable: (origin: string) => Promise, { name, version }: InjectOptions): void { 13 | // small helper with the typescript types, just cast window 14 | const windowInject = window as Window & InjectedWindow; 15 | 16 | if (windowInject) { 17 | // don't clobber the existing object, we will add it (or create as needed) 18 | windowInject.injectedWeb3 = windowInject.injectedWeb3 || {}; 19 | 20 | // add our enable function 21 | windowInject.injectedWeb3[name] = { 22 | enable: (origin: string): Promise => 23 | enable(origin), 24 | version 25 | }; 26 | } 27 | } 28 | 29 | export function isInjected (name: string): boolean { 30 | const windowInject = window as Window & InjectedWindow; 31 | 32 | return !!windowInject?.injectedWeb3 && !!windowInject?.injectedWeb3[name]; 33 | } 34 | 35 | export function isInjectionStarted (name: string): boolean { 36 | const windowInject = window as any; 37 | 38 | return !!windowInject._reefInjectionStart && !!windowInject._reefInjectionStart[name]; 39 | } 40 | 41 | export function startInjection (name: string) { 42 | if (!(window as any)._reefInjectionStart) { 43 | (window as any)._reefInjectionStart = {}; 44 | } 45 | 46 | (window as any)._reefInjectionStart[name] = true; 47 | } 48 | -------------------------------------------------------------------------------- /packages/extension-inject/src/packageInfo.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 @reef-defi/extension-inject authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Auto-generated by @polkadot/dev, do not edit 5 | 6 | export const packageInfo = { name: '@reef-defi/extension-inject', version: '1.0.22' }; 7 | -------------------------------------------------------------------------------- /packages/extension-ui/.skip-build: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reef-defi/browser-extension/3514bd2f1c4868de07f4a50b9365d0f1083c127a/packages/extension-ui/.skip-build -------------------------------------------------------------------------------- /packages/extension-ui/.skip-npm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reef-defi/browser-extension/3514bd2f1c4868de07f4a50b9365d0f1083c127a/packages/extension-ui/.skip-npm -------------------------------------------------------------------------------- /packages/extension-ui/README.md: -------------------------------------------------------------------------------- 1 | # @reef-defi/extension-ui 2 | 3 | UI for the `@reef-defi/extension` 4 | -------------------------------------------------------------------------------- /packages/extension-ui/src/MetadataCache.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { MetadataDef } from '@reef-defi/extension-inject/types'; 5 | 6 | const metadataGets = new Map>(); 7 | 8 | export function getSavedMeta (genesisHash: string): Promise | undefined { 9 | return metadataGets.get(genesisHash); 10 | } 11 | 12 | export function setSavedMeta (genesisHash: string, def: Promise): Map> { 13 | return metadataGets.set(genesisHash, def); 14 | } 15 | -------------------------------------------------------------------------------- /packages/extension-ui/src/Popup/Accounts/AccountsTree.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { AccountWithChildren } from '@reef-defi/extension-base/background/types'; 5 | 6 | import React from 'react'; 7 | 8 | import Account from './Account'; 9 | 10 | interface Props extends AccountWithChildren { 11 | parentName?: string; 12 | } 13 | 14 | export default function AccountsTree ({ parentName, suri, ...account }: Props): React.ReactElement { 15 | return ( 16 | <> 17 | {!account.isSelected && } 22 | {account?.children?.map((child, index) => ( 23 | 28 | ))} 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/extension-ui/src/Popup/AuthManagement/WebsiteEntry.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../../types'; 5 | 6 | import { IconProp } from '@fortawesome/fontawesome-svg-core'; 7 | import { faTrash } from '@fortawesome/free-solid-svg-icons'; 8 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 9 | import { AuthUrlInfo } from '@reef-defi/extension-base/background/handlers/State'; 10 | import { Switch } from '@reef-defi/extension-ui/components'; 11 | import React, { useCallback } from 'react'; 12 | import styled from 'styled-components'; 13 | 14 | import useTranslation from '../../hooks/useTranslation'; 15 | 16 | interface Props extends ThemeProps { 17 | className?: string; 18 | info: AuthUrlInfo; 19 | toggleAuth: (url: string) => void 20 | removeAuth: (url: string) => void 21 | url: string; 22 | } 23 | 24 | function WebsiteEntry ({ className = '', info, removeAuth, toggleAuth, url }: Props): React.ReactElement { 25 | const { t } = useTranslation(); 26 | 27 | const switchAccess = useCallback(() => { 28 | toggleAuth(url); 29 | }, [toggleAuth, url]); 30 | 31 | const removeAccess = useCallback(() => { 32 | removeAuth(url); 33 | }, [removeAuth, url]); 34 | 35 | return ( 36 |
37 |
38 | {url} 39 |
40 | ('allowed')} 43 | className='info' 44 | onChange={switchAccess} 45 | uncheckedLabel={t('denied')} 46 | /> 47 | 48 | 52 |
53 | ); 54 | } 55 | 56 | export default styled(WebsiteEntry)(({ theme }: Props) => ` 57 | display: flex; 58 | align-items: center; 59 | 60 | .url{ 61 | flex: 1; 62 | } 63 | 64 | &.denied { 65 | .slider::before { 66 | background-color: ${theme.backButtonBackground}; 67 | } 68 | } 69 | 70 | .remove-access { 71 | margin-left: 12px; 72 | cursor: pointer; 73 | color: ${theme.primaryColor} 74 | } 75 | `); 76 | -------------------------------------------------------------------------------- /packages/extension-ui/src/Popup/Authorize/Authorize.test.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import '../../../../../__mocks__/chrome'; 5 | 6 | import type { AuthorizeRequest } from '@reef-defi/extension-base/background/types'; 7 | 8 | import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; 9 | import { configure, mount, ReactWrapper } from 'enzyme'; 10 | import React from 'react'; 11 | import { ThemeProvider } from 'styled-components'; 12 | 13 | import { AuthorizeReqContext, themes } from '../../components'; 14 | import { Header } from '../../partials'; 15 | import Request from './Request'; 16 | import Authorize from '.'; 17 | 18 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call 19 | configure({ adapter: new Adapter() }); 20 | 21 | describe('Authorize', () => { 22 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 23 | const mountAuthorize = (authorizeRequests: AuthorizeRequest[] = []): ReactWrapper => mount( 24 | 25 | 26 | 27 | 28 | ); 29 | 30 | it('render component', () => { 31 | const wrapper = mountAuthorize(); 32 | 33 | expect(wrapper.find(Header).text()).toBe('Authorize'); 34 | expect(wrapper.find(Request).length).toBe(0); 35 | }); 36 | 37 | it('render requests', () => { 38 | const wrapper = mountAuthorize([{ id: '1', request: { origin: '???' }, url: 'http://polkadot.org' }]); 39 | 40 | expect(wrapper.find(Request).length).toBe(1); 41 | expect(wrapper.find(Request).find('.tab-info').text()).toBe('An application, self-identifying as ??? is requesting access from http://polkadot.org.'); 42 | }); 43 | 44 | it('render more request but just one accept button', () => { 45 | const wrapper = mountAuthorize([ 46 | { id: '1', request: { origin: '???' }, url: 'http://polkadot.org' }, 47 | { id: '2', request: { origin: 'abc' }, url: 'http://polkadot.pl' } 48 | ]); 49 | 50 | expect(wrapper.find(Request).length).toBe(2); 51 | expect(wrapper.find(Request).at(1).find('.tab-info').text()).toBe('An application, self-identifying as abc is requesting access from http://polkadot.pl.'); 52 | expect(wrapper.find('button.uik-button--fullWidth').length).toBe(2); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /packages/extension-ui/src/Popup/CreateAccount/Mnemonic.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { faArrowRight } from '@fortawesome/free-solid-svg-icons'; 5 | import React, { useCallback, useState } from 'react'; 6 | 7 | import { Button } from '../../../../reef/extension-ui/uik/Button'; 8 | import { ButtonArea, Checkbox, MnemonicSeed, VerticalSpace, Warning } from '../../components'; 9 | import useToast from '../../hooks/useToast'; 10 | import useTranslation from '../../hooks/useTranslation'; 11 | 12 | interface Props { 13 | onNextStep: () => void; 14 | seed: string; 15 | } 16 | 17 | const onCopy = (): void => { 18 | const mnemonicSeedTextElement = document.querySelector('textarea'); 19 | 20 | if (!mnemonicSeedTextElement) { 21 | return; 22 | } 23 | 24 | mnemonicSeedTextElement.select(); 25 | document.execCommand('copy'); 26 | }; 27 | 28 | function Mnemonic ({ onNextStep, seed }: Props): React.ReactElement { 29 | const { t } = useTranslation(); 30 | const [isMnemonicSaved, setIsMnemonicSaved] = useState(false); 31 | const { show } = useToast(); 32 | 33 | const _onCopy = useCallback((): void => { 34 | onCopy(); 35 | show(t('Copied')); 36 | }, [show, t]); 37 | 38 | return ( 39 | <> 40 | 44 | 45 | {t("Please write down your wallet's mnemonic seed and keep it in a safe place. The mnemonic can be used to restore your wallet. Keep it carefully to not lose your assets.")} 46 | 47 | 48 | ('I have saved my mnemonic seed safely.')} 51 | onChange={setIsMnemonicSaved} 52 | /> 53 | 54 | 65 | 66 | 67 | ); 68 | } 69 | 70 | export default React.memo(Mnemonic); 71 | -------------------------------------------------------------------------------- /packages/extension-ui/src/Popup/ImportQr.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React, { useCallback, useContext, useState } from 'react'; 5 | 6 | import { QrScanAddress } from '@polkadot/react-qr'; 7 | 8 | import { ActionContext, Address, ButtonArea, NextStepButton, VerticalSpace } from '../components'; 9 | import useTranslation from '../hooks/useTranslation'; 10 | import { createAccountExternal } from '../messaging'; 11 | import { Header, Name } from '../partials'; 12 | 13 | interface QrAccount { 14 | content: string; 15 | genesisHash: string; 16 | name?: string; 17 | } 18 | 19 | export default function ImportQr (): React.ReactElement { 20 | const { t } = useTranslation(); 21 | const onAction = useContext(ActionContext); 22 | const [account, setAccount] = useState(null); 23 | const [name, setName] = useState(null); 24 | 25 | const _setAccount = useCallback( 26 | (qrAccount: QrAccount) => { 27 | setAccount(qrAccount); 28 | setName(qrAccount?.name || null); 29 | }, 30 | [] 31 | ); 32 | 33 | const _onCreate = useCallback( 34 | (): void => { 35 | if (account && name) { 36 | createAccountExternal(name, account.content, account.genesisHash) 37 | .then(() => onAction('/')) 38 | .catch((error: Error) => console.error(error)); 39 | } 40 | }, 41 | [account, name, onAction] 42 | ); 43 | 44 | return ( 45 | <> 46 |
('Scan Address Qr')} 49 | /> 50 | {!account && ( 51 |
52 | 53 |
54 | )} 55 | {account && ( 56 | <> 57 |
58 |
64 |
65 | 70 | 71 | 72 | 76 | {t('Add the account with identified address')} 77 | 78 | 79 | 80 | )} 81 | 82 | ); 83 | } 84 | -------------------------------------------------------------------------------- /packages/extension-ui/src/Popup/Metadata/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React, { useContext } from 'react'; 5 | 6 | import { Loading, MetadataReqContext } from '../../components'; 7 | import useTranslation from '../../hooks/useTranslation'; 8 | import { Header } from '../../partials'; 9 | import Request from './Request'; 10 | 11 | export default function Metadata (): React.ReactElement { 12 | const { t } = useTranslation(); 13 | const requests = useContext(MetadataReqContext); 14 | 15 | return ( 16 | <> 17 |
('Metadata')} /> 20 | {requests[0] 21 | ? ( 22 | 28 | ) 29 | : 30 | } 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /packages/extension-ui/src/Popup/PhishingDetected.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React from 'react'; 7 | import { Trans } from 'react-i18next'; 8 | import { useParams } from 'react-router'; 9 | import styled from 'styled-components'; 10 | 11 | import useTranslation from '../hooks/useTranslation'; 12 | import { Header } from '../partials'; 13 | 14 | interface Props extends ThemeProps { 15 | className?: string; 16 | } 17 | 18 | interface WebsiteState { 19 | website: string; 20 | } 21 | 22 | function PhishingDetected ({ className }: Props): React.ReactElement { 23 | const { t } = useTranslation(); 24 | const { website } = useParams(); 25 | const decodedWebsite = decodeURIComponent(website); 26 | 27 | return ( 28 | <> 29 |
('Phishing detected')} /> 30 |
31 |

32 | {t('You have been redirected because the Polkadot{.js} extension believes that this website could compromise the security of your accounts and your tokens.')} 33 |

34 |

35 | {decodedWebsite} 36 |

37 |

38 | 39 | Note that this website was reported on a community-driven, curated list. It might be incomplete or inaccurate. If you think that this website was flagged incorrectly, please open an issue by clicking here. 40 | 41 |

42 |
43 | 44 | ); 45 | } 46 | 47 | export default styled(PhishingDetected)(({ theme }: Props) => ` 48 | p { 49 | color: ${theme.subTextColor}; 50 | margin-bottom: 1rem; 51 | margin-top: 0; 52 | 53 | a { 54 | color: ${theme.subTextColor}; 55 | } 56 | 57 | &.websiteAddress { 58 | font-size: 1.3rem; 59 | text-align: center; 60 | } 61 | } 62 | `); 63 | -------------------------------------------------------------------------------- /packages/extension-ui/src/Popup/Signing/Bytes.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { isAscii, u8aToString, u8aUnwrapBytes } from '@reef-defi/util'; 5 | import React, { useMemo } from 'react'; 6 | import styled from 'styled-components'; 7 | 8 | import useTranslation from '../../hooks/useTranslation'; 9 | 10 | interface Props { 11 | className?: string; 12 | bytes: string; 13 | url: string; 14 | } 15 | 16 | function Bytes ({ bytes, className, url }: Props): React.ReactElement { 17 | const { t } = useTranslation(); 18 | 19 | const text = useMemo( 20 | () => isAscii(bytes) 21 | ? u8aToString(u8aUnwrapBytes(bytes)) 22 | : bytes, 23 | [bytes] 24 | ); 25 | 26 | return ( 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
{t('from')}{url}
{t('bytes')}{text}
39 | ); 40 | } 41 | 42 | export default styled(Bytes)` 43 | border: 0; 44 | display: block; 45 | font-size: 0.75rem; 46 | margin-top: 0.75rem; 47 | 48 | td.data { 49 | max-width: 0; 50 | overflow: hidden; 51 | text-align: left; 52 | text-overflow: ellipsis; 53 | vertical-align: middle; 54 | width: 100%; 55 | 56 | pre { 57 | font-family: inherit; 58 | font-size: 0.75rem; 59 | margin: 0; 60 | } 61 | } 62 | 63 | td.label { 64 | opacity: 0.5; 65 | padding: 0 0.5rem; 66 | text-align: right; 67 | vertical-align: middle; 68 | white-space: nowrap; 69 | } 70 | `; 71 | -------------------------------------------------------------------------------- /packages/extension-ui/src/Popup/Signing/Unlock.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React, { useCallback } from 'react'; 5 | 6 | import { InputWithLabel, Warning } from '../../components'; 7 | import useTranslation from '../../hooks/useTranslation'; 8 | 9 | interface Props { 10 | className?: string; 11 | error?: string | null; 12 | isBusy: boolean; 13 | onSign: () => Promise; 14 | password: string; 15 | setError: (error: string | null) => void; 16 | setPassword: (password: string) => void; 17 | } 18 | 19 | function Unlock ({ className, error, isBusy, onSign, password, setError, setPassword }: Props): React.ReactElement { 20 | const { t } = useTranslation(); 21 | 22 | const _onChangePassword = useCallback( 23 | (password: string): void => { 24 | setPassword(password); 25 | setError(null); 26 | }, 27 | [setError, setPassword] 28 | ); 29 | 30 | return ( 31 |
32 | ('Password for this account')} 37 | onChange={_onChangePassword} 38 | onEnter={onSign} 39 | type='password' 40 | value={password} 41 | withoutMargin={true} 42 | /> 43 | {error && ( 44 | 48 | {error} 49 | 50 | )} 51 |
52 | ); 53 | } 54 | 55 | export default React.memo(Unlock); 56 | -------------------------------------------------------------------------------- /packages/extension-ui/src/Popup/Signing/index.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { SignerPayloadJSON } from '@polkadot/types/types'; 5 | 6 | import React, { useCallback, useContext, useEffect, useState } from 'react'; 7 | 8 | import { Loading, SigningReqContext } from '../../components'; 9 | import useTranslation from '../../hooks/useTranslation'; 10 | import { Header } from '../../partials'; 11 | import Request from './Request'; 12 | import TransactionIndex from './TransactionIndex'; 13 | 14 | export default function Signing (): React.ReactElement { 15 | const { t } = useTranslation(); 16 | const requests = useContext(SigningReqContext); 17 | const [requestIndex, setRequestIndex] = useState(0); 18 | 19 | const _onNextClick = useCallback( 20 | () => setRequestIndex((requestIndex) => requestIndex + 1), 21 | [] 22 | ); 23 | 24 | const _onPreviousClick = useCallback( 25 | () => setRequestIndex((requestIndex) => requestIndex - 1), 26 | [] 27 | ); 28 | 29 | useEffect(() => { 30 | setRequestIndex( 31 | (requestIndex) => requestIndex < requests.length 32 | ? requestIndex 33 | : requests.length - 1 34 | ); 35 | }, [requests]); 36 | 37 | // protect against removal overflows/underflows 38 | const request = requests.length !== 0 39 | ? requestIndex >= 0 40 | ? requestIndex < requests.length 41 | ? requests[requestIndex] 42 | : requests[requests.length - 1] 43 | : requests[0] 44 | : null; 45 | const isTransaction = !!((request?.request?.payload as SignerPayloadJSON)?.blockNumber); 46 | 47 | return request 48 | ? ( 49 | <> 50 |
('Transaction') : t('Sign message')}> 53 | {requests.length > 1 && ( 54 | 60 | )} 61 |
62 | 70 | 71 | ) 72 | : ; 73 | } 74 | -------------------------------------------------------------------------------- /packages/extension-ui/src/Popup/Welcome.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React, { useCallback, useContext } from 'react'; 7 | import styled from 'styled-components'; 8 | 9 | import { Button } from '../../../reef/extension-ui/uik/Button'; 10 | import { ActionContext, ButtonArea, VerticalSpace, Warning } from '../components'; 11 | import useTranslation from '../hooks/useTranslation'; 12 | import { Header } from '../partials'; 13 | 14 | interface Props extends ThemeProps { 15 | className?: string; 16 | } 17 | 18 | const Welcome = function ({ className }: Props): React.ReactElement { 19 | const { t } = useTranslation(); 20 | const onAction = useContext(ActionContext); 21 | 22 | const _onClick = useCallback( 23 | (): void => { 24 | window.localStorage.setItem('welcome_read', 'ok'); 25 | onAction(); 26 | }, 27 | [onAction] 28 | ); 29 | 30 | return ( 31 | <> 32 |
('Welcome')} 35 | /> 36 |
37 |

{t('Before we start, just a couple of notes regarding use:')}

38 | 39 | {t('We do not send any clicks, pageviews or events to a central server')}

40 | {t('We do not use any trackers or analytics')}

41 | {t("We don't collect keys, addresses or any information - your information never leaves this machine")} 42 |
43 |

{t('... we are not in the information collection business (even anonymized).')}

44 |
45 | 46 | 47 | 55 | 56 | 57 | ); 58 | }; 59 | 60 | export default styled(Welcome)(() => ` 61 | p { 62 | margin-top: 15px; 63 | } 64 | 65 | .warningMargin { 66 | margin: 24px 24px 0 1.45rem; 67 | } 68 | `); 69 | -------------------------------------------------------------------------------- /packages/extension-ui/src/adapter.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // import { EnzymeAdapter } from 'enzyme'; 5 | 6 | // declare module '@wojtekmaj/enzyme-adapter-react-17' { 7 | // export = EnzymeAdapter; 8 | // } 9 | 10 | declare module '@wojtekmaj/enzyme-adapter-react-17'; 11 | -------------------------------------------------------------------------------- /packages/extension-ui/src/assets/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/extension-ui/src/assets/checkmark.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/extension-ui/src/assets/details.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/extension-ui/src/assets/images.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | declare module '*.svg' { 5 | const url: string; 6 | export default url; 7 | } 8 | 9 | declare module '*.png' { 10 | const url: string; 11 | export default url; 12 | } 13 | 14 | declare module '*.woff' { 15 | const url: string; 16 | export default url; 17 | } 18 | 19 | declare module '*.woff2' { 20 | const url: string; 21 | export default url; 22 | } 23 | -------------------------------------------------------------------------------- /packages/extension-ui/src/assets/spinner-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reef-defi/browser-extension/3514bd2f1c4868de07f4a50b9365d0f1083c127a/packages/extension-ui/src/assets/spinner-white.png -------------------------------------------------------------------------------- /packages/extension-ui/src/assets/spinner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reef-defi/browser-extension/3514bd2f1c4868de07f4a50b9365d0f1083c127a/packages/extension-ui/src/assets/spinner.png -------------------------------------------------------------------------------- /packages/extension-ui/src/components/AccountNamePasswordCreation.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { faArrowLeft, faArrowRight } from '@fortawesome/free-solid-svg-icons'; 5 | import React, { useCallback, useState } from 'react'; 6 | 7 | import { Button } from '../../../reef/extension-ui/uik/Button'; 8 | import { Name, Password } from '../partials'; 9 | import { ButtonArea, VerticalSpace } from '.'; 10 | 11 | interface Props { 12 | buttonLabel: string; 13 | isBusy: boolean; 14 | onBackClick: () => void; 15 | onCreate: (name: string, password: string) => void | Promise; 16 | onNameChange: (name: string) => void; 17 | } 18 | 19 | function AccountNamePasswordCreation ({ buttonLabel, isBusy, onBackClick, onCreate, onNameChange }: Props): React.ReactElement { 20 | const [name, setName] = useState(null); 21 | const [password, setPassword] = useState(null); 22 | 23 | const _onCreate = useCallback( 24 | () => name && password && onCreate(name, password), 25 | [name, password, onCreate] 26 | ); 27 | 28 | const _onNameChange = useCallback( 29 | (name: string | null) => { 30 | onNameChange(name || ''); 31 | setName(name); 32 | }, 33 | [onNameChange] 34 | ); 35 | 36 | const _onBackClick = useCallback( 37 | () => { 38 | _onNameChange(null); 39 | setPassword(null); 40 | onBackClick(); 41 | }, 42 | [_onNameChange, onBackClick] 43 | ); 44 | 45 | return ( 46 | <> 47 | 51 | 52 | 53 | 54 | 72 | 73 | 74 | ); 75 | } 76 | 77 | export default React.memo(AccountNamePasswordCreation); 78 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/ActionBar.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React from 'react'; 5 | import styled from 'styled-components'; 6 | 7 | interface Props { 8 | children: React.ReactNode; 9 | className?: string; 10 | } 11 | 12 | function ActionBar ({ children, className }: Props): React.ReactElement { 13 | return ( 14 |
15 | {children} 16 |
17 | ); 18 | } 19 | 20 | export default styled(ActionBar)` 21 | align-content: flex-end; 22 | display: flex; 23 | justify-content: space-between; 24 | padding: 0.25rem; 25 | text-align: right; 26 | 27 | a { 28 | cursor: pointer; 29 | } 30 | 31 | a+a { 32 | margin-left: 0.75rem; 33 | } 34 | `; 35 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/ActionText.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { IconDefinition } from '@fortawesome/fontawesome-svg-core'; 5 | import type { ThemeProps } from '../types'; 6 | 7 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 8 | import React, { MouseEventHandler } from 'react'; 9 | import styled from 'styled-components'; 10 | 11 | interface Props { 12 | className?: string; 13 | icon?: IconDefinition; 14 | onClick: MouseEventHandler; 15 | text: string; 16 | } 17 | 18 | function ActionText ({ className, icon, onClick, text }: Props): React.ReactElement { 19 | return ( 20 |
24 | {icon && } 25 | {text} 26 |
27 | ); 28 | } 29 | 30 | export default styled(ActionText)(({ theme }: ThemeProps) => ` 31 | cursor: pointer; 32 | 33 | span { 34 | color: ${theme.labelColor} 35 | font-size: ${theme.labelFontSize}; 36 | line-height: ${theme.labelLineHeight}; 37 | text-decoration-line: underline; 38 | } 39 | 40 | .svg-inline--fa { 41 | color: ${theme.iconNeutralColor}; 42 | display: inline-block; 43 | margin-right: 0.3rem; 44 | position: relative; 45 | top: 2px; 46 | } 47 | `); 48 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/BackButton.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import { IconProp } from '@fortawesome/fontawesome-svg-core'; 7 | import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'; 8 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 9 | import React from 'react'; 10 | import styled from 'styled-components'; 11 | 12 | import Button from './Button'; 13 | 14 | interface Props { 15 | className?: string; 16 | onClick: () => void; 17 | } 18 | 19 | function BackButton ({ className, onClick }: Props): React.ReactElement { 20 | return ( 21 | 31 | ); 32 | } 33 | 34 | export default styled(BackButton)(({ theme }: ThemeProps) => ` 35 | background: ${theme.backButtonBackground}; 36 | margin-right: 11px; 37 | width: 42px; 38 | 39 | .arrowLeft { 40 | color: ${theme.backButtonTextColor}; 41 | display: block; 42 | margin: auto; 43 | } 44 | 45 | &:not(:disabled):hover { 46 | background: ${theme.backButtonBackgroundHover}; 47 | } 48 | `); 49 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/Box.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React from 'react'; 7 | import styled from 'styled-components'; 8 | 9 | interface Props { 10 | banner?: React.ReactNode; 11 | children: React.ReactNode; 12 | className?: string; 13 | } 14 | 15 | function Box ({ banner, children, className }: Props): React.ReactElement { 16 | return ( 17 |
18 | {children} 19 | {banner &&
{banner}
} 20 |
21 | ); 22 | } 23 | 24 | export default styled(Box)(({ theme }: ThemeProps) => ` 25 | background: ${theme.readonlyInputBackground}; 26 | border: 1px solid ${theme.inputBorderColor}; 27 | border-radius: ${theme.borderRadius}; 28 | color: ${theme.subTextColor}; 29 | font-family: ${theme.fontFamily}; 30 | font-size: ${theme.fontSize}; 31 | margin: 0.75rem 24px; 32 | padding: ${theme.boxPadding}; 33 | position: relative; 34 | 35 | .banner { 36 | background: darkorange; 37 | border-radius: 0 ${theme.borderRadius} 0 ${theme.borderRadius}; 38 | color: white; 39 | font-size: 0.75rem; 40 | padding: 0.25rem 0.5rem; 41 | position: absolute; 42 | right: 0; 43 | top: 0; 44 | } 45 | `); 46 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/ButtonArea.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React from 'react'; 7 | import styled from 'styled-components'; 8 | 9 | interface Props extends ThemeProps { 10 | className?: string; 11 | children: React.ReactNode; 12 | } 13 | 14 | const ButtonArea = function ({ children, className }: Props) { 15 | return ( 16 |
17 | {children} 18 |
19 | ); 20 | }; 21 | 22 | export default styled(ButtonArea)(({ theme }: ThemeProps) => ` 23 | display: flex; 24 | flex-direction: row; 25 | background: ${theme.highlightedAreaBackground}; 26 | border-top: 1px solid ${theme.inputBorderColor}; 27 | padding: 12px 24px; 28 | margin-left: 0; 29 | margin-right: 0; 30 | 31 | & > button:not(:last-of-type) { 32 | margin-right: 8px; 33 | } 34 | `); 35 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/ButtonWithSubtitle.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React from 'react'; 5 | import styled from 'styled-components'; 6 | 7 | import { Button } from '.'; 8 | 9 | interface ButtonWithSubtitleProps { 10 | title: string; 11 | subTitle: string; 12 | children?: string; 13 | to: string; 14 | } 15 | 16 | export default function ButtonWithSubtitle ({ children, subTitle, title, to }: ButtonWithSubtitleProps): React.ReactElement { 17 | return ( 18 | 19 |

{title}

20 | {subTitle} 21 | {children} 22 |
23 | ); 24 | } 25 | 26 | const StyledButton = styled(Button)` 27 | button { 28 | padding-top: 0; 29 | padding-bottom: 0; 30 | } 31 | 32 | p { 33 | margin: 0; 34 | font-size: 15px; 35 | line-height: 20px; 36 | } 37 | 38 | span { 39 | display: block; 40 | font-size: 12px; 41 | line-height: 16px; 42 | } 43 | `; 44 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/ErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributor 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React from 'react'; 5 | import { WithTranslation } from 'react-i18next'; 6 | 7 | import Header from '../partials/Header'; 8 | import { CTA } from './../../../reef/extension-ui/uik'; 9 | import ButtonArea from './ButtonArea'; 10 | import translate from './translate'; 11 | import VerticalSpace from './VerticalSpace'; 12 | 13 | interface Props extends WithTranslation { 14 | children: React.ReactNode; 15 | className?: string; 16 | error?: Error | null; 17 | trigger?: string; 18 | } 19 | 20 | interface State { 21 | error: Error | null; 22 | } 23 | 24 | // NOTE: This is the only way to do an error boundary, via extend 25 | class ErrorBoundary extends React.Component { 26 | public override state: State = { error: null }; 27 | 28 | public static getDerivedStateFromError (error: Error): Partial { 29 | return { error }; 30 | } 31 | 32 | public override componentDidUpdate (prevProps: Props) { 33 | const { error } = this.state; 34 | const { trigger } = this.props; 35 | 36 | if (error !== null && (prevProps.trigger !== trigger)) { 37 | this.setState({ error: null }); 38 | } 39 | } 40 | 41 | public override render (): React.ReactNode { 42 | const { children, t } = this.props; 43 | const { error } = this.state; 44 | 45 | return error 46 | ? ( 47 | <> 48 |
('An error occured')} /> 49 |
50 | {t('Something went wrong with the query and rendering of this component. {{message}}', { 51 | replace: { message: error.message } 52 | })} 53 |
54 | 55 | 56 | 59 | {t('Back to home')} 60 | 61 | 62 | 63 | ) 64 | : children; 65 | } 66 | 67 | #goHome = () => { 68 | this.setState({ error: null }); 69 | window.location.hash = '/'; 70 | }; 71 | } 72 | 73 | export default translate(ErrorBoundary); 74 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/Icon.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React from 'react'; 5 | import styled from 'styled-components'; 6 | 7 | interface Props { 8 | className?: string; 9 | icon: string; 10 | onClick?: () => void | Promise; 11 | } 12 | 13 | function Icon ({ className = '', icon, onClick }: Props): React.ReactElement { 14 | return ( 15 |
19 | {icon} 20 |
21 | ); 22 | } 23 | 24 | export default styled(Icon)(({ onClick }: Props) => ` 25 | background: white; 26 | border-radius: 50%; 27 | box-sizing: border-box; 28 | cursor: ${onClick ? 'pointer' : 'inherit'}; 29 | text-align: center; 30 | `); 31 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/Identicon.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { IconTheme } from '@polkadot/react-identicon/types'; 5 | import type { ThemeProps } from '../types'; 6 | 7 | import React from 'react'; 8 | import styled from 'styled-components'; 9 | 10 | import Icon from '@polkadot/react-identicon'; 11 | 12 | interface Props { 13 | className?: string; 14 | iconTheme?: IconTheme; 15 | isExternal?: boolean | null; 16 | onCopy?: () => void; 17 | prefix?: number; 18 | value?: string | null; 19 | } 20 | 21 | function Identicon ({ className, iconTheme, onCopy, prefix, value }: Props): React.ReactElement { 22 | return ( 23 |
24 | 32 |
33 | ); 34 | } 35 | 36 | export default styled(Identicon)(({ theme }: ThemeProps) => ` 37 | background: rgba(192, 192, 292, 0.25); 38 | border-radius: 50%; 39 | display: flex; 40 | justify-content: center; 41 | 42 | .container:before { 43 | box-shadow: none; 44 | background: ${theme.identiconBackground}; 45 | } 46 | 47 | svg { 48 | circle:first-of-type { 49 | display: none; 50 | } 51 | } 52 | `); 53 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/InputFilter.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 @polkadot/react-components authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import { IconProp } from '@fortawesome/fontawesome-svg-core'; 5 | import { faTimes } from '@fortawesome/free-solid-svg-icons'; 6 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 7 | import React, { useCallback, useRef } from 'react'; 8 | import styled from 'styled-components'; 9 | 10 | import { ThemeProps } from '../types'; 11 | import { Input } from './TextInputs'; 12 | 13 | interface Props extends ThemeProps { 14 | className?: string; 15 | onChange: (filter: string) => void; 16 | placeholder: string; 17 | value: string; 18 | withReset?: boolean; 19 | } 20 | 21 | function InputFilter ({ className, onChange, placeholder, value, withReset = false }: Props) { 22 | const inputRef: React.RefObject | null = useRef(null); 23 | 24 | const onChangeFilter = useCallback((event: React.ChangeEvent) => { 25 | onChange(event.target.value); 26 | }, [onChange]); 27 | 28 | const onResetFilter = useCallback(() => { 29 | onChange(''); 30 | inputRef.current && inputRef.current.select(); 31 | }, [onChange]); 32 | 33 | return ( 34 |
35 | 46 | {withReset && !!value && ( 47 | 52 | )} 53 |
54 | ); 55 | } 56 | 57 | export default styled(InputFilter)(({ theme }: Props) => ` 58 | padding-left: 1rem !important; 59 | padding-right: 1rem !important; 60 | position: relative; 61 | 62 | .resetIcon { 63 | position: absolute; 64 | right: 28px; 65 | top: 12px; 66 | color: ${theme.iconNeutralColor}; 67 | cursor: pointer; 68 | } 69 | `); 70 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/Label.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React from 'react'; 7 | import styled from 'styled-components'; 8 | 9 | interface Props { 10 | children: React.ReactNode; 11 | className?: string; 12 | label: string; 13 | } 14 | 15 | function Label ({ children, className, label }: Props): React.ReactElement { 16 | return ( 17 |
18 | 19 | {children} 20 |
21 | ); 22 | } 23 | 24 | export default styled(Label)(({ theme }: ThemeProps) => ` 25 | color: ${theme.textColor}; 26 | 27 | label.heading-label { 28 | font-size: ${theme.inputLabelFontSize}; 29 | line-height: 14px; 30 | letter-spacing: 0.04em; 31 | opacity: 0.65; 32 | margin-bottom: 12px; 33 | text-transform: uppercase; 34 | } 35 | `); 36 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/Link.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React from 'react'; 7 | import { Link as RouterLink } from 'react-router-dom'; 8 | import styled from 'styled-components'; 9 | 10 | interface Props { 11 | children?: React.ReactNode; 12 | className?: string; 13 | isDanger?: boolean; 14 | isDisabled?: boolean; 15 | onClick?: () => void; 16 | title?: string; 17 | to?: string; 18 | } 19 | 20 | function Link ({ children, className = '', isDisabled, onClick, title, to }: Props): React.ReactElement { 21 | if (isDisabled) { 22 | return ( 23 |
27 | {children} 28 |
29 | ); 30 | } 31 | 32 | return to 33 | ? ( 34 | 40 | {children} 41 | 42 | ) 43 | : ( 44 | 50 | {children} 51 | 52 | ); 53 | } 54 | 55 | export default styled(Link)(({ isDanger, theme }: Props & ThemeProps) => ` 56 | align-items: center; 57 | color: ${isDanger ? theme.textColorDanger : theme.textColor}; 58 | display: flex; 59 | opacity: 0.85; 60 | text-decoration: none; 61 | vertical-align: middle; 62 | 63 | &:hover { 64 | color: ${isDanger ? theme.textColorDanger : theme.textColor}; 65 | opacity: 1.0; 66 | } 67 | 68 | &:visited { 69 | color: ${isDanger ? theme.textColorDanger : theme.textColor}; 70 | } 71 | 72 | &.isDisabled { 73 | opacity: 0.4; 74 | } 75 | `); 76 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/List.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React from 'react'; 7 | import styled from 'styled-components'; 8 | 9 | interface Props extends ThemeProps { 10 | className?: string; 11 | children: React.ReactNode; 12 | } 13 | 14 | const List = ({ children, className }: Props) => ( 15 |
    16 | {children} 17 |
18 | ); 19 | 20 | export default styled(List)(({ theme }: ThemeProps) => ` 21 | list-style: none; 22 | padding-inline-start: 10px; 23 | padding-inline-end: 10px; 24 | text-indent: -22px; 25 | margin-left: 21px; 26 | 27 | li { 28 | margin-bottom: 8px; 29 | } 30 | 31 | li::before { 32 | content: '\\2022'; 33 | color: ${theme.primaryColor}; 34 | font-size: 30px; 35 | font-weight: bold; 36 | margin-right: 10px; 37 | vertical-align: -20%; 38 | } 39 | `); 40 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/Loading.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React from 'react'; 5 | 6 | import useTranslation from '../hooks/useTranslation'; 7 | 8 | interface Props { 9 | children?: React.ReactNode; 10 | } 11 | 12 | export default function Loading ({ children }: Props): React.ReactElement { 13 | const { t } = useTranslation(); 14 | 15 | if (!children) { 16 | return ( 17 |
{t('... loading ...')}
18 | ); 19 | } 20 | 21 | return ( 22 | <>{children} 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/Main.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React from 'react'; 7 | import styled from 'styled-components'; 8 | 9 | interface Props { 10 | children: React.ReactNode; 11 | className?: string; 12 | } 13 | 14 | function Main ({ children, className }: Props): React.ReactElement { 15 | return ( 16 |
17 | {children} 18 |
19 | ); 20 | } 21 | 22 | export default styled(Main)(({ theme }: ThemeProps) => ` 23 | display: flex; 24 | flex-direction: column; 25 | height: calc(100vh - 2px); 26 | background: ${theme.background}; 27 | color: ${theme.textColor}; 28 | font-size: ${theme.fontSize}; 29 | line-height: ${theme.lineHeight}; 30 | border: 1px solid ${theme.inputBorderColor}; 31 | 32 | * { 33 | font-family: ${theme.fontFamily}; 34 | } 35 | 36 | > * { 37 | padding-left: 20px; 38 | padding-right: 20px; 39 | } 40 | `); 41 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/Menu.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React from 'react'; 7 | import styled from 'styled-components'; 8 | 9 | interface Props { 10 | children: React.ReactNode; 11 | className?: string; 12 | reference: React.RefObject; 13 | } 14 | 15 | function Menu ({ children, className, reference }: Props): React.ReactElement { 16 | return ( 17 |
21 | {children} 22 |
23 | ); 24 | } 25 | 26 | export default styled(Menu)(({ theme }: ThemeProps) => ` 27 | background: ${theme.popupBackground}; 28 | border-radius: 10px; 29 | border: 1px solid ${theme.boxBorderColor}; 30 | box-sizing: border-box; 31 | box-shadow: 0 0 10px ${theme.boxShadow}; 32 | margin-top: 60px; 33 | padding: 15px 0; 34 | position: absolute; 35 | right: 0; 36 | z-index: 2; 37 | `); 38 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/MenuDivider.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React from 'react'; 7 | import styled from 'styled-components'; 8 | 9 | interface Props extends ThemeProps { 10 | className?: string; 11 | } 12 | 13 | function MenuDivider ({ className }: Props): React.ReactElement { 14 | return ( 15 |
16 | ); 17 | } 18 | 19 | export default styled(MenuDivider)(({ theme }: Props) => ` 20 | padding-top: 16px; 21 | margin-bottom: 16px; 22 | border-bottom: 1px solid ${theme.inputBorderColor}; 23 | `); 24 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/MenuItem.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React from 'react'; 7 | import styled from 'styled-components'; 8 | 9 | interface Props extends ThemeProps { 10 | children: React.ReactNode; 11 | className?: string; 12 | noBorder?: boolean; 13 | title?: React.ReactNode; 14 | } 15 | 16 | function MenuItem ({ children, className = '', title }: Props): React.ReactElement { 17 | return ( 18 |
19 | {title && ( 20 |
{title}
21 | )} 22 | {children} 23 |
24 | ); 25 | } 26 | 27 | export default styled(MenuItem)(({ theme }: ThemeProps) => ` 28 | min-width: 13rem; 29 | padding: 0 16px; 30 | max-width: 100%; 31 | 32 | > .itemTitle { 33 | margin: 0; 34 | width: 100%; 35 | font-size: ${theme.inputLabelFontSize}; 36 | line-height: 14px; 37 | letter-spacing: 0.04em; 38 | text-transform: uppercase; 39 | color: ${theme.textColor}; 40 | opacity: 0.65; 41 | } 42 | 43 | &+&.isTitled { 44 | margin-top: 16px; 45 | } 46 | `); 47 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/MnemonicSeed.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import { faCopy } from '@fortawesome/free-regular-svg-icons'; 7 | import React, { MouseEventHandler } from 'react'; 8 | import styled from 'styled-components'; 9 | 10 | import useTranslation from '../hooks/useTranslation'; 11 | import ActionText from './ActionText'; 12 | import TextAreaWithLabel from './TextAreaWithLabel'; 13 | 14 | interface Props { 15 | seed: string; 16 | onCopy: MouseEventHandler; 17 | className?: string; 18 | } 19 | 20 | function MnemonicSeed ({ className, onCopy, seed }: Props): React.ReactElement { 21 | const { t } = useTranslation(); 22 | 23 | return ( 24 |
25 | ('Generated 12-word mnemonic seed:')} 29 | value={seed} 30 | /> 31 |
32 | ('Copy to clipboard')} 38 | /> 39 |
40 |
41 | ); 42 | } 43 | 44 | export default styled(MnemonicSeed)(({ theme }: ThemeProps) => ` 45 | margin-bottom: 21px; 46 | 47 | .buttonsRow { 48 | display: flex; 49 | flex-direction: row; 50 | 51 | .copyBtn { 52 | margin-right: 32px; 53 | } 54 | } 55 | 56 | .mnemonicDisplay { 57 | textarea { 58 | color: ${theme.primaryColor}; 59 | font-size: ${theme.fontSize}; 60 | height: unset; 61 | letter-spacing: -0.01em; 62 | line-height: ${theme.lineHeight}; 63 | margin-bottom: 10px; 64 | padding: 14px; 65 | } 66 | } 67 | `); 68 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/NextStepButton.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import { IconProp } from '@fortawesome/fontawesome-svg-core'; 7 | import { faArrowRight } from '@fortawesome/free-solid-svg-icons'; 8 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 9 | import React from 'react'; 10 | import styled from 'styled-components'; 11 | 12 | import { CTA } from './../../../reef/extension-ui/uik'; 13 | 14 | type Props = React.ComponentProps; 15 | 16 | function NextStepButton ({ children, ...props }: Props): React.ReactElement { 17 | return ( 18 | 22 | {children} 23 | 28 | 29 | ); 30 | } 31 | 32 | export default styled(NextStepButton)(({ theme }: ThemeProps) => ` 33 | .arrowRight{ 34 | float: right; 35 | margin-top: 4px; 36 | margin-right: 1px; 37 | color: ${theme.buttonTextColor}; 38 | } 39 | `); 40 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/Spinner.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React from 'react'; 7 | import styled from 'styled-components'; 8 | 9 | import spinnerSrc from '../assets/spinner.png'; 10 | 11 | interface Props extends ThemeProps { 12 | className?: string; 13 | size?: 'normal'; 14 | } 15 | 16 | function Spinner ({ className = '', size = 'normal' }: Props): React.ReactElement { 17 | return ( 18 | 22 | ); 23 | } 24 | 25 | export default React.memo(styled(Spinner)` 26 | bottom: 0rem; 27 | height: 3rem; 28 | left: 50%; 29 | margin-left: -1.5rem; 30 | position: absolute; 31 | width: 3rem; 32 | z-index: 33 | `); 34 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/Svg.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React from 'react'; 7 | import styled from 'styled-components'; 8 | 9 | interface Props extends ThemeProps { 10 | className?: string; 11 | src: string; 12 | } 13 | 14 | const Svg = ({ className }: Props) => ; 15 | 16 | export default styled(Svg)(({ src, theme }: Props) => ` 17 | background: ${theme.textColor}; 18 | display: inline-block; 19 | mask: url(${src}); 20 | mask-size: cover; 21 | `); 22 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/Switch.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React, { useCallback } from 'react'; 7 | import styled from 'styled-components'; 8 | 9 | interface Props { 10 | checked: boolean; 11 | onChange: (checked: boolean) => void; 12 | uncheckedLabel: string; 13 | checkedLabel: string; 14 | className?: string; 15 | } 16 | 17 | function Switch ({ checked, checkedLabel, className, onChange, uncheckedLabel }: Props): React.ReactElement { 18 | const _onChange = useCallback( 19 | (event: React.ChangeEvent) => onChange(event.target.checked), 20 | [onChange] 21 | ); 22 | 23 | return ( 24 |
25 | {uncheckedLabel} 26 | 35 | {checkedLabel} 36 |
37 | ); 38 | } 39 | 40 | export default styled(Switch)(({ theme }: ThemeProps) => ` 41 | label { 42 | position: relative; 43 | display: inline-block; 44 | width: 48px; 45 | height: 24px; 46 | margin: 8px; 47 | } 48 | 49 | .checkbox { 50 | opacity: 0; 51 | width: 0; 52 | height: 0; 53 | 54 | &:checked + .slider:before { 55 | transform: translateX(24px); 56 | } 57 | } 58 | 59 | .slider { 60 | position: absolute; 61 | cursor: pointer; 62 | top: 0; 63 | left: 0; 64 | right: 0; 65 | bottom: 0; 66 | background-color: ${theme.readonlyInputBackground}; 67 | transition: 0.2s; 68 | border-radius: 100px; 69 | border: 1px solid ${theme.inputBorderColor}; 70 | 71 | &:before { 72 | position: absolute; 73 | content: ''; 74 | height: 16px; 75 | width: 16px; 76 | left: 4px; 77 | bottom: 3px; 78 | background-color: ${theme.primaryColor}; 79 | transition: 0.4s; 80 | border-radius: 50%; 81 | } 82 | } 83 | `); 84 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/Table.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import type { ThemeProps } from '../types'; 5 | 6 | import React from 'react'; 7 | import styled from 'styled-components'; 8 | 9 | interface Props { 10 | children: React.ReactNode; 11 | className?: string; 12 | isFull?: boolean; 13 | } 14 | 15 | function Table ({ children, className = '', isFull }: Props): React.ReactElement { 16 | return ( 17 | 18 | 19 | {children} 20 | 21 |
22 | ); 23 | } 24 | 25 | export default React.memo(styled(Table)(({ theme }: ThemeProps) => ` 26 | border: 0; 27 | display: block; 28 | font-size: ${theme.labelFontSize}; 29 | line-height: ${theme.labelLineHeight}; 30 | margin-bottom: 1rem; 31 | 32 | &.isFull { 33 | overflow: auto; 34 | } 35 | 36 | td.data { 37 | max-width: 0; 38 | overflow: hidden; 39 | text-align: left; 40 | text-overflow: ellipsis; 41 | vertical-align: middle; 42 | width: 100%; 43 | 44 | pre { 45 | font-family: inherit; 46 | font-size: 0.75rem; 47 | margin: 0; 48 | } 49 | } 50 | 51 | td.label { 52 | opacity: 0.5; 53 | padding: 0 0.5rem; 54 | text-align: right; 55 | vertical-align: top; 56 | white-space: nowrap; 57 | } 58 | 59 | details { 60 | cursor: pointer; 61 | max-width: 24rem; 62 | 63 | summary { 64 | text-overflow: ellipsis; 65 | outline: 0; 66 | overflow: hidden; 67 | white-space: nowrap; 68 | } 69 | 70 | &[open] summary { 71 | white-space: normal; 72 | } 73 | } 74 | `)); 75 | -------------------------------------------------------------------------------- /packages/extension-ui/src/components/TextAreaWithLabel.tsx: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 @polkadot/extension-ui authors & contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | import React, { useCallback } from 'react'; 5 | 6 | import Label from './Label'; 7 | import { TextArea } from './TextInputs'; 8 | 9 | interface Props { 10 | className?: string; 11 | isError?: boolean; 12 | isFocused?: boolean; 13 | isReadOnly?: boolean; 14 | rowsCount?: number; 15 | label: string; 16 | onChange?: (value: string) => void; 17 | value?: string; 18 | } 19 | 20 | export default function TextAreaWithLabel ({ className, isError, isFocused, isReadOnly, label, onChange, rowsCount, value }: Props): React.ReactElement { 21 | const _onChange = useCallback( 22 | ({ target: { value } }: React.ChangeEvent): void => { 23 | onChange && onChange(value); 24 | }, 25 | [onChange] 26 | ); 27 | 28 | return ( 29 |