├── .babelrc
├── .bootstraprc
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── README.md
├── bin
├── dependencies.js
└── generate.js
├── package-lock.json
├── package.json
├── src
├── Root.jsx
├── actions.js
├── app.js
├── configureStore.js
├── features
│ ├── accessControl
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── AccessControlList.jsx
│ │ │ ├── AccessControlList.scss
│ │ │ ├── EditPolicies.jsx
│ │ │ ├── EditPolicies.scss
│ │ │ ├── GrantListItem.jsx
│ │ │ ├── GrantListItem.scss
│ │ │ ├── NewCertificate.jsx
│ │ │ ├── NewCertificate.scss
│ │ │ ├── NewToken.jsx
│ │ │ └── TokenCreateModal.jsx
│ │ ├── constants.js
│ │ ├── reducers.js
│ │ ├── routes.js
│ │ └── selectors.js
│ ├── accounts
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── AccountShow.jsx
│ │ │ ├── AccountUpdate.jsx
│ │ │ ├── List.jsx
│ │ │ ├── ListItem.jsx
│ │ │ ├── New.jsx
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ └── routes.js
│ ├── app
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── Config
│ │ │ │ └── Config.jsx
│ │ │ ├── Container.jsx
│ │ │ ├── Loading
│ │ │ │ ├── Loading.jsx
│ │ │ │ └── Loading.scss
│ │ │ ├── Login
│ │ │ │ ├── Login.jsx
│ │ │ │ └── Login.scss
│ │ │ ├── Main
│ │ │ │ ├── Main.jsx
│ │ │ │ └── Main.scss
│ │ │ ├── Modal
│ │ │ │ ├── Modal.jsx
│ │ │ │ └── Modal.scss
│ │ │ ├── Navigation
│ │ │ │ ├── Navigation.jsx
│ │ │ │ └── Navigation.scss
│ │ │ ├── SecondaryNavigation
│ │ │ │ ├── SecondaryNavigation.jsx
│ │ │ │ └── SecondaryNavigation.scss
│ │ │ ├── Sync
│ │ │ │ ├── Sync.jsx
│ │ │ │ └── Sync.scss
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ └── utils.jsx
│ ├── assets
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── AssetShow.jsx
│ │ │ ├── AssetUpdate.jsx
│ │ │ ├── List.jsx
│ │ │ ├── ListItem.jsx
│ │ │ ├── New.jsx
│ │ │ ├── New.scss
│ │ │ ├── NewAssetInfo.jsx
│ │ │ ├── NewKeyAndSign.jsx
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ └── routes.js
│ ├── backup
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── Backup.jsx
│ │ │ ├── Backup.scss
│ │ │ └── index.js
│ │ ├── index.js
│ │ └── routes.js
│ ├── balances
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── List.jsx
│ │ │ ├── ListItem.jsx
│ │ │ ├── RescanDialog
│ │ │ │ ├── RescanDialog.jsx
│ │ │ │ └── RescanDialog.scss
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ └── routes.js
│ ├── configuration
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── Index
│ │ │ │ ├── Index.jsx
│ │ │ │ └── Index.scss
│ │ │ └── index.js
│ │ ├── index.js
│ │ └── routes.js
│ ├── core
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── CoreIndex
│ │ │ │ ├── CoreIndex.jsx
│ │ │ │ └── CoreIndex.scss
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ └── routes.js
│ ├── form
│ │ └── reducers.js
│ ├── initialization
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── FormIndex.scss
│ │ │ ├── Index
│ │ │ │ ├── Index.jsx
│ │ │ │ └── Index.scss
│ │ │ ├── Keystore
│ │ │ │ └── Keystore.jsx
│ │ │ ├── Mnemonic
│ │ │ │ └── Mnemonic.jsx
│ │ │ ├── MnemonicStepper
│ │ │ │ ├── MnemonicStepper.jsx
│ │ │ │ └── MnemonicStepper.scss
│ │ │ ├── Register
│ │ │ │ └── Register.jsx
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ └── routes.js
│ ├── mockhsm
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── CheckPassword
│ │ │ │ ├── CheckPassword.jsx
│ │ │ │ └── CheckPassword.scss
│ │ │ ├── ExportKey
│ │ │ │ ├── ExportKey.jsx
│ │ │ │ └── ExportKey.scss
│ │ │ ├── KeyUpdate.jsx
│ │ │ ├── List.jsx
│ │ │ ├── ListItem.jsx
│ │ │ ├── MnemonicStepper
│ │ │ │ ├── MnemonicStepper.jsx
│ │ │ │ └── MnemonicStepper.scss
│ │ │ ├── New.jsx
│ │ │ ├── ResetPassword
│ │ │ │ ├── ResetPassword.jsx
│ │ │ │ └── ResetPassword.scss
│ │ │ ├── Show.jsx
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ └── routes.js
│ ├── peers
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── List.jsx
│ │ │ ├── List.scss
│ │ │ ├── ListItem.jsx
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ └── routes.js
│ ├── shared
│ │ ├── actions
│ │ │ ├── create.js
│ │ │ ├── index.js
│ │ │ ├── list.js
│ │ │ └── update.js
│ │ ├── components
│ │ │ ├── AmountField
│ │ │ │ ├── AmountField.jsx
│ │ │ │ └── AmountField.scss
│ │ │ ├── AmountInputMask
│ │ │ │ └── AmountInputMask.jsx
│ │ │ ├── AmountUnitField
│ │ │ │ ├── AmountUnitField.jsx
│ │ │ │ └── AmountUnitField.scss
│ │ │ ├── Autocomplete
│ │ │ │ ├── AccountAlias.jsx
│ │ │ │ ├── AssetAlias.jsx
│ │ │ │ ├── AutocompleteField.jsx
│ │ │ │ ├── AutocompleteField.scss
│ │ │ │ └── index.js
│ │ │ ├── BaseList
│ │ │ │ ├── BaseList.jsx
│ │ │ │ ├── EmptyList.jsx
│ │ │ │ └── EmptyList.scss
│ │ │ ├── BaseNew.jsx
│ │ │ ├── BaseShow.jsx
│ │ │ ├── BaseUpdate.jsx
│ │ │ ├── CheckboxField
│ │ │ │ ├── CheckboxField.jsx
│ │ │ │ └── CheckboxField.scss
│ │ │ ├── ConfirmMnemonic
│ │ │ │ ├── ConfirmMnemonic.jsx
│ │ │ │ └── ConfirmMnemonic.scss
│ │ │ ├── ConsoleSection
│ │ │ │ ├── ConsoleSection.jsx
│ │ │ │ ├── ConsoleSection.scss
│ │ │ │ ├── ListItem
│ │ │ │ │ ├── ListItem.jsx
│ │ │ │ │ └── ListItem.scss
│ │ │ │ └── command.json
│ │ │ ├── CopyableBlock
│ │ │ │ ├── CopyableBlock.jsx
│ │ │ │ └── CopyableBlock.scss
│ │ │ ├── EmptyContent
│ │ │ │ ├── EmptyContent.jsx
│ │ │ │ └── EmptyContent.scss
│ │ │ ├── ErrorBanner
│ │ │ │ ├── ErrorBanner.jsx
│ │ │ │ └── ErrorBanner.scss
│ │ │ ├── FieldLabel
│ │ │ │ ├── FieldLabel.jsx
│ │ │ │ └── FieldLabel.scss
│ │ │ ├── FileField.jsx
│ │ │ ├── Flash
│ │ │ │ ├── Flash.jsx
│ │ │ │ └── Flash.scss
│ │ │ ├── FormContainer
│ │ │ │ ├── FormContainer.jsx
│ │ │ │ └── FormContainer.scss
│ │ │ ├── FormSection
│ │ │ │ ├── FormSection.jsx
│ │ │ │ └── FormSection.scss
│ │ │ ├── GasField
│ │ │ │ ├── GasField.jsx
│ │ │ │ └── GasField.scss
│ │ │ ├── HiddenField.jsx
│ │ │ ├── JsonField
│ │ │ │ ├── JsonField.jsx
│ │ │ │ └── JsonField.scss
│ │ │ ├── KeyConfiguration.jsx
│ │ │ ├── KeyValueTable
│ │ │ │ ├── KeyValueTable.jsx
│ │ │ │ └── KeyValueTable.scss
│ │ │ ├── Mnemonic
│ │ │ │ ├── Mnemonic.jsx
│ │ │ │ └── Mnemonic.scss
│ │ │ ├── NotFound.jsx
│ │ │ ├── ObjectSelectorField
│ │ │ │ ├── ObjectSelectorField.jsx
│ │ │ │ └── ObjectSelectorField.scss
│ │ │ ├── PageContent
│ │ │ │ ├── PageContent.jsx
│ │ │ │ └── PageContent.scss
│ │ │ ├── PageTitle
│ │ │ │ ├── PageTitle.jsx
│ │ │ │ └── PageTitle.scss
│ │ │ ├── Pagination
│ │ │ │ ├── Pagination.jsx
│ │ │ │ └── Pagination.scss
│ │ │ ├── PasswordField
│ │ │ │ ├── PasswordField.jsx
│ │ │ │ └── PasswordField.scss
│ │ │ ├── RadioField
│ │ │ │ ├── RadioField.jsx
│ │ │ │ └── RadioField.scss
│ │ │ ├── RawJsonButton.jsx
│ │ │ ├── RelativeTime.jsx
│ │ │ ├── RestoreKeystore
│ │ │ │ ├── RestoreKeystore.jsx
│ │ │ │ └── RestoreKeystore.scss
│ │ │ ├── RestoreMnemonic
│ │ │ │ ├── RestoreMnemonic.jsx
│ │ │ │ └── RestoreMnemonic.scss
│ │ │ ├── RoutingContainer.jsx
│ │ │ ├── SearchBar
│ │ │ │ ├── SearchBar.jsx
│ │ │ │ └── SearchBar.scss
│ │ │ ├── Section
│ │ │ │ ├── Section.jsx
│ │ │ │ └── Section.scss
│ │ │ ├── SelectField.jsx
│ │ │ ├── SingletonField.jsx
│ │ │ ├── Stepper
│ │ │ │ ├── Step.jsx
│ │ │ │ ├── StepList.jsx
│ │ │ │ └── stepper.scss
│ │ │ ├── SubmitIndicator
│ │ │ │ ├── SubmitIndicator.jsx
│ │ │ │ └── SubmitIndicator.scss
│ │ │ ├── TableList
│ │ │ │ ├── TableList.jsx
│ │ │ │ └── TableList.scss
│ │ │ ├── TextField.jsx
│ │ │ ├── TextareaField.jsx
│ │ │ ├── XpubField
│ │ │ │ ├── XpubField.jsx
│ │ │ │ └── XpubField.scss
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ └── routes.js
│ ├── testnet
│ │ ├── actions.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ └── utils.js
│ ├── transactionFeeds
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── List.jsx
│ │ │ ├── ListItem.jsx
│ │ │ ├── New.jsx
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ └── routes.js
│ ├── transactions
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── DetailSummary
│ │ │ │ ├── DetailSummary.jsx
│ │ │ │ └── DetailSummary.scss
│ │ │ ├── GeneratedTxHex
│ │ │ │ ├── GeneratedTxHex.jsx
│ │ │ │ ├── GeneratedTxHex.scss
│ │ │ │ ├── QrCode.jsx
│ │ │ │ └── QrCode.scss
│ │ │ ├── List.jsx
│ │ │ ├── ListItem
│ │ │ │ ├── ListItem.jsx
│ │ │ │ └── ListItem.scss
│ │ │ ├── New
│ │ │ │ ├── AdvancedTransactionForm.jsx
│ │ │ │ ├── ConfirmModal
│ │ │ │ │ ├── ConfirmModal.jsx
│ │ │ │ │ └── ConfirmModal.scss
│ │ │ │ ├── FormActionItem.jsx
│ │ │ │ ├── FormActionItem.scss
│ │ │ │ ├── IssueAssets.jsx
│ │ │ │ ├── MultiSignTransactionDetails
│ │ │ │ │ ├── Modal.jsx
│ │ │ │ │ ├── TransactionDetails.jsx
│ │ │ │ │ └── TransactionDetails.scss
│ │ │ │ ├── New.jsx
│ │ │ │ ├── New.scss
│ │ │ │ ├── NewTransactionsContainer
│ │ │ │ │ ├── TxContainer.jsx
│ │ │ │ │ └── TxContainer.scss
│ │ │ │ └── NormalTransactionForm.jsx
│ │ │ ├── Show.jsx
│ │ │ ├── Summary
│ │ │ │ ├── Summary.jsx
│ │ │ │ └── Summary.scss
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ ├── routes.js
│ │ └── transactions.js
│ ├── tutorial
│ │ ├── actions.js
│ │ ├── components
│ │ │ ├── Tutorial.jsx
│ │ │ ├── TutorialForm
│ │ │ │ ├── TutorialForm.jsx
│ │ │ │ └── TutorialForm.scss
│ │ │ ├── TutorialHeader
│ │ │ │ ├── TutorialHeader.jsx
│ │ │ │ └── TutorialHeader.scss
│ │ │ ├── TutorialInfo
│ │ │ │ ├── TutorialInfo.jsx
│ │ │ │ └── TutorialInfo.scss
│ │ │ └── TutorialModal
│ │ │ │ ├── TutorialModal.jsx
│ │ │ │ └── TutorialModal.scss
│ │ ├── index.js
│ │ ├── introduction.json
│ │ ├── reducers.js
│ │ └── steps.json
│ └── unspents
│ │ ├── actions.js
│ │ ├── components
│ │ ├── List.jsx
│ │ ├── ListItem.jsx
│ │ └── index.js
│ │ ├── index.js
│ │ ├── reducers.js
│ │ └── routes.js
├── i18n.js
├── locales
│ ├── en
│ │ └── translation.json
│ └── zh
│ │ └── translation.json
├── reducers.js
├── routes.js
├── sdk
│ ├── api
│ │ ├── accessTokens.js
│ │ ├── accounts.js
│ │ ├── assets.js
│ │ ├── authorizationGrants.js
│ │ ├── backUp.js
│ │ ├── balances.js
│ │ ├── bytomCLI.js
│ │ ├── config.js
│ │ ├── hsmSigner.js
│ │ ├── mockHsmKeys.js
│ │ ├── peer.js
│ │ ├── transactionFeeds.js
│ │ ├── transactions.js
│ │ └── unspentOutputs.js
│ ├── client.js
│ ├── connection.js
│ ├── errors.js
│ ├── index.js
│ ├── page.js
│ ├── shared.js
│ └── util.js
└── utility
│ ├── buildInOutDisplay.js
│ ├── clipboard.js
│ ├── componentClassNames.js
│ ├── disableAutocomplete.js
│ ├── environment.js
│ ├── localStorage.js
│ ├── math.js
│ ├── string.js
│ └── time.js
├── static
├── fonts
│ ├── dotsfont
│ │ ├── dotsfont.eot
│ │ ├── dotsfont.svg
│ │ ├── dotsfont.ttf
│ │ └── dotsfont.woff
│ ├── nitti-normal.woff
│ ├── nittigrotesk-medium.woff
│ └── nittigrotesk-normal.woff
├── images
│ ├── backup
│ │ ├── backup-active.svg
│ │ ├── backup.svg
│ │ ├── restore-keystore-active.svg
│ │ ├── restore-keystore.svg
│ │ ├── restore-mnemonic-active.svg
│ │ └── restore-mnemonic.svg
│ ├── capslock-green.svg
│ ├── capslock.svg
│ ├── chain-favicon.png
│ ├── chevron-blue.png
│ ├── chevron-green.png
│ ├── chevron.png
│ ├── config
│ │ ├── join-active.png
│ │ ├── join.png
│ │ ├── new-active.png
│ │ ├── new.png
│ │ ├── testnet-active.png
│ │ └── testnet.png
│ ├── console-window.svg
│ ├── copy.svg
│ ├── empty
│ │ ├── account.svg
│ │ ├── asset.svg
│ │ ├── balance.svg
│ │ ├── client_access_token.svg
│ │ ├── key.svg
│ │ ├── network_access_token.svg
│ │ ├── transaction.svg
│ │ ├── transactionFeed.svg
│ │ └── unspent.svg
│ ├── favicon.png
│ ├── logo-bytom-white.svg
│ ├── logo-shadowed.png
│ ├── logo-white.png
│ ├── navigation
│ │ ├── account-active.png
│ │ ├── account.png
│ │ ├── asset-active.png
│ │ ├── asset.png
│ │ ├── balance-active.png
│ │ ├── balance.png
│ │ ├── client-active.png
│ │ ├── client.png
│ │ ├── core-active.png
│ │ ├── core.png
│ │ ├── docs.png
│ │ ├── error.png
│ │ ├── feed-active.png
│ │ ├── feed.png
│ │ ├── help.png
│ │ ├── logout.png
│ │ ├── mockhsm-active.png
│ │ ├── mockhsm.png
│ │ ├── network-active.png
│ │ ├── network.png
│ │ ├── settings.png
│ │ ├── transaction-active.png
│ │ ├── transaction.png
│ │ ├── tutorial-active.png
│ │ ├── tutorial.png
│ │ ├── unspent-active.png
│ │ └── unspent.png
│ ├── search.png
│ ├── sum.png
│ ├── transactions
│ │ ├── coinbase.svg
│ │ ├── fail.svg
│ │ ├── issue.svg
│ │ ├── received.svg
│ │ ├── retire.svg
│ │ ├── sent.svg
│ │ └── success.svg
│ └── warning.svg
└── styles
│ ├── _body.scss
│ ├── _bootstrap-overrides.scss
│ ├── app.scss
│ └── resources.scss
└── webpack
├── webpack.app.js
├── webpack.base.js
└── webpack.dll.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "react"],
3 | "plugins": ["transform-object-rest-spread"],
4 | "env": {
5 | "development": {
6 | "presets": ["react-hmre"]
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.bootstraprc:
--------------------------------------------------------------------------------
1 | {
2 | bootstrapVersion: 3,
3 |
4 | // This gets loaded before bootstrap/variables
5 | preBootstrapCustomizations: ./static/styles/_bootstrap-overrides.scss,
6 |
7 | // Imports app styles into project
8 | appStyles: ./static/styles/app.scss,
9 |
10 | env: {
11 | development: {
12 | extractStyles: false
13 | },
14 | production: {
15 | extractStyles: true
16 | }
17 | },
18 |
19 | styleLoaders: ['style', 'css', 'postcss', 'sass'],
20 |
21 | // Bootstrap styles
22 | styles: {
23 | mixins: true,
24 | normalize: true,
25 | glyphicons: true,
26 | scaffolding: true,
27 | type: true,
28 | code: true,
29 | grid: true,
30 | tables: true,
31 | forms: true,
32 | buttons: true,
33 | alerts: true,
34 | labels: true,
35 | dropdowns: true,
36 | input-groups: true,
37 | navs: true,
38 | navbar: true,
39 | wells: true,
40 | jumbotron: true,
41 | pagination: true,
42 | pager: true,
43 | panels: true,
44 | close: true,
45 | utilities: true,
46 | responsive-utilities: true,
47 | button-groups: true
48 | },
49 |
50 | // Bootstrap scripts
51 | scripts: false
52 | }
53 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*.svg
2 | **/*.png
3 | **/*.ttf
4 | **/*.scss
5 | **/*.woff
6 | **/*.md
7 | **/*.json
8 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true,
5 | "commonjs": true
6 | },
7 | "plugins": [
8 | "react"
9 | ],
10 | "extends": "eslint:recommended",
11 | "parserOptions": {
12 | "ecmaVersion": 6,
13 | "sourceType": "module",
14 | "ecmaFeatures": {
15 | "jsx": true,
16 | "experimentalObjectRestSpread": true
17 | }
18 | },
19 | "rules": {
20 | "quotes": ["error", "single", {"avoidEscape": true}],
21 | "jsx-quotes": ["error", "prefer-single"],
22 | "indent": ["error", 2, {"SwitchCase": 1}],
23 | "linebreak-style": ["error", "unix"],
24 | "semi": ["error", "never"],
25 | "react/jsx-uses-react": "error",
26 | "react/jsx-uses-vars": "error"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | .env
4 | public
5 | errorShots
6 | .idea
7 | .DS_Store
8 |
--------------------------------------------------------------------------------
/bin/dependencies.js:
--------------------------------------------------------------------------------
1 | // Original from https://github.com/mxstbr/react-boilerplate/
2 |
3 | /*eslint-env node*/
4 |
5 | // No need to build the DLL in production
6 | if (process.env.NODE_ENV === 'production') {
7 | process.exit(0)
8 | }
9 |
10 | require('shelljs/global')
11 |
12 | const path = require('path')
13 | const fs = require('fs')
14 | const exists = fs.existsSync
15 | const writeFile = fs.writeFileSync
16 |
17 | const pkg = require(path.join(process.cwd(), 'package.json'))
18 | const outputPath = path.join(process.cwd(), 'node_modules/dashboard-dlls')
19 | const dllManifestPath = path.join(outputPath, 'package.json')
20 |
21 | /**
22 | * I use node_modules/react-boilerplate-dlls by default just because
23 | * it isn't going to be version controlled and babel wont try to parse it.
24 | */
25 | mkdir('-p', outputPath)
26 |
27 | echo('Building the Webpack DLL...')
28 |
29 | /**
30 | * Create a manifest so npm install doesn't warn us
31 | */
32 | if (!exists(dllManifestPath)) {
33 | writeFile(
34 | dllManifestPath,
35 | JSON.stringify({
36 | name: 'react-boilerplate-dlls',
37 | private: true,
38 | author: pkg.author,
39 | repository: pkg.repository,
40 | version: pkg.version,
41 | }),
42 | 'utf8'
43 | )
44 | }
45 |
46 | // the BUILDING_DLL env var is set to avoid confusing the development environment
47 | exec('webpack --display-chunks --display-error-details --color --config webpack/webpack.dll.js')
48 |
--------------------------------------------------------------------------------
/src/Root.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Provider } from 'react-redux'
3 | import { applyRouterMiddleware, Router } from 'react-router'
4 | import { history } from 'utility/environment'
5 | import { syncHistoryWithStore } from 'react-router-redux'
6 | import useScroll from 'react-router-scroll/lib/useScroll'
7 |
8 | import makeRoutes from './routes'
9 |
10 | export default class Root extends React.Component {
11 | componentWillMount() {
12 | document.title = 'Bytom Dashboard'
13 | }
14 |
15 | render() {
16 | const store = this.props.store
17 | const syncedHistory = syncHistoryWithStore(history, store)
18 | return (
19 |
20 |
25 |
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/actions.js:
--------------------------------------------------------------------------------
1 | import accessControl from 'features/accessControl/actions'
2 | import { actions as account } from 'features/accounts'
3 | import { actions as app } from 'features/app'
4 | import { actions as asset } from 'features/assets'
5 | import { actions as backUp } from 'features/backup'
6 | import { actions as balance } from 'features/balances'
7 | import { actions as configuration } from 'features/configuration'
8 | import { actions as core } from 'features/core'
9 | import { actions as initialization } from 'features/initialization'
10 | import { actions as mockhsm } from 'features/mockhsm'
11 | import { actions as testnet } from 'features/testnet'
12 | import { actions as transaction } from 'features/transactions'
13 | import { actions as transactionFeed } from 'features/transactionFeeds'
14 | import { actions as tutorial } from 'features/tutorial'
15 | import { actions as unspent } from 'features/unspents'
16 | import { actions as peer } from 'features/peers'
17 |
18 | const actions = {
19 | accessControl,
20 | account,
21 | app,
22 | asset,
23 | backUp,
24 | balance,
25 | configuration,
26 | core,
27 | initialization,
28 | key: mockhsm,
29 | testnet,
30 | transaction,
31 | transactionFeed,
32 | tutorial,
33 | unspent,
34 | peer
35 | }
36 |
37 | export default actions
38 |
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | /*eslint-env node*/
2 |
3 | import 'bootstrap-loader'
4 | import React from 'react'
5 | import { render } from 'react-dom'
6 | import Root from 'Root'
7 | import configureStore from 'configureStore'
8 | import { I18nextProvider } from 'react-i18next'
9 | import i18n from './i18n'
10 |
11 | // Set favicon
12 | let faviconPath = require('!!file?name=favicon.ico!../static/images/favicon.png')
13 | let favicon = document.createElement('link')
14 | favicon.type = 'image/png'
15 | favicon.rel = 'shortcut icon'
16 | favicon.href = faviconPath
17 | document.getElementsByTagName('head')[0].appendChild(favicon)
18 |
19 | // Start app
20 | export const store = configureStore()
21 | render(
22 |
23 |
24 | ,
25 | document.getElementById('root')
26 | )
27 |
--------------------------------------------------------------------------------
/src/configureStore.js:
--------------------------------------------------------------------------------
1 | /*eslint-env node*/
2 |
3 | import { createStore, applyMiddleware, compose } from 'redux'
4 | import thunkMiddleware from 'redux-thunk'
5 | import { routerMiddleware as createRouterMiddleware } from 'react-router-redux'
6 | import { history } from 'utility/environment'
7 | import { exportState, importState } from 'utility/localStorage'
8 |
9 | import makeRootReducer from 'reducers'
10 |
11 | const routerMiddleware = createRouterMiddleware(history)
12 |
13 | export default function() {
14 | const store = createStore(
15 | makeRootReducer(),
16 | importState(),
17 | compose(
18 | applyMiddleware(
19 | thunkMiddleware,
20 | routerMiddleware
21 | ),
22 | window.devToolsExtension ? window.devToolsExtension() : f => f
23 | )
24 | )
25 |
26 | if (module.hot) {
27 | // Enable Webpack hot module replacement for reducers
28 | module.hot.accept('reducers', () => {
29 | const newRootReducer = require('reducers').default
30 | store.replaceReducer(newRootReducer())
31 | })
32 | }
33 |
34 | store.subscribe(exportState(store))
35 |
36 | return store
37 | }
38 |
--------------------------------------------------------------------------------
/src/features/accessControl/components/AccessControlList.scss:
--------------------------------------------------------------------------------
1 | .newBtn {
2 | position: absolute;
3 | right: $gutter-size;
4 | top: $gutter-size;
5 | vertical-align: baseline;
6 | }
7 |
8 | .btnGroup {
9 | margin-bottom: $gutter-size;
10 | }
11 |
12 | .btn {
13 | color: $highlight-default;
14 | visibility: hidden;
15 |
16 | &:hover {
17 | text-decoration: underline;
18 | }
19 |
20 | &:hover, &:active, &:hover:active, &:active:focus, &:focus {
21 | color: $highlight-default;
22 | background: $background-color;
23 | }
24 |
25 | &:focus {
26 | background: white;
27 | }
28 | }
29 |
30 | .active, .active:focus, .active:hover, .active:active:focus {
31 | box-shadow: inset 0 2px 3px rgba(0, 0, 0, 0.15);
32 | border-color: $border-strong-color;
33 | background: $background-emphasis-color;
34 | color: $text-color;
35 | cursor: default;
36 | text-decoration: none;
37 | }
38 |
--------------------------------------------------------------------------------
/src/features/accessControl/components/EditPolicies.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | position: relative;
3 | text-align: left;
4 |
5 | button {
6 | position: absolute;
7 | right: 0;
8 | top: 0;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/features/accessControl/components/GrantListItem.scss:
--------------------------------------------------------------------------------
1 | .tdWrap {
2 | border-top: 1px solid #f2f2f2;
3 | color: #747c89;
4 | line-height: 20px;
5 | vertical-align: top;
6 | word-wrap: break-word;
7 | }
8 |
--------------------------------------------------------------------------------
/src/features/accessControl/components/NewCertificate.scss:
--------------------------------------------------------------------------------
1 | .subjectField {
2 | display: flex;
3 | align-items: center;
4 |
5 | :global {
6 | .form-group {
7 | flex: 1;
8 | margin-right: $gutter-size;
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/features/accessControl/components/TokenCreateModal.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { CopyableBlock } from 'features/shared/components'
3 |
4 | export default class CreateModal extends React.Component {
5 | render() {
6 | return
7 |
Created new access token
8 |
Please store this token carefully. This is the last time it will be displayed.
9 |
10 |
11 |
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/features/accessControl/constants.js:
--------------------------------------------------------------------------------
1 | export const policyOptions = [
2 | {
3 | label: 'Client read/write',
4 | value: 'client-readwrite',
5 | hint: 'Full access to the Client API'
6 | },
7 | {
8 | label: 'Client read-only',
9 | value: 'client-readonly',
10 | hint: 'Access to read-only Client endpoints'
11 | },
12 | {
13 | label: 'Monitoring',
14 | value: 'monitoring',
15 | hint: 'Access to monitoring-specific endpoints'
16 | },
17 | {
18 | label: 'Cross-core',
19 | value: 'crosscore',
20 | hint: 'Access to the cross-core API, not including block-signing. Necessary for connecting to the generator'
21 | },
22 | {
23 | label: 'Cross-core block signing',
24 | value: 'crosscore-signblock',
25 | hint: 'Access to the cross-core API\'s block-signing functionality'
26 | },
27 | {
28 | label: 'Internal',
29 | value: 'internal',
30 | hidden: true,
31 | },
32 | ]
33 |
34 | export const subjectFieldOptions = [
35 | {label: 'CommonName', value: 'cn'},
36 | {label: 'Country', value: 'c', array: true},
37 | {label: 'Organization', value: 'o', array: true},
38 | {label: 'OrganizationalUnit', value: 'ou', array: true},
39 | {label: 'Locality', value: 'l', array: true},
40 | {label: 'Province', value: 'st', array: true},
41 | {label: 'StreetAddress', value: 'street', array: true},
42 | {label: 'PostalCode', value: 'postalcode', array: true},
43 | {label: 'SerialNumber', value: 'serialnumber'},
44 | ]
45 |
--------------------------------------------------------------------------------
/src/features/accessControl/routes.js:
--------------------------------------------------------------------------------
1 | import AccessControlList from './components/AccessControlList'
2 | import NewToken from './components/NewToken'
3 | // import NewCertificate from './components/NewCertificate'
4 | import { makeRoutes } from 'features/shared'
5 | import actions from './actions'
6 |
7 | const checkParams = (nextState, replace) => {
8 | if (!['token', 'certificate'].includes(nextState.location.query.type)) {
9 | replace({
10 | pathname: '/access-control',
11 | search: '?type=token',
12 | state: {preserveFlash: true}
13 | })
14 | return false
15 | }
16 | return true
17 | }
18 |
19 | export default (store) => {
20 | const loadGrants = () => store.dispatch(actions.fetchItems())
21 |
22 | const routes = makeRoutes(store, 'accessControl', AccessControlList, null, null, {
23 | path: 'access-control',
24 | name: 'accessControl'
25 | })
26 |
27 | routes.indexRoute.onEnter = (nextState, replace) => {
28 | if (checkParams(nextState, replace)) { loadGrants() }
29 | }
30 |
31 | routes.indexRoute.onChange = (_, nextState, replace) => {
32 | checkParams(nextState, replace)
33 | }
34 |
35 | routes.childRoutes.push({
36 | path: 'create-token',
37 | component: NewToken
38 | })
39 |
40 | // routes.childRoutes.push({
41 | // path: 'add-certificate',
42 | // component: NewCertificate
43 | // })
44 |
45 | return routes
46 | }
47 |
--------------------------------------------------------------------------------
/src/features/accessControl/selectors.js:
--------------------------------------------------------------------------------
1 | import { createSelector } from 'reselect'
2 | import { policyOptions } from './constants'
3 |
4 | export const getPolicyNames = createSelector(
5 | item => item.grants,
6 | grants => grants.map(
7 | grant => {
8 | const isProtected = grant.protected
9 | const policy = grant.policy
10 |
11 | const found = policyOptions.find(elem => elem.value == policy)
12 | let label = found ? found.label : policy
13 | if (isProtected) {
14 | label = label + ' (Protected)'
15 | }
16 | return label
17 | }
18 | )
19 | )
20 |
21 | export const guardType = (item) => item.guardType
22 |
23 | export const isAccessToken = createSelector(
24 | guardType,
25 | type => type == 'access_token'
26 | )
27 |
28 | export const hasProtectedGrant = (grants, policy) =>
29 | grants.find(grant => grant.protected && grant.policy == policy) != undefined
30 |
--------------------------------------------------------------------------------
/src/features/accounts/actions.js:
--------------------------------------------------------------------------------
1 | import { chainClient } from 'utility/environment'
2 | import { baseCreateActions, baseUpdateActions, baseListActions } from 'features/shared/actions'
3 |
4 | const type = 'account'
5 |
6 | const list = baseListActions(type, { defaultKey: 'alias' })
7 | const create = baseCreateActions(type, {
8 | jsonFields: ['tags'],
9 | intFields: ['quorum'],
10 | redirectToShow: true,
11 | })
12 | const update = baseUpdateActions(type, {
13 | jsonFields: ['tags']
14 | })
15 |
16 | let actions = {
17 | ...list,
18 | ...create,
19 | ...update,
20 | createReceiver: (data) => () => {
21 | return chainClient().accounts.createReceiver(data)
22 | },
23 | createAddress: (data) => () => {
24 | return chainClient().accounts.createAddress(data)
25 | },
26 | listAddresses: (accountId) => {
27 | return chainClient().accounts.listAddresses({accountId})
28 | }
29 | }
30 |
31 | export default actions
32 |
--------------------------------------------------------------------------------
/src/features/accounts/components/List.jsx:
--------------------------------------------------------------------------------
1 | import { BaseList, TableList } from 'features/shared/components'
2 |
3 | import ListItem from './ListItem'
4 | import {withNamespaces} from 'react-i18next'
5 |
6 | const type = 'account'
7 |
8 | const mapStateToProps = (state, ownProps) => {
9 | return {
10 | ...BaseList.mapStateToProps(type, ListItem, {
11 | wrapperComponent: TableList,
12 | wrapperProps: {
13 | titles: ownProps.t('account.formTitle', { returnObjects: true })
14 | }
15 | })(state)
16 | }
17 | }
18 | export default withNamespaces('translations') (BaseList.connect(
19 | mapStateToProps,
20 | BaseList.mapDispatchToProps(type)
21 | ))
22 |
--------------------------------------------------------------------------------
/src/features/accounts/components/ListItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router'
3 | import {withNamespaces} from 'react-i18next'
4 |
5 | class ListItem extends React.Component {
6 | render() {
7 | const item = this.props.item
8 | const t = this.props.t
9 |
10 | return(
11 |
12 | {item.alias || '-'} |
13 | {item.id} |
14 |
15 |
16 | {t('commonWords.viewDetails')} →
17 |
18 | |
19 |
20 | )
21 | }
22 | }
23 |
24 | export default withNamespaces('translations') (ListItem)
25 |
--------------------------------------------------------------------------------
/src/features/accounts/components/index.js:
--------------------------------------------------------------------------------
1 | import List from './List'
2 | import New from './New'
3 | import AccountShow from './AccountShow'
4 | import AccountUpdate from './AccountUpdate'
5 |
6 | export {
7 | List,
8 | New,
9 | AccountShow,
10 | AccountUpdate
11 | }
12 |
--------------------------------------------------------------------------------
/src/features/accounts/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import reducers from './reducers'
3 | import routes from './routes'
4 |
5 | export {
6 | actions,
7 | reducers,
8 | routes
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/accounts/reducers.js:
--------------------------------------------------------------------------------
1 | import { reducers } from 'features/shared'
2 | import { combineReducers } from 'redux'
3 |
4 | const type = 'account'
5 |
6 | export default combineReducers({
7 | items: reducers.itemsReducer(type),
8 | queries: reducers.queriesReducer(type),
9 | autocompleteIsLoaded: reducers.autocompleteIsLoadedReducer(type),
10 | })
11 |
--------------------------------------------------------------------------------
/src/features/accounts/routes.js:
--------------------------------------------------------------------------------
1 | import { List, New, AccountShow, AccountUpdate } from './components'
2 | import { makeRoutes } from 'features/shared'
3 |
4 | export default (store) => {
5 | const routes = makeRoutes(store, 'account', List, New, AccountShow)
6 | routes.childRoutes.push({
7 | path: ':id/alias',
8 | component: AccountUpdate
9 | })
10 |
11 | return routes
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/src/features/app/actions.js:
--------------------------------------------------------------------------------
1 | import { push } from 'react-router-redux'
2 | import { chainClient } from 'utility/environment'
3 |
4 | const actions = {
5 | dismissFlash: (param) => ({type: 'DISMISS_FLASH', param}),
6 | displayedFlash: (param) => ({type: 'DISPLAYED_FLASH', param}),
7 | showModal: (body, accept, cancel, options = {}) => ({type: 'SHOW_MODAL', payload: { body, accept, cancel, options }}),
8 | hideModal: { type: 'HIDE_MODAL' },
9 | showNavAdvanced: { type: 'SHOW_NAV_ADVANCE' },
10 | hideNavAdvanced: (dispatch, getState) => {
11 | if (getState().app.navAdvancedState === 'advance' ) {
12 | dispatch({ type: 'HIDE_NAV_ADVANCE' })
13 | }
14 | },
15 | showRoot: push('/transactions'),
16 | toggleDropdown: { type: 'TOGGLE_DROPDOWN' },
17 | closeDropdown: (dispatch, getState) => {
18 | if (getState().app.dropdownState === 'open') {
19 | dispatch({ type: 'CLOSE_DROPDOWN' })
20 | }
21 | },
22 | showInitialization: () => {
23 | return (dispatch, getState) => {
24 | // Need a default here, since locationBeforeTransitions gets cleared
25 | // during logout.
26 | let pathname = (getState().routing.locationBeforeTransitions || {}).pathname
27 |
28 | if (!(pathname.indexOf('initialization') >= 0)) {
29 | dispatch(push('/initialization'))
30 | }
31 | }
32 | },
33 | showConfiguration: () => {
34 | return (dispatch, getState) => {
35 | // Need a default here, since locationBeforeTransitions gets cleared
36 | // during logout.
37 | let pathname = (getState().routing.locationBeforeTransitions || {}).pathname
38 |
39 | if (pathname !== 'configuration') {
40 | dispatch(push('/configuration'))
41 | }
42 | }
43 | },
44 | cmd: (data) => () => {
45 | return chainClient().bytomCli.request(data)
46 | }
47 | }
48 |
49 | export default actions
50 |
--------------------------------------------------------------------------------
/src/features/app/components/Config/Config.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | class Config extends React.Component {
4 | render() {
5 | return (
6 |
7 |
8 | {this.props.children}
9 |
10 |
11 | )
12 | }
13 | }
14 |
15 | export default Config
16 |
--------------------------------------------------------------------------------
/src/features/app/components/Loading/Loading.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './Loading.scss'
3 | import componentClassNames from 'utility/componentClassNames'
4 |
5 | class Loading extends React.Component {
6 | render() {
7 | return (
8 |
9 |
})
10 | {this.props.children}
11 |
12 | )
13 | }
14 | }
15 |
16 | export default Loading
17 |
--------------------------------------------------------------------------------
/src/features/app/components/Loading/Loading.scss:
--------------------------------------------------------------------------------
1 | @include keyframes(pulsing) {
2 | 0% { opacity: 0.6; }
3 | 50% { opacity: 1.0; }
4 | 100% { opacity: 0.6; }
5 | }
6 |
7 | .main {
8 | display: flex;
9 | align-items: center;
10 | justify-content: center;
11 | color: $text-color;
12 | font-size: 24px;
13 | margin-top: -75px;
14 | }
15 |
16 | .logo {
17 | width: 175px;
18 | padding-bottom: $gutter-size;
19 | @include animation(pulsing 2s infinite);
20 | @include animation-timing-function(ease-in-out);
21 | }
22 |
--------------------------------------------------------------------------------
/src/features/app/components/Login/Login.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | background: $background-inverse-color;
3 | position: absolute;
4 | top: 0;
5 | bottom: 0;
6 | left: 0;
7 | right: 0;
8 | }
9 |
10 | .image {
11 | width: 150px;
12 | position: absolute;
13 | top: calc(100px);
14 | left: calc(50% - 75px);
15 | }
16 |
17 | .form {
18 | background: $background-color;
19 | border-radius: $border-radius-standard;
20 | position: absolute;
21 | left: calc(50% - 250px);
22 | top: calc(200px);
23 | width: 500px;
24 | padding: 30px;
25 | }
26 |
--------------------------------------------------------------------------------
/src/features/app/components/Modal/Modal.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | position: fixed;
3 | top: 0;
4 | right: 0;
5 | left: 0;
6 | bottom: 0;
7 | z-index: 100;
8 | }
9 | .backdrop {
10 | background: transparentize(black, 0.2);
11 | width: 100%;
12 | height: 100%;
13 | }
14 |
15 | .content {
16 | background: $background-color;
17 | padding: 30px;
18 | position: absolute;
19 | top: 10%;
20 | left: calc(50% - 250px);
21 | width: 500px;
22 | max-height: 80%;
23 | overflow: auto;
24 |
25 | pre {
26 | white-space: pre-wrap;
27 | background: $background-content-color;
28 | padding: 8px 12px;
29 | color: $text-color;
30 | line-height: 1.4;
31 | }
32 | }
33 |
34 | .wide {
35 | left: calc(50% - 425px);
36 | width: 850px;
37 | }
38 |
39 | .cancel {
40 | color: $text-color;
41 | }
42 |
43 | .box {
44 | background: $background-color;
45 | position: absolute;
46 | top: 10%;
47 | height: 80%;
48 | }
49 |
50 | .title {
51 | height: 55px;
52 | padding: $gutter-size/2 $gutter-size;
53 | border-bottom: 1px solid $border-color;
54 | font-size: $font-size-section-title;
55 | color: $text-strong-color;
56 | }
57 |
58 | .close {
59 | position: absolute;
60 | right: 10px;
61 | top: 10px;
62 | }
63 |
--------------------------------------------------------------------------------
/src/features/app/components/SecondaryNavigation/SecondaryNavigation.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | background: $background-color;
3 | border-radius: $border-radius-standard;
4 | position: absolute;
5 | top: $title-height - 8px;
6 | right: $gutter-size/2;
7 | width: $sidebar-width - $gutter-size;
8 | z-index: 11;
9 |
10 | a {
11 | display: block;
12 | padding: 1px 0;
13 | color: $text-color;
14 | }
15 |
16 | .active {
17 | color: $highlight-default;
18 | }
19 | }
20 |
21 | .navigation {
22 | padding: 0;
23 | list-style-type: none;
24 | margin-top: 8px;
25 | margin-bottom: 8px;
26 |
27 | li {
28 | padding: 0 20px;
29 | }
30 | }
31 |
32 | .navigationTitle {
33 | font-size: $font-size-caps;
34 | text-transform: uppercase;
35 | padding: 3px 20px;
36 | }
37 |
38 | .logOut {
39 | margin-top: 5px;
40 | border-top: 1px solid $border-color;
41 | margin-bottom: -3px;
42 |
43 | a {
44 | padding-top: 5px;
45 | }
46 | }
47 |
48 | .activeIcon {
49 | display: none;
50 | }
51 |
52 | .active .activeIcon {
53 | display: inline;
54 | }
55 |
56 | .active .icon {
57 | display: none;
58 | }
59 |
60 | .iconWrapper {
61 | img {
62 | width: 28px;
63 | height: 28px;
64 | margin-top: -2px;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/features/app/components/Sync/Sync.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | padding: $gutter-size;
3 | border-top: 1px solid $border-inverse-color;
4 | margin-top: $gutter-size;
5 | }
6 |
7 | .testnetError {
8 | color: $brand-danger;
9 | }
10 |
11 | .testnetReset {
12 | color: $brand-warning;
13 | }
14 |
15 | .blockHightlight {
16 | color: $navbar-default-link-color;
17 | }
18 |
19 | .progressBar{
20 | margin-top: 6px;
21 | height: 5px;
22 | border-radius: 0;
23 | }
24 |
--------------------------------------------------------------------------------
/src/features/app/components/index.js:
--------------------------------------------------------------------------------
1 | import Container from './Container'
2 | import Main from './Main/Main'
3 | import Config from './Config/Config'
4 | import Login from './Login/Login'
5 | import Loading from './Loading/Loading'
6 | import Modal from './Modal/Modal'
7 | import Navigation from './Navigation/Navigation'
8 | import SecondaryNavigation from './SecondaryNavigation/SecondaryNavigation'
9 |
10 | export {
11 | Container,
12 | Main,
13 | Config,
14 | Login,
15 | Loading,
16 | Modal,
17 | Navigation,
18 | SecondaryNavigation,
19 | }
20 |
--------------------------------------------------------------------------------
/src/features/app/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import reducers from './reducers'
3 |
4 | export {
5 | actions,
6 | reducers,
7 | }
8 |
--------------------------------------------------------------------------------
/src/features/app/utils.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export const navIcon = (name, styles) => {
4 | let active = false
5 | const icon = require(`images/navigation/${name}.png`)
6 |
7 | try {
8 | active = require(`images/navigation/${name}-active.png`)
9 | } catch (err) { /* do nothing */ }
10 | return (
11 |
12 |
13 | {active &&
}
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/src/features/assets/actions.js:
--------------------------------------------------------------------------------
1 | import { baseCreateActions, baseUpdateActions, baseListActions } from 'features/shared/actions'
2 |
3 | const type = 'asset'
4 |
5 | const list = baseListActions(type, { defaultKey: 'alias' })
6 | const create = baseCreateActions(type, {
7 | intFields: ['quorum'],
8 | redirectToShow: true,
9 | })
10 | const update = baseUpdateActions(type, {
11 | jsonFields: ['tags']
12 | })
13 |
14 | const actions = {
15 | ...list,
16 | ...create,
17 | ...update,
18 | }
19 | export default actions
20 |
--------------------------------------------------------------------------------
/src/features/assets/components/List.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { BaseList, TableList } from 'features/shared/components'
3 | import ListItem from './ListItem'
4 | import { withNamespaces } from 'react-i18next'
5 |
6 | const type = 'asset'
7 |
8 | const mapStateToProps = (state, props) => {
9 | return {
10 | ...BaseList.mapStateToProps(type, ListItem, {
11 | wrapperComponent: TableList,
12 | wrapperProps: {
13 | titles: props.t('asset.formTitle', { returnObjects: true })
14 | }
15 | })(state)
16 | }
17 | }
18 |
19 | export default withNamespaces('translations') (BaseList.connect(
20 | mapStateToProps,
21 | BaseList.mapDispatchToProps(type)
22 | ))
23 |
--------------------------------------------------------------------------------
/src/features/assets/components/ListItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router'
3 | import {withNamespaces} from 'react-i18next'
4 |
5 | class ListItem extends React.Component {
6 | render() {
7 | const {item, t} = this.props
8 |
9 | return(
10 |
11 | {item.alias || '-'} |
12 | {item.id} |
13 |
14 |
15 | {t('commonWords.viewDetails')} →
16 |
17 | |
18 |
19 | )
20 | }
21 | }
22 |
23 | export default withNamespaces('translations') ( ListItem)
24 |
--------------------------------------------------------------------------------
/src/features/assets/components/New.scss:
--------------------------------------------------------------------------------
1 | .subjectField {
2 | display: flex;
3 | align-items: flex-start;
4 |
5 | :global {
6 | .form-group {
7 | flex: 1;
8 | margin-right: $gutter-size/2;
9 | }
10 | }
11 |
12 | button{
13 | margin-top: 35px;
14 | }
15 | }
16 |
17 | .receiverBtn{
18 | width: 25px;
19 | height: 25px;
20 | font-size: 22px;
21 | line-height: 20px;
22 | }
23 |
24 | .panel{
25 | padding: $gutter-size;
26 | border: solid 1px $border-color;
27 | }
--------------------------------------------------------------------------------
/src/features/assets/components/index.js:
--------------------------------------------------------------------------------
1 | import List from './List'
2 | import New from './New'
3 | import AssetShow from './AssetShow'
4 | import AssetUpdate from './AssetUpdate'
5 |
6 | export {
7 | List,
8 | New,
9 | AssetShow,
10 | AssetUpdate
11 | }
12 |
--------------------------------------------------------------------------------
/src/features/assets/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import reducers from './reducers'
3 | import routes from './routes'
4 |
5 | export {
6 | actions,
7 | reducers,
8 | routes
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/assets/reducers.js:
--------------------------------------------------------------------------------
1 | import { reducers } from 'features/shared'
2 | import { combineReducers } from 'redux'
3 |
4 | const type = 'asset'
5 |
6 | export default combineReducers({
7 | items: reducers.itemsReducer(type),
8 | queries: reducers.queriesReducer(type),
9 | autocompleteIsLoaded: reducers.autocompleteIsLoadedReducer(type),
10 | })
11 |
--------------------------------------------------------------------------------
/src/features/assets/routes.js:
--------------------------------------------------------------------------------
1 | import { List, New, AssetShow, AssetUpdate } from './components'
2 | import { makeRoutes } from 'features/shared'
3 |
4 | export default (store) => {
5 | const routes = makeRoutes(store, 'asset', List, New, AssetShow)
6 | routes.childRoutes.push({
7 | path: ':id/alias',
8 | component: AssetUpdate
9 | })
10 |
11 | return routes
12 | }
13 |
--------------------------------------------------------------------------------
/src/features/backup/actions.js:
--------------------------------------------------------------------------------
1 | import { chainClient } from 'utility/environment'
2 |
3 | let actions = {
4 | backup:()=>{
5 | return function(dispatch) {
6 | return chainClient().backUp.backup()
7 | .then(resp => {
8 | if (resp.status === 'fail') {
9 | throw resp
10 | }
11 | const date = new Date()
12 | const dateStr = date.toLocaleDateString().split(' ')[0]
13 | const timestamp = date.getTime()
14 | const fileName = ['bytom-wallet-backup-', dateStr, timestamp].join('-')
15 |
16 | let element = document.createElement('a')
17 | element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(resp.data)))
18 | element.setAttribute('download', fileName)
19 | element.style.display = 'none'
20 | document.body.appendChild(element)
21 | element.click()
22 |
23 | document.body.removeChild(element)
24 |
25 | }).catch((err) => {
26 | throw err
27 | })
28 | }
29 | },
30 |
31 | success: ()=>{
32 | return (dispatch) => {
33 | dispatch( { type: 'HIDE_MODAL' })
34 | dispatch({type: 'RESTORE_SUCCESS'})
35 | }
36 | }
37 |
38 | }
39 |
40 | export default actions
41 |
--------------------------------------------------------------------------------
/src/features/backup/components/index.js:
--------------------------------------------------------------------------------
1 |
2 | import Backup from './Backup'
3 |
4 | export {
5 | Backup
6 | }
7 |
--------------------------------------------------------------------------------
/src/features/backup/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import routes from './routes'
3 |
4 | export {
5 | actions,
6 | routes,
7 | }
8 |
--------------------------------------------------------------------------------
/src/features/backup/routes.js:
--------------------------------------------------------------------------------
1 | import { RoutingContainer } from 'features/shared/components'
2 | import { Backup } from './components'
3 |
4 | export default {
5 | path: 'backup',
6 | component: RoutingContainer,
7 | indexRoute: { component: Backup }
8 | }
9 |
--------------------------------------------------------------------------------
/src/features/balances/actions.js:
--------------------------------------------------------------------------------
1 | import { baseListActions } from 'features/shared/actions'
2 | import { chainClient } from 'utility/environment'
3 | import {push} from 'react-router-redux'
4 |
5 | const updateInfo = (param) => ({type: 'UPDATE_WALLET_INFO', param})
6 |
7 | const rescan = () => {
8 | return (dispatch) => {
9 | return chainClient().backUp.rescan()
10 | .then((resp) => {
11 | if (resp.status === 'fail') {
12 | dispatch({type: 'ERROR', payload: {'message': resp.msg}})
13 | } else {
14 | dispatch({type: 'START_RESCAN'})
15 | }
16 | })
17 | .catch(err => {
18 | throw {_error: err}
19 | })
20 | }
21 | }
22 |
23 | const walletInfo = () => {
24 | return (dispatch) => {
25 | return chainClient().backUp.info()
26 | .then((info) => {
27 | if (info.status === 'fail') {
28 | dispatch({type: 'ERROR', payload: {'message': info.msg}})
29 | } else {
30 | if (info.data.bestBlockHeight === info.data.walletHeight) {
31 | dispatch({type: 'STOP_RESCAN'})
32 | dispatch( push('/balances') )
33 | }else{
34 | dispatch(updateInfo(info.data))
35 | }
36 | }
37 | })
38 | .catch((err) => {
39 | dispatch({type: 'ERROR', payload: err})
40 | })
41 | }
42 | }
43 |
44 | let actions = {
45 | updateInfo,
46 | rescan,
47 | walletInfo,
48 | ...baseListActions('balance')
49 | }
50 | export default actions
51 |
--------------------------------------------------------------------------------
/src/features/balances/components/ListItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { KeyValueTable } from 'features/shared/components'
3 | import { buildBalanceDisplay } from 'utility/buildInOutDisplay'
4 | import {withNamespaces} from 'react-i18next'
5 |
6 | class ListItem extends React.Component {
7 | render() {
8 | return
9 | }
10 | }
11 |
12 | export default withNamespaces('translations') (ListItem)
13 |
--------------------------------------------------------------------------------
/src/features/balances/components/RescanDialog/RescanDialog.scss:
--------------------------------------------------------------------------------
1 | .title{
2 | text-align: center;
3 | }
4 |
5 | .submitBtn{
6 | display: flex;
7 | justify-content: center;
8 | }
9 |
10 | .infoContainer{
11 | word-break: break-word;
12 | }
13 |
--------------------------------------------------------------------------------
/src/features/balances/components/index.js:
--------------------------------------------------------------------------------
1 | import List from './List'
2 |
3 | export {
4 | List,
5 | }
6 |
--------------------------------------------------------------------------------
/src/features/balances/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import reducers from './reducers'
3 | import routes from './routes'
4 |
5 | export {
6 | actions,
7 | reducers,
8 | routes
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/balances/reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 |
3 | const itemsReducer = (state = {}, action) => {
4 | if (action.type == 'APPEND_BALANCE_PAGE') {
5 | const newState = {}
6 | action.param.data.forEach((item, index) => {
7 | const id = `balance-${index}`
8 | newState[id] = {
9 | id: `balance-${index}`,
10 | ...item
11 | }
12 | })
13 |
14 | return newState
15 | }
16 | return state
17 | }
18 |
19 | const queriesReducer = (state = {}, action) => {
20 | if (action.type == 'APPEND_BALANCE_PAGE') {
21 | return {
22 | loadedOnce: true
23 | }
24 | }
25 |
26 | return state
27 | }
28 |
29 | const rescanProgress = (state = {}, action) => {
30 | if (action.type == 'UPDATE_WALLET_INFO') {
31 | return action.param
32 | }
33 | return state
34 | }
35 |
36 |
37 | const rescanning = (state = {}, action) => {
38 | if (action.type == 'START_RESCAN') return true
39 | else if (action.type == 'STOP_RESCAN') return false
40 | return state
41 | }
42 |
43 |
44 | export default combineReducers({
45 | items: itemsReducer,
46 | queries: queriesReducer,
47 | rescanning,
48 | rescanProgress
49 | })
50 |
--------------------------------------------------------------------------------
/src/features/balances/routes.js:
--------------------------------------------------------------------------------
1 | import { List } from './components'
2 | import { makeRoutes } from 'features/shared'
3 |
4 | export default (store) => makeRoutes(store, 'balance', List)
5 |
--------------------------------------------------------------------------------
/src/features/configuration/actions.js:
--------------------------------------------------------------------------------
1 | import { chainClient } from 'utility/environment'
2 | import { actions as coreActions } from 'features/core'
3 | import { fetchTestnetInfo } from 'features/testnet/actions'
4 |
5 | const retry = (dispatch, promise, count = 10) => {
6 | return dispatch(promise).catch((err) => {
7 | var currentTime = new Date().getTime()
8 | while (currentTime + 200 >= new Date().getTime()) { /* wait for retry */ }
9 |
10 | if (count >= 1) {
11 | retry(dispatch, promise, count - 1)
12 | } else {
13 | throw(err)
14 | }
15 | })
16 | }
17 |
18 | let actions = {
19 | submitConfiguration: (data) => {
20 | const configureWithRetry = (dispatch, config) => {
21 | return chainClient().config.configure(config)
22 | .then(() => retry(dispatch, coreActions.fetchCoreInfo({throw: true})))
23 | }
24 |
25 | return (dispatch) => {
26 | if (data.type == 'testnet') {
27 | return dispatch(fetchTestnetInfo()).then(testnet =>
28 | configureWithRetry(dispatch, testnet))
29 | } else {
30 | if (data.type == 'new') {
31 | data = {
32 | isGenerator: true,
33 | isSigner: true,
34 | quorum: 1,
35 | }
36 | }
37 |
38 | delete data.type
39 | return configureWithRetry(dispatch, data)
40 | }
41 | }
42 | }
43 | }
44 |
45 | export default actions
46 |
--------------------------------------------------------------------------------
/src/features/configuration/components/index.js:
--------------------------------------------------------------------------------
1 | import Index from './Index/Index'
2 |
3 | export {
4 | Index
5 | }
6 |
--------------------------------------------------------------------------------
/src/features/configuration/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import routes from './routes'
3 |
4 | export {
5 | actions,
6 | routes,
7 | }
8 |
--------------------------------------------------------------------------------
/src/features/configuration/routes.js:
--------------------------------------------------------------------------------
1 | import { RoutingContainer } from 'features/shared/components'
2 | import { Index } from './components'
3 |
4 | export default {
5 | path: 'configuration',
6 | component: RoutingContainer,
7 | indexRoute: { component: Index }
8 | }
9 |
--------------------------------------------------------------------------------
/src/features/core/actions.js:
--------------------------------------------------------------------------------
1 | import { chainClient } from 'utility/environment'
2 |
3 | const updateInfo = (param) => ({type: 'UPDATE_CORE_INFO', param})
4 | const setClientToken = (token) => ({type: 'SET_CLIENT_TOKEN', token})
5 | const clearSession = ({ type: 'USER_LOG_OUT' })
6 | const updateBTMAmountUnit = (param) => ({type: 'UPDATE_BTM_AMOUNT_UNIT', param})
7 |
8 | const updateMiningState = (param) => {
9 | return (dispatch) => {
10 | return chainClient().config.mining(param)
11 | .then(() => {
12 | dispatch({type: 'UPDATE_MINING_STATE', param})
13 | })
14 | .catch((err) => {
15 | if (!err.status) {
16 | throw err
17 | }
18 | })
19 | }
20 | }
21 |
22 | const fetchCoreInfo = (options = {}) => {
23 | return (dispatch) => {
24 | return chainClient().config.info()
25 | .then((info) => {
26 | dispatch(updateInfo(info))
27 | })
28 | .catch((err) => {
29 | if (options.throw || !err.status) {
30 | throw err
31 | } else {
32 | if (err.status == 401) {
33 | dispatch({type: 'ERROR', payload: err})
34 | } else {
35 | dispatch({type: 'CORE_DISCONNECT'})
36 | }
37 | }
38 | })
39 | }
40 | }
41 |
42 |
43 | let actions = {
44 | setClientToken,
45 | updateInfo,
46 | updateBTMAmountUnit,
47 | updateMiningState,
48 | fetchCoreInfo,
49 | clearSession,
50 | logIn: (token) => (dispatch) => {
51 | dispatch(setClientToken(token))
52 | return dispatch(fetchCoreInfo({throw: true}))
53 | .then(() => dispatch({type: 'USER_LOG_IN'}))
54 | }
55 | }
56 |
57 | export default actions
58 |
--------------------------------------------------------------------------------
/src/features/core/components/index.js:
--------------------------------------------------------------------------------
1 | import CoreIndex from './CoreIndex/CoreIndex'
2 |
3 | export {
4 | CoreIndex,
5 | }
6 |
--------------------------------------------------------------------------------
/src/features/core/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import reducers from './reducers'
3 | import routes from './routes'
4 |
5 | export {
6 | actions,
7 | reducers,
8 | routes,
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/core/routes.js:
--------------------------------------------------------------------------------
1 | import { RoutingContainer } from 'features/shared/components'
2 | import { CoreIndex } from './components'
3 |
4 | export default {
5 | path: 'core',
6 | component: RoutingContainer,
7 | indexRoute: { component: CoreIndex }
8 | }
9 |
--------------------------------------------------------------------------------
/src/features/form/reducers.js:
--------------------------------------------------------------------------------
1 | import { reducer as form } from 'redux-form'
2 |
3 | export default form.normalize({
4 | newAssetForm: { // <--- name of the form
5 | symbol: value => value && value.toUpperCase(), // normalizer for 'upper' field
6 | }
7 | })
--------------------------------------------------------------------------------
/src/features/initialization/components/FormIndex.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | background: $background-inverse-color;
3 | position: absolute;
4 | top: 0;
5 | bottom: 0;
6 | left: 0;
7 | right: 0;
8 | padding-top: 50px;
9 | display: block;
10 | overflow: auto;
11 | }
12 |
13 | .title{
14 | text-align: center;
15 | color: white;
16 | }
17 |
18 | .formWarpper {
19 | display: flex;
20 | justify-content: space-around;
21 |
22 | margin-top: 30px;
23 | }
24 |
25 | .form {
26 | background: $background-color;
27 | border-radius: $border-radius-standard;
28 | width: 500px;
29 | padding: 30px;
30 | }
31 |
--------------------------------------------------------------------------------
/src/features/initialization/components/Keystore/Keystore.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {RestoreKeystore} from 'features/shared/components'
3 | import {withNamespaces} from 'react-i18next'
4 | import { Link } from 'react-router'
5 | import {connect} from 'react-redux'
6 | import actions from 'actions'
7 | import styles from '../FormIndex.scss'
8 |
9 | class Keystore extends React.Component {
10 | constructor(props) {
11 | super(props)
12 | }
13 |
14 | render() {
15 | const t = this.props.t
16 |
17 | return (
18 |
19 |
20 |
{t('backup.restoreKeystore')}
21 |
22 |
23 |
24 |
27 | {t('commonWords.cancel')}
28 |
29 |
30 |
31 |
32 |
33 | )
34 | }
35 | }
36 |
37 |
38 | export default withNamespaces('translations') (connect(
39 | () => ({}),
40 | (dispatch) => ({
41 | success: () => dispatch(actions.initialization.initSucceeded()),
42 | })
43 | )(Keystore))
44 |
--------------------------------------------------------------------------------
/src/features/initialization/components/Mnemonic/Mnemonic.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {RestoreMnemonic} from 'features/shared/components'
3 | import {withNamespaces} from 'react-i18next'
4 | import { Link } from 'react-router'
5 | import {connect} from 'react-redux'
6 | import actions from 'actions'
7 | import styles from '../FormIndex.scss'
8 |
9 | class Mnemonic extends React.Component {
10 | constructor(props) {
11 | super(props)
12 | }
13 |
14 | render() {
15 | const t = this.props.t
16 | return (
17 |
18 |
19 |
{t('backup.restoreMnemonic')}
20 |
21 |
22 |
23 |
26 | {t('commonWords.cancel')}
27 |
28 |
29 |
30 |
31 |
32 | )
33 | }
34 | }
35 |
36 |
37 |
38 | export default withNamespaces('translations') (connect(
39 | () => ({}),
40 | (dispatch) => ({
41 | success: () => dispatch(actions.initialization.initSucceeded()),
42 | })
43 | )(Mnemonic))
44 |
--------------------------------------------------------------------------------
/src/features/initialization/components/MnemonicStepper/MnemonicStepper.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | background: $background-inverse-color;
3 | position: absolute;
4 | top: 0;
5 | bottom: 0;
6 | left: 0;
7 | right: 0;
8 | padding-top: 50px;
9 | display: block;
10 | overflow: auto;
11 | }
12 |
13 | .title{
14 | text-align: center;
15 | color: white;
16 | }
17 |
18 | .formWarpper {
19 | display: flex;
20 | justify-content: space-around;
21 |
22 | margin-top: 30px;
23 | }
24 |
25 | .form {
26 | background: $background-color;
27 | border-radius: $border-radius-standard;
28 | width: 570px;
29 | padding: 30px;
30 | padding-bottom: $gutter-size*1.25;
31 | }
32 |
--------------------------------------------------------------------------------
/src/features/initialization/components/index.js:
--------------------------------------------------------------------------------
1 | import Index from './Index/Index'
2 | import Register from './Register/Register'
3 | import MnemonicStepper from './MnemonicStepper/MnemonicStepper'
4 | import Mnemonic from './Mnemonic/Mnemonic'
5 | import Keystore from './Keystore/Keystore'
6 |
7 | export {
8 | Index,
9 | Register,
10 | Mnemonic,
11 | Keystore,
12 | MnemonicStepper,
13 | }
14 |
--------------------------------------------------------------------------------
/src/features/initialization/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import reducers from './reducers'
3 | import routes from './routes'
4 |
5 | export {
6 | actions,
7 | reducers,
8 | routes
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/initialization/reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 |
3 | const mnemonic = (state = [], action) => {
4 | if (action.type == 'INIT_ACCOUNT') {
5 | return action.data
6 | }
7 | return state
8 | }
9 |
10 | export default combineReducers({
11 | mnemonic
12 | })
13 |
--------------------------------------------------------------------------------
/src/features/initialization/routes.js:
--------------------------------------------------------------------------------
1 | import { RoutingContainer } from 'features/shared/components'
2 | import { Index, Register, Restore, Mnemonic, Keystore, MnemonicStepper } from './components'
3 |
4 | export default {
5 | path: 'initialization',
6 | component: RoutingContainer,
7 | indexRoute: { component: Index },
8 | childRoutes: [
9 | {
10 | path: 'register',
11 | component: Register
12 | },
13 | {
14 | path: 'mnemonic',
15 | component: MnemonicStepper
16 | },
17 | {
18 | path: 'restore',
19 | component: Restore
20 | },
21 | {
22 | path: 'restoreMnemonic',
23 | component: Mnemonic
24 | },
25 | {
26 | path: 'restoreKeystore',
27 | component: Keystore
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/src/features/mockhsm/components/CheckPassword/CheckPassword.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | margin: $gutter-size auto;
3 | border-radius: $border-radius-standard;
4 | background-color: $background-content-color;
5 | padding: $gutter-size/2;
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/src/features/mockhsm/components/ExportKey/ExportKey.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | margin: $gutter-size auto;
3 | border-radius: $border-radius-standard;
4 | background-color: $background-content-color;
5 | padding: $gutter-size/2;
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/src/features/mockhsm/components/List.jsx:
--------------------------------------------------------------------------------
1 | import { BaseList, TableList } from 'features/shared/components'
2 | import ListItem from './ListItem'
3 | import {withNamespaces} from 'react-i18next'
4 |
5 | const type = 'key'
6 |
7 | class KeyList extends BaseList.ItemList {
8 | }
9 |
10 | const mapStateToProps = (state, props) => {
11 | return {
12 | ...BaseList.mapStateToProps(type, ListItem, {
13 | skipQuery: true,
14 | label: 'Keys',
15 | wrapperComponent: TableList,
16 | wrapperProps: {
17 | titles: props.t('key.formTitle', { returnObjects: true })
18 | }
19 | })(state)
20 | }
21 | }
22 |
23 | export default withNamespaces('translations') (BaseList.connect(
24 | mapStateToProps,
25 | BaseList.mapDispatchToProps(type),
26 | KeyList
27 | ))
28 |
--------------------------------------------------------------------------------
/src/features/mockhsm/components/ListItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router'
3 | import {withNamespaces} from 'react-i18next'
4 |
5 | class ListItem extends React.Component {
6 | constructor(props) {
7 | super(props)
8 | }
9 |
10 | render() {
11 | const {item, t} = this.props
12 |
13 | return(
14 |
15 | {item.alias} |
16 | {item.xpub} |
17 |
18 | {t('form.more')}
19 | |
20 |
21 | )
22 | }
23 | }
24 |
25 | export default withNamespaces('translations') (ListItem)
26 |
--------------------------------------------------------------------------------
/src/features/mockhsm/components/MnemonicStepper/MnemonicStepper.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | background: $background-color;
3 | display: flex;
4 | flex-direction: row;
5 | padding: 0 $gutter-size;
6 | margin-top: $title-height;
7 | }
8 |
9 | .content {
10 | min-width: 400px;
11 | max-width: 900px;
12 | width: 100%;
13 | margin: 0 auto;
14 | padding: $gutter-size;
15 | }
16 |
17 |
18 | .marginLeft{
19 | margin-left: $gutter-size/2;
20 | }
--------------------------------------------------------------------------------
/src/features/mockhsm/components/ResetPassword/ResetPassword.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | margin: $gutter-size auto;
3 | border-radius: $border-radius-standard;
4 | background-color: $background-content-color;
5 | padding: $gutter-size/2;
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/src/features/mockhsm/components/index.js:
--------------------------------------------------------------------------------
1 | import List from './List'
2 | import New from './New'
3 | import MnemonicStepper from './MnemonicStepper/MnemonicStepper'
4 | import Show from './Show'
5 | import ResetPassword from './ResetPassword/ResetPassword'
6 | import CheckPassword from './CheckPassword/CheckPassword'
7 | import KeyUpdate from './KeyUpdate'
8 |
9 | export {
10 | List,
11 | New,
12 | MnemonicStepper,
13 | Show,
14 | ResetPassword,
15 | CheckPassword,
16 | KeyUpdate
17 | }
18 |
--------------------------------------------------------------------------------
/src/features/mockhsm/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import reducers from './reducers'
3 | import routes from './routes'
4 |
5 | export {
6 | actions,
7 | reducers,
8 | routes,
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/mockhsm/reducers.js:
--------------------------------------------------------------------------------
1 | import { reducers } from 'features/shared'
2 | import { combineReducers } from 'redux'
3 |
4 | const type = 'key'
5 | const idFunc = item => item.xpub
6 |
7 | export default combineReducers({
8 | items: reducers.itemsReducer(type, idFunc),
9 | queries: reducers.queriesReducer(type, idFunc),
10 | autocompleteIsLoaded: reducers.autocompleteIsLoadedReducer(type),
11 | success: (state = '', action) => {
12 | if (action.type == 'KEY_PASSWORD_SUCCESS') {
13 | return 'F_BTM002'
14 | }
15 | if(action.type == 'redux-form/CHANGE'){
16 | return ''
17 | }
18 | return state
19 | },
20 | mnemonic: (state = [], action) => {
21 | if (action.type == 'NEW_KEY') {
22 | return action.data
23 | }
24 | return state
25 | },
26 | })
27 |
--------------------------------------------------------------------------------
/src/features/mockhsm/routes.js:
--------------------------------------------------------------------------------
1 | import { List, New, Show, ResetPassword, CheckPassword, MnemonicStepper, KeyUpdate } from './components'
2 | import { makeRoutes } from 'features/shared'
3 |
4 | export default (store) => makeRoutes(store, 'key', List, New, Show,
5 | {
6 | childRoutes: [
7 | {
8 | path: ':id/reset-password',
9 | component: ResetPassword,
10 | },
11 | {
12 | path: ':id/check-password',
13 | component: CheckPassword,
14 | },
15 | {
16 | path: 'mnemonic',
17 | component: MnemonicStepper
18 | },
19 | {
20 | path:':id/alias',
21 | component: KeyUpdate
22 | }
23 | ],skipFilter: true, name: 'key' })
--------------------------------------------------------------------------------
/src/features/peers/actions.js:
--------------------------------------------------------------------------------
1 | import { baseListActions } from 'features/shared/actions'
2 | import { chainClient } from 'utility/environment'
3 | import {push} from 'react-router-redux'
4 |
5 | const disconnect = (id) => {
6 | return (dispatch) => {
7 | return chainClient().peers.disconnect({peer_id: id})
8 | .then((resp) => {
9 | if(resp.status == 'fail'){
10 | dispatch({type: 'ERROR', payload: { 'message': resp.msg}})
11 | }
12 | })
13 | .catch((err) => {
14 | if (!err.status) {
15 | dispatch({type: 'ERROR', payload: { 'message': err}})
16 | }
17 | })
18 | }
19 | }
20 |
21 | let actions = {
22 | disconnect,
23 | ...baseListActions('peer')
24 | }
25 |
26 | export default actions
27 |
--------------------------------------------------------------------------------
/src/features/peers/components/List.jsx:
--------------------------------------------------------------------------------
1 | import { BaseList, TableList } from 'features/shared/components'
2 | import ListItem from './ListItem'
3 | import { withNamespaces } from 'react-i18next'
4 | import styles from './List.scss'
5 | import { actions } from 'features/peers'
6 | import React from 'react'
7 |
8 | const type = 'peer'
9 |
10 | class List extends React.Component {
11 | componentWillReceiveProps(nextProps) {
12 | if (nextProps.peerCount != this.props.peerCount) {
13 | this.props.getLatest()
14 | }
15 | }
16 | render() {
17 | const ItemList = BaseList.ItemList
18 | return ()
19 | }
20 | }
21 |
22 |
23 | const mapStateToProps = (state, props) => {
24 | return {
25 | peerCount: state.core.peerCount,
26 | skipCreate: true,
27 | ...BaseList.mapStateToProps(type, ListItem, {
28 | wrapperComponent: TableList,
29 | wrapperProps: {
30 | titles: props.t('peers.formTitle', { returnObjects: true }),
31 | styles: styles.main
32 | }
33 | })(state)
34 | }
35 | }
36 |
37 | const mapDispatchToProps = ( dispatch ) => ({
38 | itemActions: {
39 | disconnect: (id) => dispatch(actions.disconnect(id))
40 | },
41 | getLatest: () => dispatch(actions.fetchAll()),
42 | ...BaseList.mapDispatchToProps(type),
43 | })
44 |
45 | export default withNamespaces('translations') (BaseList.connect(
46 | mapStateToProps,
47 | mapDispatchToProps,
48 | List
49 | ))
50 |
--------------------------------------------------------------------------------
/src/features/peers/components/List.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | th, td {
3 | &:first-child {
4 | padding-left: $gutter-size;
5 | width: 33%;
6 | }
7 |
8 | &:last-child {
9 | width: 100px;
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/features/peers/components/ListItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {withNamespaces} from 'react-i18next'
3 | import { formatBytes } from 'utility/math'
4 |
5 | class ListItem extends React.Component {
6 | render() {
7 | const {item, t} = this.props
8 |
9 | return(
10 |
11 | {item.remoteAddr || '-'} |
12 | {item.height} |
13 | {item.ping} |
14 | {item.duration} |
15 | { formatBytes(item.totalSent+ item.totalReceived, 0) } |
16 |
17 |
20 | |
21 |
22 | )
23 | }
24 | }
25 |
26 | export default withNamespaces('translations') (ListItem)
27 |
--------------------------------------------------------------------------------
/src/features/peers/components/index.js:
--------------------------------------------------------------------------------
1 | import List from './List'
2 |
3 | export {
4 | List,
5 | }
6 |
--------------------------------------------------------------------------------
/src/features/peers/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import reducers from './reducers'
3 | import routes from './routes'
4 |
5 | export {
6 | actions,
7 | reducers,
8 | routes,
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/peers/reducers.js:
--------------------------------------------------------------------------------
1 | import { reducers } from 'features/shared'
2 | import { combineReducers } from 'redux'
3 |
4 | const type = 'peer'
5 |
6 | const itemsReducer = (state = {}, action) => {
7 | if (action.type == 'RECEIVED_PEER_ITEMS') {
8 | return action.param.data
9 | }
10 | return state
11 | }
12 |
13 | export default combineReducers({
14 | items: itemsReducer,
15 | queries: reducers.queriesReducer(type)
16 | })
17 |
--------------------------------------------------------------------------------
/src/features/peers/routes.js:
--------------------------------------------------------------------------------
1 | // import { RoutingContainer } from 'features/shared/components'
2 | // import { PeerIndex } from './components'
3 | // import { List } from './components'
4 | //
5 | //
6 | // export default {
7 | // path: 'peers',
8 | // component: RoutingContainer,
9 | // indexRoute: { component: List }
10 | // }
11 | //
12 |
13 | import { List } from './components'
14 | import { makeRoutes } from 'features/shared'
15 |
16 | export default (store) => makeRoutes(store, 'peer', List)
17 |
--------------------------------------------------------------------------------
/src/features/shared/actions/index.js:
--------------------------------------------------------------------------------
1 | import baseCreateActions from './create'
2 | import baseUpdateActions from './update'
3 | import baseListActions from './list'
4 |
5 | export {
6 | baseCreateActions,
7 | baseUpdateActions,
8 | baseListActions,
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/shared/actions/update.js:
--------------------------------------------------------------------------------
1 | import { chainClient } from 'utility/environment'
2 | import { push } from 'react-router-redux'
3 |
4 | export default function(type, options = {}) {
5 | const updated = (param) => ({ type: `UPDATED_${type.toUpperCase()}`, param })
6 |
7 | return {
8 | updated,
9 | submitUpdateForm: (data, id) => {
10 | const clientApi = options.clientApi ? options.clientApi() : chainClient()[`${type}s`]
11 | let promise = Promise.resolve()
12 |
13 | return function(dispatch) {
14 | return promise.then(() => clientApi.updateAlias({
15 | id: id,
16 | alias: data.alias,
17 | }).then((resp) => {
18 | if (resp.status === 'fail') {
19 | throw resp
20 | }
21 | dispatch(updated(resp))
22 |
23 | dispatch(push({
24 | pathname: `/${type}s/${id}`,
25 | state: {
26 | preserveFlash: true
27 | }
28 | }))
29 | }))
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/features/shared/components/AmountField/AmountField.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {AmountInputMask, AmountUnitField } from '../'
3 |
4 | class AmountField extends React.Component {
5 | constructor(props) {
6 | super(props)
7 | }
8 |
9 | render() {
10 | const isBTM = this.props.isBTM
11 |
12 | return(isBTM ?
13 |
17 | :
18 |
23 | )
24 | }
25 | }
26 |
27 | export default AmountField
--------------------------------------------------------------------------------
/src/features/shared/components/AmountField/AmountField.scss:
--------------------------------------------------------------------------------
1 | .base {
2 | background: red;
3 | }
4 |
5 | .unit{
6 | & > div{
7 | z-index: unset !important;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/shared/components/AmountUnitField/AmountUnitField.scss:
--------------------------------------------------------------------------------
1 | .base {
2 | background: red;
3 | }
4 |
5 | .unit{
6 | & > div{
7 | z-index: unset !important;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/shared/components/Autocomplete/AccountAlias.jsx:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import AutocompleteField, {mapStateToProps, mapDispatchToProps} from './AutocompleteField'
3 |
4 | const type = 'account'
5 |
6 | export default connect(
7 | mapStateToProps(type),
8 | mapDispatchToProps(type)
9 | )(AutocompleteField)
10 |
--------------------------------------------------------------------------------
/src/features/shared/components/Autocomplete/AssetAlias.jsx:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import AutocompleteField, {mapStateToProps, mapDispatchToProps} from './AutocompleteField'
3 |
4 | const type = 'asset'
5 |
6 | export default connect(
7 | mapStateToProps(type),
8 | mapDispatchToProps(type)
9 | )(AutocompleteField)
10 |
--------------------------------------------------------------------------------
/src/features/shared/components/Autocomplete/AutocompleteField.scss:
--------------------------------------------------------------------------------
1 | .container {
2 | position: relative;
3 | }
4 |
5 | .suggestionsContainer{
6 | display: none;
7 | }
8 |
9 | .suggestionsContainerOpen {
10 | background: $body-bg;
11 | border: 1px solid $input-border;
12 | border-radius: $input-border-radius-large;
13 | box-shadow: 0px 0px 4px transparentize($text-color, 0.5);
14 | list-style-type: none;
15 | margin-top: 6px;
16 | max-height: 200px;
17 | overflow-y: auto;
18 | padding: 0;
19 | position: absolute;
20 | top: 40px;
21 | width: 100%;
22 | z-index: 10;
23 | display: block;
24 | }
25 |
26 | .suggestionsList {
27 | margin: 0;
28 | padding: 0;
29 | list-style-type: none;
30 | }
31 |
32 | .suggestion {
33 | cursor: pointer;
34 | border-bottom: 1px solid $input-border;
35 |
36 | &:last-child {
37 | border: none;
38 | }
39 |
40 | div {
41 | padding: 0;
42 | }
43 |
44 | span {
45 | padding: 12px;
46 | display: inline-block;
47 | width: 100%;
48 | }
49 | }
50 |
51 | .suggestionHighlighted {
52 | color: $brand-primary;
53 | background: #e6e6e6;
54 | }
55 |
--------------------------------------------------------------------------------
/src/features/shared/components/Autocomplete/index.js:
--------------------------------------------------------------------------------
1 | import AccountAlias from './AccountAlias'
2 | import AssetAlias from './AssetAlias'
3 |
4 | export default {
5 | AccountAlias,
6 | AssetAlias
7 | }
8 |
--------------------------------------------------------------------------------
/src/features/shared/components/BaseList/EmptyList.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './EmptyList.scss'
3 | import componentClassNames from 'utility/componentClassNames'
4 | import { docsRoot } from 'utility/environment'
5 | import {withNamespaces} from 'react-i18next'
6 |
7 | class EmptyList extends React.Component {
8 | render() {
9 | const t =this.props.t
10 | let emptyImage
11 |
12 | try {
13 | emptyImage = require(`images/empty/${this.props.type}.svg`)
14 | } catch (err) { /* do nothing */ }
15 |
16 | let emptyBlock
17 | if (!this.props.loadedOnce) {
18 | emptyBlock = {t('emptyList.loading')}…
19 | } else if (this.props.showFirstTimeFlow) {
20 | emptyBlock =
21 |
22 | {t('emptyList.noRecord', {objectName: this.props.objectName})}
23 |
24 |
25 | }
26 |
27 | const classNames = [
28 | 'flex-container',
29 | styles.empty
30 | ]
31 |
32 | return (
33 |
34 | {emptyImage &&

}
35 | {emptyBlock}
36 |
37 | )
38 | }
39 | }
40 |
41 | EmptyList.propTypes = {
42 | type: React.PropTypes.string,
43 | objectName: React.PropTypes.string,
44 | newButton: React.PropTypes.object,
45 | loadedOnce: React.PropTypes.bool,
46 | showFirstTimeFlow: React.PropTypes.bool
47 | }
48 |
49 | export default withNamespaces('translations') (EmptyList)
50 |
--------------------------------------------------------------------------------
/src/features/shared/components/BaseList/EmptyList.scss:
--------------------------------------------------------------------------------
1 | .empty {
2 | text-align: center;
3 | display: flex;
4 | flex-direction: column;
5 | justify-content: flex-start;
6 | padding-top: 100px;
7 | margin-top: $title-height;
8 | }
9 |
10 | .emptyContainer {
11 | margin: 0 auto;
12 | width: 350px;
13 |
14 | .emptyContent {
15 | margin-top: $gutter-size;
16 | border: 1px solid $border-color;
17 | padding: $gutter-size/2;
18 | text-align: left;
19 | background: $background-color;
20 | }
21 |
22 | ol {
23 | padding-left: $gutter-size/2;
24 | margin-bottom: 0px;
25 | padding-top: 10px;
26 | padding-bottom: 10px;
27 | }
28 |
29 | li {
30 | padding-left: 6px;
31 | }
32 | }
33 |
34 | .emptyLabel {
35 | color: $text-strong-color;
36 | display: block;
37 | font-size: $font-size-page-title;
38 | padding-bottom: $gutter-size;
39 | width: 250px;
40 | margin: 0 auto;
41 | line-height: 26px;
42 | }
43 |
44 | .noResults {
45 | background: transparent;
46 | }
47 |
48 | .noResultsLabel {
49 | padding-bottom: 0;
50 | }
51 |
52 | .image {
53 | height: 70px;
54 | margin-bottom: $gutter-size;
55 | }
56 |
57 | .code {
58 | padding: 2px 6px;
59 | font-size: $font-size-section-title;
60 | }
61 |
--------------------------------------------------------------------------------
/src/features/shared/components/BaseNew.jsx:
--------------------------------------------------------------------------------
1 | import { connect as reduxConnect } from 'react-redux'
2 | import actions from 'actions'
3 |
4 | export const mapStateToProps = ( type ) => ( state ) => ({
5 | type: type,
6 | })
7 |
8 | export const mapDispatchToProps = (type) => (dispatch) => ({
9 | submitForm: (data) => {
10 | return dispatch(actions[type].submitForm(data)).then((resp) => {
11 | dispatch(actions.tutorial.submitTutorialForm(data, type))
12 | return resp
13 | })
14 | }
15 | })
16 |
17 | export const connect = (state, dispatch, element) => reduxConnect(
18 | state,
19 | dispatch
20 | )(element)
21 |
22 | export default {
23 | mapStateToProps,
24 | mapDispatchToProps,
25 | connect,
26 | }
27 |
--------------------------------------------------------------------------------
/src/features/shared/components/BaseShow.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import NotFound from './NotFound'
3 |
4 | export default class BaseShow extends React.Component {
5 | constructor(props) {
6 | super(props)
7 |
8 | this.state = {}
9 | }
10 |
11 | componentDidMount() {
12 | this.props.fetchItem(this.props.params.id).then(resp => {
13 | if (resp.status == 'fail') {
14 | this.setState({notFound: true})
15 | }
16 | })
17 | }
18 |
19 | renderIfFound(view) {
20 | if (this.state.notFound) {
21 | return()
22 | } else if (view) {
23 | return(view)
24 | } else {
25 | return(Loading...
)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/features/shared/components/BaseUpdate.jsx:
--------------------------------------------------------------------------------
1 | import { connect as reduxConnect } from 'react-redux'
2 | import actions from 'actions'
3 |
4 | export const mapStateToProps = ( type ) => ( /* state */ ) => ({
5 | type: type,
6 | })
7 |
8 | export const mapDispatchToProps = (type) => (dispatch) => ({
9 | fetchItem: (id) => dispatch(actions[type].fetchItems({id: `${id}`})).then((resp) => {
10 | return resp
11 | }),
12 | submitForm: (data, id) => dispatch(actions[type].submitUpdateForm(data, id))
13 | })
14 |
15 | export const connect = (state, dispatch, element) => reduxConnect(
16 | state,
17 | dispatch
18 | )(element)
19 |
20 | export default {
21 | mapStateToProps,
22 | mapDispatchToProps,
23 | connect,
24 | }
25 |
--------------------------------------------------------------------------------
/src/features/shared/components/CheckboxField/CheckboxField.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import pick from 'lodash/pick'
3 | import styles from './CheckboxField.scss'
4 |
5 | const CHECKBOX_FIELD_PROPS = [
6 | 'value',
7 | 'onBlur',
8 | 'onChange',
9 | 'onFocus',
10 | 'name',
11 | 'checked',
12 | 'disabled'
13 | ]
14 |
15 | class CheckboxField extends React.Component {
16 | render() {
17 | const fieldProps = pick(this.props.fieldProps, CHECKBOX_FIELD_PROPS)
18 |
19 | return (
20 |
21 |
27 |
28 | )
29 | }
30 | }
31 |
32 | export default CheckboxField
33 |
--------------------------------------------------------------------------------
/src/features/shared/components/CheckboxField/CheckboxField.scss:
--------------------------------------------------------------------------------
1 | .label {
2 | position: relative;
3 |
4 | .title {
5 | position: absolute;
6 | left: 20px;
7 | }
8 |
9 | user-select: none;
10 | }
11 |
12 | .hint {
13 | color: $text-light-color;
14 | margin-left: 20px;
15 | }
16 |
--------------------------------------------------------------------------------
/src/features/shared/components/ConfirmMnemonic/ConfirmMnemonic.scss:
--------------------------------------------------------------------------------
1 | .submit{
2 | float: left;
3 | }
4 |
5 | .seed{
6 | user-select: none;
7 | background-color: #F8F8F8;
8 | border-radius: 3px;
9 | border: 1px solid $border-color;
10 | padding: 6px 0px;
11 | color: $text-strong-color;
12 | }
13 |
14 | .seedWord{
15 | width: 22%;
16 | height: 36px;
17 | margin: 5px 10px 5px 0;
18 | &, input{
19 | text-align: center;
20 | }
21 | }
22 |
23 | .seedArea{
24 | display: flex;
25 | flex-wrap: wrap;
26 | margin-bottom: $gutter-size/2;
27 | }
28 |
29 | .container{
30 | width: 540px;
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/src/features/shared/components/ConsoleSection/ConsoleSection.scss:
--------------------------------------------------------------------------------
1 | .main{
2 | height: 100%;
3 | }
4 |
5 | .reactConsoleContainer {
6 | box-sizing: border-box;
7 | height: calc(100% - 121px);
8 | padding: 30px;
9 | overflow: scroll;
10 | background-color: $background-content-color;
11 | }
12 |
13 | .input {
14 | height: 65px;
15 | border: none;
16 | color: $text-strong-color;
17 | padding: 10px 12px;
18 | width: 750px;
19 | }
20 |
21 | .inputBox{
22 | display: flex;
23 | align-items: center;
24 | justify-content: center;
25 | border-top: solid 1px $border-color;
26 | }
27 |
--------------------------------------------------------------------------------
/src/features/shared/components/ConsoleSection/ListItem/ListItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './ListItem.scss'
3 |
4 | class ListItem extends React.Component {
5 | render(){
6 | let content = this.props.content
7 | let message = content.commandOutput
8 |
9 | if(content.success){
10 | if(message) {
11 | if(content.command === 'help'){
12 | let line = []
13 | message.forEach( (descriptionLine) => {
14 | line.push({descriptionLine}
)
15 | })
16 | message = line
17 | }else{
18 | const keys = Object.keys(message)
19 | if (keys.length === 1) {
20 | message = message[keys[0]].toString()
21 | }
22 | else{
23 | message = JSON.stringify(message, null, 2)
24 | }
25 | }
26 | }
27 | }else{
28 | message = message.replace(/"/g,'')
29 | }
30 |
31 | return (
32 |
33 |
{content.command}
34 | {message &&
{message}
}
35 |
36 | )
37 | }
38 | }
39 |
40 | export default ListItem
41 |
--------------------------------------------------------------------------------
/src/features/shared/components/ConsoleSection/ListItem/ListItem.scss:
--------------------------------------------------------------------------------
1 | .main{
2 | background-color: $background-color;
3 | border: 1px solid $border-color;
4 | margin-bottom: 30px;
5 | font-family: Nitti;
6 | }
7 |
8 | .title{
9 | border-bottom: 1px solid $border-color;
10 | padding: 10px;
11 | color: $text-strong-color;
12 | }
13 |
14 | .messageBox{
15 | padding: 10px;
16 | white-space: pre-wrap; /* CSS3 */
17 | white-space: -moz-pre-wrap; /* Firefox */
18 | white-space: -pre-wrap; /* Opera <7 */
19 | white-space: -o-pre-wrap; /* Opera 7 */
20 | word-wrap: break-word; /* IE */
21 | }
22 |
--------------------------------------------------------------------------------
/src/features/shared/components/ConsoleSection/command.json:
--------------------------------------------------------------------------------
1 | {
2 | "help": [
3 | "validate-address ",
4 | "sign-message ",
5 | "get-transaction ",
6 | "list-transactions",
7 | "build-transaction [{\"account_id\":id,\"amount\":amount,\"asset_id\": id,\"type\":type of transaction}…] ",
8 | "sign-transaction ",
9 | "estimate-transaction-gas ",
10 | "get-unconfirmed-transaction ",
11 | "list-unconfirmed-transactions",
12 | "decode-raw-transaction ",
13 | "get-block-count",
14 | "get-block-hash",
15 | "get-block ( | )",
16 | "get-block-header ( | )",
17 | "get-difficulty ( | )",
18 | "get-hash-rate ( | )",
19 | "net-info",
20 | "is-mining",
21 | "set-mining ",
22 | "gas-rate",
23 | "verify-message ",
24 | "decode-program "
25 | ]
26 | }
--------------------------------------------------------------------------------
/src/features/shared/components/CopyableBlock/CopyableBlock.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { copyToClipboard } from 'utility/clipboard'
3 | import styles from './CopyableBlock.scss'
4 | import {withNamespaces} from 'react-i18next'
5 |
6 | class CopyableBlock extends React.Component {
7 | copyClick() {
8 | copyToClipboard(this.props.value)
9 | }
10 |
11 | render() {
12 | return (
13 |
14 |
{this.props.value}
15 |
16 |
18 |
19 |
20 | )
21 | }
22 | }
23 |
24 | export default withNamespaces('translations') (CopyableBlock)
25 |
--------------------------------------------------------------------------------
/src/features/shared/components/CopyableBlock/CopyableBlock.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | margin: $gutter-size auto;
3 | border-radius: $border-radius-standard;
4 | background-color: $background-content-color;
5 | padding: $gutter-size/2;
6 | }
7 |
8 | .pre {
9 | padding: 0px !important;
10 | background-color: transparent !important;
11 | margin-bottom: 10px;
12 | }
13 |
14 | .copyButton {
15 | width: 100%;
16 | }
17 |
--------------------------------------------------------------------------------
/src/features/shared/components/EmptyContent/EmptyContent.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './EmptyContent.scss'
3 |
4 | class EmptyContent extends React.Component {
5 | render() {
6 |
7 | return (
8 |
9 | {this.props.children &&
10 | {this.props.children}
11 |
}
12 |
13 | )
14 | }
15 | }
16 |
17 | EmptyContent.propTypes = {
18 | title: React.PropTypes.string
19 | }
20 |
21 | export default EmptyContent
22 |
--------------------------------------------------------------------------------
/src/features/shared/components/EmptyContent/EmptyContent.scss:
--------------------------------------------------------------------------------
1 | .emptyContainer {
2 | margin: $gutter-size auto;
3 | width: 350px;
4 |
5 | .emptyContent {
6 | border: 1px solid $border-color;
7 | padding: $gutter-size/2;
8 | text-align: left;
9 | background: $background-color;
10 | }
11 |
12 | ol {
13 | padding-left: $gutter-size/2;
14 | margin-bottom: 0px;
15 | padding-top: 10px;
16 | padding-bottom: 10px;
17 | }
18 |
19 | li {
20 | padding-left: 6px;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/features/shared/components/ErrorBanner/ErrorBanner.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './ErrorBanner.scss'
3 | import {withNamespaces} from 'react-i18next'
4 |
5 | class ErrorBanner extends React.Component {
6 | render() {
7 | const {t, i18n} = this.props
8 | const error = this.props.error || ''
9 | const codeMessage = error.code && i18n.exists(`btmError.${error.code}`) && t(`btmError.${error.code}`)
10 | const success = this.props.success
11 | const message = codeMessage || error.chainMessage || error.message || error || success
12 |
13 | return (
14 |
15 | {this.props.title &&
{this.props.title}
}
16 |
17 | {message &&
18 |
19 | {message}
20 |
}
21 |
22 | {error.code &&
23 |
{t('commonWords.errorCode')} {error.code}
}
24 |
25 | {error.requestId &&
26 |
{t('commonWords.requestId')} {error.requestId}
}
27 |
28 | )
29 | }
30 | }
31 |
32 | export default withNamespaces('translations') (ErrorBanner)
33 |
--------------------------------------------------------------------------------
/src/features/shared/components/ErrorBanner/ErrorBanner.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | background: $highlight-danger-background;
3 | color: $highlight-danger;
4 | border: 1px solid $highlight-danger-border;
5 | border-radius: $border-radius-standard;
6 | padding: 20px;
7 | margin-bottom: 20px;
8 | word-wrap: break-word;
9 |
10 | a {
11 | color: $highlight-danger;
12 | text-decoration: underline;
13 | }
14 | }
15 |
16 | .mainSuccess {
17 | background: $success-background;
18 | color: $success;
19 | border: 1px solid $success-border;
20 | border-radius: $border-radius-standard;
21 | padding: 20px;
22 | margin-bottom: 20px;
23 | word-wrap: break-word;
24 |
25 | a {
26 | color: $success;
27 | text-decoration: underline;
28 | }
29 | }
30 |
31 | .message {
32 | margin-bottom: 15px;
33 | }
34 |
35 | .extra {
36 | line-height: 1.3;
37 | }
38 |
--------------------------------------------------------------------------------
/src/features/shared/components/FieldLabel/FieldLabel.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './FieldLabel.scss'
3 |
4 | class FieldLabel extends React.Component {
5 | render() {
6 | return ()
7 | }
8 | }
9 |
10 | export default FieldLabel
11 |
--------------------------------------------------------------------------------
/src/features/shared/components/FieldLabel/FieldLabel.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | font-size: $font-size-caps;
3 | text-transform: uppercase;
4 | }
5 |
--------------------------------------------------------------------------------
/src/features/shared/components/FileField.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { FieldLabel } from 'features/shared/components'
3 |
4 | class FileField extends React.Component {
5 | constructor(props) {
6 | super(props)
7 | this.onChange = this.onChange.bind(this)
8 | }
9 |
10 | onChange(e) {
11 | const { fieldProps: { onChange } } = this.props
12 | onChange(e.target.files[0])
13 | }
14 |
15 | render() {
16 |
17 | return(
18 |
19 | {this.props.title && {this.props.title}}
20 |
24 | {this.props.hint && {this.props.hint}}
25 |
26 | )
27 | }
28 | }
29 |
30 | export default FileField
--------------------------------------------------------------------------------
/src/features/shared/components/Flash/Flash.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | display: flex;
3 | position: relative;
4 | top: $title-height;
5 | }
6 |
7 | .alert {
8 | padding: 15px;
9 | border-bottom: 1px solid transparent;
10 | }
11 |
12 | .info {
13 | background-color: $alert-info-bg;
14 | border-color: $alert-info-border;
15 | color: $alert-info-text;
16 | }
17 |
18 | .success {
19 | background-color: $success-background;
20 | border-color: $success-border;
21 | color: $success;
22 | }
23 |
24 | .danger {
25 | background-color: $highlight-danger-background;
26 | border-color: $highlight-danger-border;
27 | color: $highlight-danger;
28 | }
29 |
30 | .content {
31 | width: 100%;
32 | flex: 1;
33 |
34 | p {
35 | margin: 0;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/features/shared/components/FormContainer/FormContainer.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | background: $background-color;
3 | display: flex;
4 | flex-direction: row;
5 | padding: 0 $gutter-size;
6 | margin-top: $title-height;
7 | }
8 |
9 | .content {
10 | min-width: 400px;
11 | width: 55%;
12 | margin: 0 auto;
13 | }
14 |
15 | .submit {
16 | text-align: right;
17 | }
18 |
--------------------------------------------------------------------------------
/src/features/shared/components/FormSection/FormSection.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './FormSection.scss'
3 |
4 | class FormSection extends React.Component {
5 | render() {
6 | return (
7 |
8 |
{this.props.title}
9 |
10 |
11 | {this.props.children}
12 |
13 |
14 | )
15 | }
16 | }
17 |
18 | export default FormSection
19 |
--------------------------------------------------------------------------------
/src/features/shared/components/FormSection/FormSection.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | padding-bottom: $gutter-size;
3 | border-bottom: 1px solid $border-color;
4 | margin: $gutter-size 0;
5 |
6 | &:first-child {
7 | margin-top: 0;
8 | padding-top: $gutter-size;
9 | }
10 |
11 | &:last-child {
12 | border: 0;
13 | margin-bottom: 0;
14 | padding-bottom: $gutter-size*2;
15 | }
16 | }
17 |
18 | .title {
19 | color: $text-strong-color;
20 | font-weight: 600;
21 | font-size: $font-size-section-title;
22 | text-transform: uppercase;
23 | margin-bottom: $gutter-size;
24 | }
25 |
--------------------------------------------------------------------------------
/src/features/shared/components/GasField/GasField.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import pick from 'lodash/pick'
3 | import { normalizeBTMAmountUnit } from 'utility/buildInOutDisplay'
4 | import { btmID } from 'utility/environment'
5 | import styles from './GasField.scss'
6 |
7 | const TEXT_FIELD_PROPS = [
8 | 'value',
9 | 'onBlur',
10 | 'onChange',
11 | 'onFocus',
12 | 'name'
13 | ]
14 |
15 | class GasField extends React.Component {
16 | constructor(props) {
17 | super(props)
18 | }
19 |
20 | render() {
21 | const fieldProps = pick(this.props.fieldProps, TEXT_FIELD_PROPS)
22 | const {touched, error} = this.props.fieldProps
23 | const chainGas = this.props.chainGas || 0
24 |
25 | return
26 | {normalizeBTMAmountUnit(btmID, ( chainGas + fieldProps.value * this.props.gas ), this.props.btmAmountUnit)}
27 |
33 |
34 | {touched && error && {error}}
35 | {this.props.hint && {this.props.hint}}
36 |
37 | }
38 | }
39 |
40 | export default GasField
41 |
--------------------------------------------------------------------------------
/src/features/shared/components/GasField/GasField.scss:
--------------------------------------------------------------------------------
1 | .slider{
2 | min-width: 340px;
3 | }
4 |
--------------------------------------------------------------------------------
/src/features/shared/components/HiddenField.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | class HiddenField extends React.Component {
4 | render() {
5 | return(
6 |
10 | )
11 | }
12 |
13 | }
14 |
15 | export default HiddenField
16 |
--------------------------------------------------------------------------------
/src/features/shared/components/JsonField/JsonField.scss:
--------------------------------------------------------------------------------
1 | .editorWrapper {
2 | border: 1px solid $input-border;
3 | border-radius: $input-border-radius-large;
4 | padding: 12px 8px; // ace-text-layer gets 4px default padding
5 | }
6 |
7 | .editorError {
8 | border-color: $brand-danger;
9 | }
10 |
11 | .errorBlock {
12 | color: $brand-danger;
13 | }
14 |
--------------------------------------------------------------------------------
/src/features/shared/components/KeyValueTable/KeyValueTable.scss:
--------------------------------------------------------------------------------
1 | .table {
2 | background: $background-color;
3 | table-layout: fixed;
4 | width: 100%;
5 |
6 | td {
7 | border-bottom: 1px solid $border-color;
8 | vertical-align: top;
9 | padding: 13px $gutter-size;
10 | line-height: 15px;
11 | }
12 |
13 | a .pre {
14 | color: $highlight-default;
15 | }
16 |
17 | tr:first-child td {
18 | padding-top: 20px;
19 | }
20 |
21 | tr:last-child td {
22 | border-bottom: none;
23 | padding-bottom: 20px;
24 | }
25 | }
26 |
27 | .edit {
28 | float: right;
29 | font-family: $font-family-sans-serif;
30 | font-size: $font-size-base;
31 | }
32 |
33 | .detail {
34 | padding: 0px;
35 | margin: 0px;
36 | line-height: 15px;
37 | }
38 |
39 | .pencil {
40 | padding-right: 5px;
41 | font-size: 10px;
42 | font-weight: 600;
43 | }
44 |
45 | .label {
46 | background: $background-color;
47 | border-right: 1px solid $border-color;
48 | color: $text-strong-color;
49 | font-weight: 600;
50 | width: 200px;
51 | font-size: $font-size-base;
52 | font-family: Nitti Grotesk;
53 | text-align: right;
54 | letter-spacing: 0.2px;
55 | text-transform: uppercase;
56 | }
57 |
58 | .value, .pre {
59 | font-size: $font-size-code;
60 | color: $text-table-value-color;
61 | }
62 |
63 | .value {
64 | word-wrap: break-word;
65 | font-family: Nitti;
66 | }
67 |
68 | .pre {
69 | display: inline-block;
70 | padding: 0;
71 | margin: 0;
72 |
73 | word-break: break-all;
74 | word-wrap: break-word;
75 | white-space: pre-wrap;
76 |
77 | border: none;
78 | line-height: 1.4;
79 | }
80 |
--------------------------------------------------------------------------------
/src/features/shared/components/Mnemonic/Mnemonic.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { copyToClipboard } from 'utility/clipboard'
3 | import styles from './Mnemonic.scss'
4 | import {withNamespaces} from 'react-i18next'
5 |
6 | class Mnemonic extends React.Component {
7 | constructor(props) {
8 | super(props)
9 | this.state={
10 | mnemonicArray : this.props.mnemonic.split(' ')
11 | }
12 | }
13 |
14 | render() {
15 | const t = this.props.t
16 | const {mnemonicArray} = this.state
17 | return (
18 |
19 |
20 |
{t('init.mnemonic')}
21 |
27 |
28 |
{t('mnemonic.backupMessage')}
29 |
30 |
31 | {
32 | mnemonicArray.map((seedWord) =>
33 |
{seedWord}
)
34 | }
35 |
36 |
37 |
38 | )
39 | }
40 | }
41 |
42 | export default withNamespaces('translations') (Mnemonic)
43 |
--------------------------------------------------------------------------------
/src/features/shared/components/Mnemonic/Mnemonic.scss:
--------------------------------------------------------------------------------
1 | .seed{
2 | user-select: none;
3 | width: 22%;
4 | height: 36px;
5 | background-color: #F8F8F8;
6 | border: 1px solid $border-color;
7 | border-radius: 3px;
8 | color: $text-strong-color;
9 | margin: 5px 10px 5px 0;
10 | padding: 6px 0px;
11 | float: left;
12 | text-align: center;
13 | }
14 |
15 | .copy{
16 | width: 18px;
17 | margin-bottom: 3px;
18 | }
19 |
20 | .flexContainer{
21 | display: flex;
22 | flex-wrap: wrap;
23 | }
24 |
25 | .seedArea{
26 | margin-bottom : $gutter-size/2;
27 | }
28 |
29 | .container{
30 | width: 540px;
31 | }
32 |
--------------------------------------------------------------------------------
/src/features/shared/components/NotFound.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {withNamespaces} from 'react-i18next'
3 |
4 | class NotFound extends React.Component {
5 | render() {
6 | const t = this.props.t
7 | return (
8 |
9 |
404 {t('notFound.title')}
10 |
{t('notFound.word')}
11 |
12 | )
13 | }
14 | }
15 |
16 | export default withNamespaces('translations') (NotFound)
17 |
--------------------------------------------------------------------------------
/src/features/shared/components/ObjectSelectorField/ObjectSelectorField.scss:
--------------------------------------------------------------------------------
1 | .dropdownButton {
2 | text-align: left;
3 | width: 90px;
4 | }
5 |
6 | .aliasFieldGroupItem {
7 | border-top-left-radius: 0 !important;
8 | border-bottom-left-radius: 0 !important;
9 | border-top-right-radius: $input-border-radius-large !important;
10 | border-bottom-right-radius: $input-border-radius-large !important;
11 | }
12 |
--------------------------------------------------------------------------------
/src/features/shared/components/PageContent/PageContent.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './PageContent.scss'
3 |
4 | class PageContent extends React.Component {
5 | render() {
6 | return (
7 |
8 | {this.props.children}
9 |
10 | )
11 | }
12 | }
13 |
14 | export default PageContent
15 |
--------------------------------------------------------------------------------
/src/features/shared/components/PageContent/PageContent.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | padding: $gutter-size;
3 | position: relative;
4 | margin-top: $title-height;
5 | }
6 |
--------------------------------------------------------------------------------
/src/features/shared/components/PageTitle/PageTitle.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | background: $background-color;
3 | padding: $gutter-size;
4 | border-bottom: 1px solid $border-color;
5 | height: $title-height;
6 | display: flex;
7 | align-items: center;
8 | left: $sidebar-width;
9 | z-index: 99;
10 | position:fixed;
11 | right:0;
12 | code {
13 | display: inline-block;
14 | font-size: $font-size-code;
15 | font-weight: normal;
16 | margin-left: 3px;
17 | vertical-align: middle;
18 | overflow: hidden;
19 | text-overflow: ellipsis;
20 | white-space: nowrap;
21 | max-width: 300px;
22 | padding: 0 6px;
23 | background: $background-emphasis-color;
24 | border: 1px solid transparentize($border-color, 0.5);
25 | line-height: 1.4;
26 | }
27 | }
28 | .title {
29 | color: $text-strong-color;
30 | }
31 |
32 | .navigation {
33 | flex-grow: 1;
34 | }
35 |
36 | .crumbs {
37 | display: flex;
38 | font-size: $font-size-page-title;
39 | list-style-type: none;
40 | margin: 0;
41 | padding: 0;
42 | }
43 |
44 | .crumb a {
45 | position: relative;
46 | padding-right: $gutter-size/2;
47 | margin-right: $gutter-size/2;
48 |
49 | .chevron {
50 | width: 5px;
51 | height: 10px;
52 | position: absolute;
53 | right: -2px;
54 | top: calc(50% - 5px) ;
55 | }
56 | }
57 |
58 | .actions {
59 | list-style-type: none;
60 | display: flex;
61 | margin: 0;
62 | padding: 0;
63 | }
64 |
65 | .flash {
66 | z-index: 10;
67 | width: calc(100% - 220px);
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/src/features/shared/components/Pagination/Pagination.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './Pagination.scss'
3 | import {withNamespaces} from 'react-i18next'
4 |
5 | class Pagination extends React.Component {
6 | render() {
7 | const t = this.props.t
8 | const prevClass = `${styles.button} ${this.props.currentPage > 1 ? '' : styles.disabled}`
9 | const nextClass = `${styles.button} ${this.props.isLastPage ? styles.disabled : ''}`
10 | const nextPage = () => this.props.pushList(this.props.currentFilter, this.props.currentPage + 1)
11 | const prevPage = () => this.props.pushList(this.props.currentFilter, this.props.currentPage - 1)
12 |
13 | return (
14 |
15 | -
16 |
17 | ←
18 |
19 |
20 | - {t('commonWords.page')} {this.props.currentPage}
21 | -
22 |
23 | →
24 |
25 |
26 |
27 | )
28 | }
29 | }
30 |
31 | Pagination.propTypes = {
32 | currentPage: React.PropTypes.number,
33 | isLastPage: React.PropTypes.bool,
34 | pushList: React.PropTypes.func,
35 | currentFilter: React.PropTypes.object,
36 | }
37 |
38 | export default withNamespaces('translations') (Pagination)
39 |
--------------------------------------------------------------------------------
/src/features/shared/components/Pagination/Pagination.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | text-align: center;
3 | list-style-type: none;
4 | padding: 0;
5 | user-select: none;
6 | margin-top: $gutter-size;
7 |
8 | li {
9 | display: inline-block;
10 | margin: 0 $gutter-size;
11 | }
12 | }
13 |
14 |
15 | .button {
16 | border: 2px solid $highlight-default;
17 | color: $highlight-default;
18 | cursor: pointer;
19 | display: block;
20 | line-height: 26px;
21 | font-size: $font-size-btn-lg;
22 | height: 30px;
23 | width: 30px;
24 | border-radius: 15px;
25 |
26 | &:hover {
27 | border-color: $highlight-secondary;
28 | }
29 | }
30 |
31 | .disabled {
32 | border-color: $text-color;
33 | color: $text-color;
34 | opacity: 0;
35 | pointer-events: none;
36 | }
37 |
--------------------------------------------------------------------------------
/src/features/shared/components/PasswordField/PasswordField.scss:
--------------------------------------------------------------------------------
1 | .capsIcon{
2 | background-image: url('images/capslock.svg');
3 | }
4 |
5 | .capsIconGreen{
6 | background-image: url('images/capslock-green.svg');
7 | }
8 |
9 | .password{
10 | background-repeat: no-repeat;
11 | background-position: 99% center;
12 | background-size: 20px 20px;
13 | padding-right: 30px;
14 |
15 | -webkit-text-security:disc !important;
16 | }
17 |
18 | .dotFont{
19 | font-family:dotsfont;
20 | }
21 |
--------------------------------------------------------------------------------
/src/features/shared/components/RadioField/RadioField.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import pick from 'lodash/pick'
3 | import { FieldLabel } from 'features/shared/components'
4 | import styles from './RadioField.scss'
5 |
6 | const TEXT_FIELD_PROPS = [
7 | 'value',
8 | 'onBlur',
9 | 'onChange',
10 | 'onFocus',
11 | 'name'
12 | ]
13 |
14 | class RadioField extends React.Component {
15 | constructor(props) {
16 | super(props)
17 | }
18 |
19 | render() {
20 | const fieldProps = pick(this.props.fieldProps, TEXT_FIELD_PROPS)
21 | const options = this.props.options
22 |
23 | return(
24 |
25 | {this.props.title &&
{this.props.title}}
26 |
27 | {options.map((option) =>
28 | )}
37 |
38 |
39 |
40 | )
41 | }
42 | }
43 |
44 | export default RadioField
--------------------------------------------------------------------------------
/src/features/shared/components/RadioField/RadioField.scss:
--------------------------------------------------------------------------------
1 | .radio{
2 | margin-right: $gutter-size/4 !important;
3 | }
4 | .label{
5 | margin-right: $gutter-size;
6 | }
--------------------------------------------------------------------------------
/src/features/shared/components/RawJsonButton.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Connection } from 'sdk'
3 |
4 | class RawJsonButton extends React.Component {
5 | showRawJson(item){
6 | const snakeCased = Connection.snakeize({...item})
7 | this.props.showJsonModal({JSON.stringify(snakeCased, null, 2)}
)
8 | }
9 |
10 | render() {
11 | return (
12 |
15 | )
16 | }
17 | }
18 |
19 | import { connect } from 'react-redux'
20 | import actions from 'actions'
21 |
22 | const mapDispatchToProps = ( dispatch ) => ({
23 | showJsonModal: (body) => dispatch(actions.app.showModal(
24 | body,
25 | actions.app.hideModal,
26 | null,
27 | { wide: true }
28 | )),
29 | })
30 |
31 | export default connect(
32 | () => ({}),
33 | mapDispatchToProps
34 | )(RawJsonButton)
35 |
--------------------------------------------------------------------------------
/src/features/shared/components/RelativeTime.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import moment from 'moment'
3 | import { humanizeDuration } from 'utility/time'
4 |
5 | class RelativeTime extends React.Component {
6 | render() {
7 | const momentTime = moment.unix(this.props.timestamp)
8 |
9 | let timestamp = momentTime.fromNow()
10 | const diff = momentTime.diff(moment())
11 |
12 | if (diff > 0) {
13 | timestamp = humanizeDuration(diff/1000) + ' ahead of local time'
14 | }
15 | else if( moment.duration(diff).asHours()<-24){
16 | timestamp = momentTime.format('YYYY-MM-DD HH:mm:ss')
17 | }
18 |
19 | return(
20 | {timestamp}
21 | )
22 | }
23 | }
24 |
25 | export default RelativeTime
26 |
--------------------------------------------------------------------------------
/src/features/shared/components/RestoreKeystore/RestoreKeystore.scss:
--------------------------------------------------------------------------------
1 | .submitButton{
2 | float: left;
3 | }
4 |
--------------------------------------------------------------------------------
/src/features/shared/components/RestoreMnemonic/RestoreMnemonic.scss:
--------------------------------------------------------------------------------
1 | .submitButton{
2 | float: left;
3 | }
4 |
--------------------------------------------------------------------------------
/src/features/shared/components/RoutingContainer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default class RoutingContainer extends React.Component {
4 | render() {
5 | return (
6 |
7 | {this.props.children}
8 |
9 | )
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/features/shared/components/SearchBar/SearchBar.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | padding: $gutter-size/2 $gutter-size;
3 | }
4 |
5 | .search_field_full, .search_field_half, .sum_by_field {
6 | display: inline-block;
7 | position: relative;
8 | }
9 |
10 | .searchField {
11 | position: relative;
12 | }
13 |
14 | .search_field_full {
15 | width: 100%;
16 | }
17 |
18 | .search_field_half {
19 | width: 55%;
20 | padding-right: $gutter-size/2;
21 | }
22 |
23 | .sum_by_field {
24 | width: 45%;
25 | }
26 |
27 | .label {
28 | padding-left: 20px;
29 | }
30 |
31 | .search_input {
32 | height: 50px;
33 | padding: 10px 20px 10px 45px;
34 | border: 1px solid $border-color;
35 | height: 44px;
36 | color: $text-strong-color;
37 | border-radius: 25px;
38 | background-image: url('images/search.png');
39 | background-repeat: no-repeat;
40 | background-size: 15px 15px;
41 | background-position: 20px center;
42 | }
43 |
44 | .sum_by_input {
45 | background-image: url('images/sum.png');
46 | }
47 |
48 | .showSumBy {
49 | border: 1px solid $border-color;
50 | border-radius: 25px;
51 | color: $text-light-color;
52 | padding: 0px 10px;
53 | position: absolute;
54 | right: $gutter-size/2;
55 | bottom: 8px;
56 |
57 | &:hover {
58 | cursor: pointer;
59 | background-color: $background-emphasis-color;
60 | }
61 | }
62 |
63 | .search_input::-webkit-input-placeholder {
64 | color: #b6bcc5;
65 | }
66 |
67 | .submit {
68 | position: absolute;
69 | left: -9999px;
70 | }
71 |
72 | .clearSearch {
73 | display: inline-block;
74 | text-decoration: underline;
75 |
76 | &:hover {
77 | cursor: pointer;
78 | }
79 | }
80 |
81 | .queryTime {
82 | display: inline-block;
83 | padding: 4px 20px;
84 | opacity: 0.66
85 | }
86 |
--------------------------------------------------------------------------------
/src/features/shared/components/Section/Section.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './Section.scss'
3 |
4 | class Section extends React.Component {
5 | render() {
6 | return (
7 |
8 | {this.props.title &&
9 |
10 | {this.props.title}
11 |
12 |
13 | {this.props.actions &&
{this.props.actions}
}
14 |
}
15 |
16 |
17 | {this.props.children}
18 |
19 |
20 | )
21 | }
22 | }
23 |
24 | export default Section
25 |
--------------------------------------------------------------------------------
/src/features/shared/components/Section/Section.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | margin: 0 0 $gutter-size;
3 | }
4 |
5 | .title {
6 | display: flex;
7 |
8 | h5 {
9 | flex-grow: 1;
10 | font-size: $font-size-section-title;
11 | font-weight: 500;
12 | }
13 |
14 | code {
15 | display: inline-block;
16 | font-size: $font-size-code;
17 | font-weight: normal;
18 | margin-left: 3px;
19 | vertical-align: middle;
20 | overflow: hidden;
21 | text-overflow: ellipsis;
22 | white-space: nowrap;
23 | max-width: 200px;
24 | padding: 0 6px;
25 | background: $background-emphasis-color;
26 | border: 1px solid transparentize($border-color, 0.5);
27 | line-height: 1.5;
28 | }
29 | }
30 |
31 | .children {
32 | border: 1px solid $border-color;
33 | }
34 |
--------------------------------------------------------------------------------
/src/features/shared/components/SelectField.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import FieldLabel from './FieldLabel/FieldLabel'
3 | import pick from 'lodash/pick'
4 | import ReactMarkdown from 'react-markdown'
5 | import {withNamespaces} from 'react-i18next'
6 |
7 | const SELECT_FIELD_PROPS = [
8 | 'value',
9 | 'onBlur',
10 | 'onChange',
11 | 'onFocus',
12 | ]
13 |
14 | class SelectField extends React.Component {
15 | render() {
16 | const t = this.props.t
17 | const options = this.props.options
18 | const emptyLabel = this.props.emptyLabel || (t('form.selectPlaceholder'))
19 | const valueKey = this.props.valueKey || 'value'
20 | const labelKey = this.props.labelKey || 'label'
21 |
22 | const fieldProps = pick(this.props.fieldProps, SELECT_FIELD_PROPS)
23 | const {touched, error} = this.props.fieldProps
24 |
25 | return(
26 |
27 | {this.props.title && {this.props.title}}
28 |
38 |
39 | {touched && error && {error}}
40 | {this.props.hint && }
41 |
42 | )
43 | }
44 | }
45 |
46 | export default withNamespaces('translations') (SelectField)
47 |
--------------------------------------------------------------------------------
/src/features/shared/components/SingletonField.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import pick from 'lodash/pick'
3 | import disableAutocomplete from 'utility/disableAutocomplete'
4 |
5 | const TEXT_FIELD_PROPS = [
6 | 'value',
7 | 'onBlur',
8 | 'onChange',
9 | 'onFocus',
10 | 'name'
11 | ]
12 |
13 | class SingletonField extends React.Component {
14 | constructor(props) {
15 | super(props)
16 | }
17 |
18 | render() {
19 | const fieldProps = pick(this.props.fieldProps, TEXT_FIELD_PROPS)
20 | const {touched, error} = this.props.fieldProps
21 |
22 | return(
23 |
24 |
29 |
30 | )
31 | }
32 | }
33 |
34 | export default SingletonField
35 |
--------------------------------------------------------------------------------
/src/features/shared/components/Stepper/Step.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './stepper.scss'
3 | import {withNamespaces} from 'react-i18next'
4 |
5 | class Step extends React.Component {
6 |
7 | render() {
8 | const {
9 | isActive,
10 | displayPrevious,
11 | displayNext,
12 | component,
13 | children,
14 | t,
15 | nextL,
16 | prevL
17 | } = this.props
18 |
19 | if(isActive === false) return null
20 |
21 | return (
22 |
23 | {component ? React.createElement(component) : children}
24 |
this.props.goToNextStep()}
28 | />
29 | this.props.goToPreviousStep()}
33 | />
34 |
35 | )
36 | }
37 | }
38 |
39 | class Next extends React.Component {
40 |
41 | render() {
42 | const { isActive, label } = this.props
43 | if (isActive === false) return null
44 |
45 | return (
46 |
51 | )
52 | }
53 | }
54 |
55 | class Previous extends React.Component {
56 |
57 | render() {
58 | const { isActive, label } = this.props
59 | if (isActive === false) return null
60 |
61 | return (
62 | this.props.goToPreviousStep()}>
65 | {label}
66 |
67 | )
68 | }
69 | }
70 |
71 | export default withNamespaces('translations') (Step)
--------------------------------------------------------------------------------
/src/features/shared/components/Stepper/StepList.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | class StepList extends React.Component {
4 | constructor(props) {
5 | super(props)
6 |
7 | this.state = {
8 | currentStep: 0,
9 | totalSteps: this.props.children.length - 1,
10 | }
11 | }
12 |
13 | goToPreviousStep () {
14 | this.setState({ currentStep: this.state.currentStep - 1 })
15 | }
16 |
17 | goToNextStep() {
18 | this.setState({ currentStep: this.state.currentStep + 1 })
19 | }
20 |
21 | render() {
22 | const children = React.Children.map(this.props.children, (child, index) => {
23 | const { currentStep, totalSteps } = this.state
24 |
25 | return React.cloneElement(child, {
26 | isActive: index === currentStep,
27 | displayPrevious: currentStep > 0,
28 | displayNext: currentStep < totalSteps,
29 | displaySubmit: currentStep === totalSteps,
30 | goToPreviousStep: () => this.goToPreviousStep(),
31 | goToNextStep: () => this.goToNextStep(),
32 | })
33 | })
34 |
35 | return (
36 | {children}
37 | )
38 | }
39 | }
40 |
41 | export default StepList
--------------------------------------------------------------------------------
/src/features/shared/components/Stepper/stepper.scss:
--------------------------------------------------------------------------------
1 | .marginLeft{
2 | margin-left: $gutter-size/2;
3 | cursor: pointer;
4 | }
5 |
6 | .floatLeft{
7 | float: left;
8 | }
9 |
--------------------------------------------------------------------------------
/src/features/shared/components/SubmitIndicator/SubmitIndicator.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './SubmitIndicator.scss'
3 |
4 | export default class SubmitIndicator extends React.Component {
5 | render() {
6 | const text = this.props.text || 'Submitting...'
7 | return {text}
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/shared/components/SubmitIndicator/SubmitIndicator.scss:
--------------------------------------------------------------------------------
1 | .activeSubmit {
2 | animation: pulse 2s infinite;
3 | }
4 |
5 | @keyframes pulse {
6 | 0% {
7 | opacity: 1.0;
8 | }
9 | 50% {
10 | opacity: 0.0;
11 | }
12 | 100% {
13 | opacity: 1.0;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/features/shared/components/TableList/TableList.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './TableList.scss'
3 |
4 | class TableList extends React.Component {
5 | render() {
6 | return (
7 |
8 |
9 |
10 | {this.props.titles.map(title => {title} | )}
11 | |
12 |
13 |
14 |
15 | {this.props.children}
16 |
17 |
18 | )
19 | }
20 | }
21 |
22 | export default TableList
23 |
--------------------------------------------------------------------------------
/src/features/shared/components/TableList/TableList.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | background: $background-color;
3 | border: 1px solid $border-color !important;
4 | color: $text-strong-color;
5 | width: 100%;
6 | table-layout: fixed;
7 | margin-bottom: $gutter-size;
8 |
9 | code {
10 | padding: 0;
11 | font-size: $font-size-code;
12 | }
13 |
14 | td {
15 | border-top: 1px solid $border-light-color;
16 | overflow: hidden;
17 | text-overflow: ellipsis;
18 | color: $text-color;
19 | line-height: 20px;
20 | vertical-align: top;
21 | }
22 |
23 | th {
24 | color: $text-strong-color;
25 | //font-size: $font-size-caps;
26 | text-transform: uppercase;
27 | font-weight: 500;
28 | font-size: $font-size-chrome;
29 | }
30 |
31 | td, th {
32 | padding: 13px $gutter-size 13px 0;
33 | }
34 | th, td {
35 |
36 | &:first-child {
37 | padding-left: $gutter-size * 2;
38 | }
39 |
40 | &:last-child {
41 | text-align: right;
42 | }
43 | }
44 |
45 | :global {
46 | .btn-link {
47 | padding-top: 0;
48 | padding-bottom: 0;
49 | line-height: 1;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/features/shared/components/TextField.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import pick from 'lodash/pick'
3 | import { FieldLabel } from 'features/shared/components'
4 | import disableAutocomplete from 'utility/disableAutocomplete'
5 |
6 | const TEXT_FIELD_PROPS = [
7 | 'value',
8 | 'onBlur',
9 | 'onChange',
10 | 'onFocus',
11 | 'name'
12 | ]
13 |
14 | class TextField extends React.Component {
15 | constructor(props) {
16 | super(props)
17 | const type = props.type || 'text'
18 | this.state = {type}
19 | }
20 |
21 | render() {
22 | const fieldProps = pick(this.props.fieldProps, TEXT_FIELD_PROPS)
23 | const {touched, error} = this.props.fieldProps
24 |
25 | return(
26 |
27 | {this.props.title && {this.props.title}}
28 |
36 |
37 | {touched && error && {error}}
38 | {this.props.hint && {this.props.hint}}
39 |
40 | )
41 | }
42 | }
43 |
44 | export default TextField
45 |
--------------------------------------------------------------------------------
/src/features/shared/components/TextareaField.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import pick from 'lodash/pick'
3 | import { FieldLabel } from 'features/shared/components'
4 | import disableAutocomplete from 'utility/disableAutocomplete'
5 |
6 | const TEXT_FIELD_PROPS = [
7 | 'value',
8 | 'onBlur',
9 | 'onChange',
10 | 'onFocus',
11 | 'name'
12 | ]
13 |
14 | class TextareaField extends React.Component {
15 | constructor(props) {
16 | super(props)
17 | }
18 |
19 | render() {
20 | const fieldProps = pick(this.props.fieldProps, TEXT_FIELD_PROPS)
21 | const {touched, error} = this.props.fieldProps
22 |
23 | return(
24 |
25 | {this.props.title && {this.props.title}}
26 |
32 |
33 | {touched && error && {error}}
34 | {this.props.hint && {this.props.hint}}
35 |
36 | )
37 | }
38 | }
39 |
40 | export default TextareaField
41 |
--------------------------------------------------------------------------------
/src/features/shared/components/XpubField/XpubField.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | border: 1px solid $border-color;
3 | margin-top: $grid-gutter-width/2;
4 | padding: $grid-gutter-width;
5 | margin-bottom: $grid-gutter-width;
6 |
7 | > div {
8 | margin-bottom: $gutter-size/2;
9 | }
10 | }
11 |
12 | .options {
13 | width: 100%;
14 |
15 | td { border: none; }
16 | }
17 |
18 | .options .radio {
19 | margin-right: 8px;
20 | }
21 |
22 |
23 | .label {
24 | width: 220px;
25 | vertical-align: top;
26 |
27 | label {
28 | font-weight: normal;
29 | line-height: 36px;
30 | }
31 | }
32 |
33 | .field {
34 | :global {
35 | .form-group {
36 | margin: 0;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/features/shared/index.js:
--------------------------------------------------------------------------------
1 | import * as actions from './actions'
2 | import makeRoutes from './routes'
3 | import * as reducers from './reducers'
4 |
5 | export {
6 | actions,
7 | reducers,
8 | makeRoutes
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/shared/routes.js:
--------------------------------------------------------------------------------
1 | import { RoutingContainer } from 'features/shared/components'
2 | import { humanize } from 'utility/string'
3 | import { UTXOpageSize, pageSize } from 'utility/environment'
4 | import actions from 'actions'
5 |
6 | const makeRoutes = (store, type, List, New, Show, options = {}) => {
7 | const loadPage = ( state ) => {
8 | if(type === 'transaction' || type === 'unspent'){
9 | const query = state.location.query
10 | const pageNumber = parseInt(state.location.query.page || 1)
11 | const pageSizes = (type === 'unspent')? UTXOpageSize: pageSize
12 | if (pageNumber == 1) {
13 | store.dispatch(actions[type].fetchPage(query, pageNumber, { refresh: true, pageSize: pageSizes }))
14 | } else {
15 | store.dispatch(actions[type].fetchPage(query, pageNumber, { pageSize: pageSizes }))
16 | }
17 | }else{
18 | store.dispatch(actions[type].fetchAll())
19 | }
20 | }
21 |
22 | const childRoutes = []
23 |
24 | if (New) {
25 | childRoutes.push({
26 | path: 'create',
27 | component: New
28 | })
29 | }
30 |
31 | if (options.childRoutes) {
32 | childRoutes.push(...options.childRoutes)
33 | }
34 |
35 | if (Show) {
36 | childRoutes.push({
37 | path: ':id',
38 | component: Show
39 | })
40 | }
41 |
42 | return {
43 | path: options.path || type + 's',
44 | component: RoutingContainer,
45 | name: options.name || humanize(type),
46 | indexRoute: {
47 | component: List,
48 | onEnter: (nextState, replace) => {
49 | loadPage(nextState, replace)
50 | },
51 | onChange: (_, nextState, replace) => { loadPage(nextState, replace) }
52 | },
53 | childRoutes: childRoutes
54 | }
55 | }
56 |
57 | export default makeRoutes
58 |
--------------------------------------------------------------------------------
/src/features/testnet/actions.js:
--------------------------------------------------------------------------------
1 | // FIXME: Microsoft Edge has issues returning errors for responses
2 | // with a 401 status. We should add browser detection to only
3 | // use the ponyfill for unsupported browsers.
4 | const { fetch } = require('fetch-ponyfill')()
5 | import { testnetInfoUrl } from 'utility/environment'
6 |
7 | export const fetchTestnetInfo = () => {
8 | return (dispatch) =>
9 | fetch(testnetInfoUrl)
10 | .then(resp => resp.json())
11 | .then(json => {
12 | dispatch({type: 'TESTNET_CONFIG', data: json})
13 | return json
14 | })
15 | }
16 |
17 | const actions = {
18 | fetchTestnetInfo,
19 | }
20 |
21 | export default actions
22 |
--------------------------------------------------------------------------------
/src/features/testnet/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import reducers from './reducers'
3 | import utils from './utils'
4 |
5 | export {
6 | actions,
7 | reducers,
8 | utils,
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/testnet/reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import moment from 'moment-timezone'
3 |
4 | export const nextReset = (state = '', action) => {
5 | if (action.type == 'TESTNET_CONFIG') {
6 | if (action.data.next_reset) {
7 | return moment(action.data.next_reset)
8 | } else {
9 | // Default reset time is the upcoming Sunday 00:00:00 Pacific.
10 | return moment().tz('America/Los_Angeles').day(7).startOf('day')
11 | }
12 | }
13 | return state
14 | }
15 |
16 | export const blockchainId = (state = 0, action) => {
17 | if (action.type == 'TESTNET_CONFIG') {
18 | return action.data.blockchain_id
19 | }
20 | return state
21 | }
22 |
23 | export const crosscoreRpcVersion = (state = 0, action) => {
24 | if (action.type == 'TESTNET_CONFIG') {
25 | return action.data.crosscore_rpc_version || action.data.network_rpc_version
26 | }
27 | return state
28 | }
29 |
30 | export const testnetInfo = (state = { loading: true }, action) => {
31 | if (action.type == 'TESTNET_CONFIG') {
32 | state = {...action.data}
33 | }
34 | return state
35 | }
36 |
37 | export default combineReducers({
38 | blockchainId,
39 | nextReset,
40 | crosscoreRpcVersion,
41 | testnetInfo,
42 | })
43 |
--------------------------------------------------------------------------------
/src/features/testnet/utils.js:
--------------------------------------------------------------------------------
1 | const isBlockchainMismatch = (state) => {
2 | if (!state.core.onTestnet) {
3 | return false
4 | }
5 |
6 | return !!state.core.blockchainId && !!state.testnet.blockchainId &&
7 | state.core.blockchainId != state.testnet.blockchainId
8 | }
9 |
10 | const isCrosscoreRpcMismatch = (state) => {
11 | if (!state.core.onTestnet) {
12 | return false
13 | }
14 |
15 | return !!state.core.crosscoreRpcVersion && !!state.testnet.crosscoreRpcVersion &&
16 | state.core.crosscoreRpcVersion != state.testnet.crosscoreRpcVersion
17 | }
18 |
19 | export default {
20 | isBlockchainMismatch,
21 | isCrosscoreRpcMismatch,
22 | }
23 |
--------------------------------------------------------------------------------
/src/features/transactionFeeds/actions.js:
--------------------------------------------------------------------------------
1 | import { baseListActions, baseCreateActions } from 'features/shared/actions'
2 |
3 | const type = 'transactionFeed'
4 |
5 | export default {
6 | ...baseCreateActions(type, {
7 | listPath: 'transaction-feeds',
8 | className: 'TransactionFeed',
9 | }),
10 | ...baseListActions(type, {
11 | listPath: 'transaction-feeds',
12 | className: 'TransactionFeed',
13 | }),
14 | }
15 |
--------------------------------------------------------------------------------
/src/features/transactionFeeds/components/List.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { BaseList, EmptyContent } from 'features/shared/components'
3 | import ListItem from './ListItem'
4 | import { actions } from 'features/transactionFeeds'
5 | import { docsRoot } from 'utility/environment'
6 |
7 | const type = 'transactionFeed'
8 |
9 | const firstTimeContent =
10 |
11 | Transaction feeds enable real-time processing of transactions as they arrive on the blockchain.
12 |
13 |
14 | Learn more
15 | about how to use transaction feeds.
16 |
17 |
18 | const dispatch = (dispatch) => ({
19 | ...BaseList.mapDispatchToProps(type)(dispatch),
20 | itemActions: {
21 | delete: (feed) => {
22 | let label = `ID ${feed.id}`
23 | if (!!feed.alias && feed.alias.length > 0) {
24 | label = `"${feed.alias}"`
25 | }
26 |
27 | dispatch(actions.deleteItem(
28 | feed.id,
29 | `Really delete transaction feed ${label}?`,
30 | `Deleted transaction feed ${label}.`
31 | ))
32 | }
33 | },
34 | })
35 |
36 | export default BaseList.connect(
37 | BaseList.mapStateToProps(type, ListItem, {
38 | skipQuery: true,
39 | label: 'transaction feeds',
40 | firstTimeContent
41 | }),
42 | dispatch
43 | )
44 |
--------------------------------------------------------------------------------
/src/features/transactionFeeds/components/ListItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { KeyValueTable } from 'features/shared/components'
3 |
4 | class ListItem extends React.Component {
5 | render() {
6 | const item = {...this.props.item}
7 |
8 | const after = item.after.split('-')[0].split(':')
9 | const blockHeight = after[0]
10 | const blockPosition = after[1]
11 |
12 | // int max (2147483647) is used to indicate that a feed
13 | // hasn't yet been read from.
14 | const hasStarted = blockPosition != '2147483647'
15 |
16 | const options = [
17 | {label: 'ID', value: item.id}
18 | ]
19 |
20 | if (item.alias) options.push({label: 'Alias', value: item.alias})
21 | options.push({label: 'Filter', value: item.filter, link: `/transactions?filter=${item.filter}`, pre: true})
22 |
23 | if (hasStarted) {
24 | options.push({label: 'Last Acknowledged', value: {blockHeight, blockPosition}})
25 | } else {
26 | options.push({label: 'Last Acknowledged', value: 'None'})
27 | }
28 |
29 | return(
30 |
31 | )
32 | }
33 | }
34 |
35 | export default ListItem
36 |
--------------------------------------------------------------------------------
/src/features/transactionFeeds/components/New.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { BaseNew, FormContainer, FormSection, TextField } from 'features/shared/components'
3 | import { reduxForm } from 'redux-form'
4 |
5 | class New extends React.Component {
6 | constructor(props) {
7 | super(props)
8 |
9 | this.submitWithValidations = this.submitWithValidations.bind(this)
10 | }
11 |
12 | submitWithValidations(data) {
13 | return new Promise((resolve, reject) => {
14 | this.props.submitForm(data)
15 | .catch((err) => reject({_error: err}))
16 | })
17 | }
18 |
19 | render() {
20 | const {
21 | fields: { alias, filter },
22 | error,
23 | handleSubmit,
24 | submitting
25 | } = this.props
26 |
27 | return(
28 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | )
40 | }
41 | }
42 |
43 | const fields = [ 'alias', 'filter' ]
44 | export default BaseNew.connect(
45 | BaseNew.mapStateToProps('transactionFeed'),
46 | BaseNew.mapDispatchToProps('transactionFeed'),
47 | reduxForm({
48 | form: 'newTxFeed',
49 | fields,
50 | })(New)
51 | )
52 |
--------------------------------------------------------------------------------
/src/features/transactionFeeds/components/index.js:
--------------------------------------------------------------------------------
1 | import List from './List'
2 | import New from './New'
3 |
4 | export {
5 | List,
6 | New,
7 | }
8 |
--------------------------------------------------------------------------------
/src/features/transactionFeeds/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import reducers from './reducers'
3 | import routes from './routes'
4 |
5 | export {
6 | actions,
7 | reducers,
8 | routes,
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/transactionFeeds/reducers.js:
--------------------------------------------------------------------------------
1 | import { reducers } from 'features/shared'
2 | import { combineReducers } from 'redux'
3 |
4 | const type = 'transactionFeed'
5 |
6 | export default combineReducers({
7 | items: reducers.itemsReducer(type),
8 | queries: reducers.queriesReducer(type),
9 | })
10 |
--------------------------------------------------------------------------------
/src/features/transactionFeeds/routes.js:
--------------------------------------------------------------------------------
1 | import { List, New } from './components'
2 | import { makeRoutes } from 'features/shared'
3 |
4 | export default (store) => makeRoutes(
5 | store, 'transactionFeed', List, New, null, { path: 'transaction-feeds', name: 'Transaction feeds'}
6 | )
7 |
--------------------------------------------------------------------------------
/src/features/transactions/components/GeneratedTxHex/GeneratedTxHex.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | background: $background-color;
3 | padding: $gutter-size;
4 | margin: 0 auto;
5 | }
6 |
7 | .hex {
8 | word-break: break-all;
9 | word-wrap: break-word;
10 | white-space: pre-wrap;
11 |
12 | margin-top: 5px;
13 | margin-left: auto;
14 | margin-right: auto;
15 | padding: 10px;
16 | background-color: #EEE;
17 | }
18 |
19 | .mgl{
20 | margin-left: $gutter-size/2;
21 | }
22 |
--------------------------------------------------------------------------------
/src/features/transactions/components/GeneratedTxHex/QrCode.scss:
--------------------------------------------------------------------------------
1 | .code{
2 | display: block;
3 | margin: auto;
4 | }
5 |
6 | .progressBar{
7 | height: 6px;
8 | margin-top: 15px;
9 | }
--------------------------------------------------------------------------------
/src/features/transactions/components/List.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { BaseList } from 'features/shared/components'
3 | import { pageSize} from 'utility/environment'
4 | import ListItem from './ListItem/ListItem'
5 | import actions from 'actions'
6 |
7 | const type = 'transaction'
8 |
9 | class List extends React.Component {
10 | componentWillReceiveProps(nextProps) {
11 | if (nextProps.currentBlock != this.props.currentBlock) {
12 | if (nextProps.currentPage == 1) {
13 | this.props.getLatest('')
14 | }
15 | }
16 | }
17 | render() {
18 | const ItemList = BaseList.ItemList
19 | return ()
20 | }
21 | }
22 |
23 | export default BaseList.connect(
24 | (state, ownProps) => ({
25 | ...BaseList.mapStateToProps(type, ListItem)(state, ownProps)
26 | }),
27 | (dispatch) => ({
28 | ...BaseList.mapDispatchToProps(type)(dispatch),
29 | getLatest: (query) => dispatch(actions.transaction.fetchPage(query, 1, { refresh: true, pageSize: pageSize })),
30 | }),
31 | List
32 | )
33 |
34 |
--------------------------------------------------------------------------------
/src/features/transactions/components/ListItem/ListItem.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | border: 1px solid $border-color;
3 | margin-bottom: 30px;
4 | }
5 |
6 | .titleBar {
7 | background: $background-color;
8 | border-bottom: 1px solid $border-color;
9 | display: flex;
10 | align-items: center;
11 | padding: 16px 25px;
12 |
13 | code {
14 | display: inline-block;
15 | font-size: $font-size-code;
16 | overflow: hidden;
17 | text-overflow: ellipsis;
18 | vertical-align: bottom;
19 | width: 200px;
20 | padding: 0 6px;
21 | background: $background-emphasis-color;
22 | border: 1px solid transparentize($border-color, 0.5);
23 | line-height: 1.4;
24 | }
25 | }
26 |
27 | .title {
28 | flex-grow: 1;
29 | align-items: center;
30 | display: flex;
31 |
32 | label {
33 | color: $text-strong-color;
34 | font-size: $font-size-chrome;
35 | text-transform: uppercase;
36 | font-weight: 500;
37 | margin: 0 8px 0 0;
38 | }
39 | img{
40 | width: 20px;
41 | margin-left: $gutter-size/2;
42 | margin-bottom: 2px;
43 | }
44 | }
45 |
46 | .timestamp {
47 | position: absolute;
48 | left: 59%;
49 | }
50 |
51 | .confirmation {
52 | margin-left: $gutter-size;
53 | }
54 |
55 |
56 | .viewLink {
57 | margin: -15px 0;
58 | padding: 15px;
59 | position: relative;
60 |
61 | background: url('images/chevron-green.png');
62 | background-repeat: no-repeat;
63 | background-position: right center;
64 | background-size: 5px 9px;
65 | }
66 |
--------------------------------------------------------------------------------
/src/features/transactions/components/New/ConfirmModal/ConfirmModal.scss:
--------------------------------------------------------------------------------
1 | .submitIndicator{
2 | float:right;
3 | }
4 |
5 | .btnGroup{
6 | display: flex;
7 | >div{
8 | width: 50%;
9 | button{
10 | width: 100%;
11 | }
12 | }
13 |
14 | >button{
15 | width: 50%;
16 | margin-left: $gutter-size;
17 | }
18 | }
19 |
20 | .hr{
21 | margin-top: 11px;
22 | margin-bottom: 11px;
23 | }
24 |
25 | .table{
26 | word-break: break-all;
27 | width: 100%;
28 | td{
29 | padding: 4px 0px;
30 | }
31 | }
32 |
33 | .colLabel{
34 | width: 20%;
35 | color: $text-strong-color;
36 | font-weight: 500;
37 | }
38 |
39 | .unit{
40 | overflow: hidden;
41 | text-overflow: ellipsis;
42 | vertical-align: bottom;
43 | width: 130px;
44 | display: inline-block;
45 | }
46 |
--------------------------------------------------------------------------------
/src/features/transactions/components/New/FormActionItem.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | border: 1px solid $border-color;
3 | padding: $gutter-size;
4 | margin-bottom: $gutter-size;
5 | }
6 |
7 | .error {
8 | border-color: $highlight-danger-border;
9 | }
10 |
11 | .header {
12 | display: flex;
13 | justify-content: space-between;
14 | margin-bottom: $gutter-size;
15 | }
16 |
17 | .title {
18 | color: $text-strong-color;
19 | font-size: $font-size-form-section-title;
20 | font-weight: 600;
21 | }
22 |
--------------------------------------------------------------------------------
/src/features/transactions/components/New/MultiSignTransactionDetails/TransactionDetails.scss:
--------------------------------------------------------------------------------
1 | .btnGroup{
2 | margin-bottom: $gutter-size;
3 | }
4 |
5 | .main {
6 | padding: $gutter-size;
7 | margin-bottom: $gutter-size;
8 | word-break: break-word;
9 | background: $background-content-color;
10 | }
11 |
12 | .btn {
13 | margin-top: 5px;
14 | padding: 0px;
15 | float: right;
16 | font-size: $font-size-caps;
17 | }
18 |
19 | .table{
20 | background: $background-color;
21 | width: 100%;
22 | margin-bottom: $gutter-size;
23 |
24 | code{
25 | font-size: $font-size-code;
26 | }
27 |
28 | thead {
29 | border-bottom: 1px solid $border-color;
30 | }
31 |
32 | td, th {
33 | padding-top: 8px;
34 | padding-bottom: 8px;
35 | padding-right: 13px;
36 | }
37 |
38 | td {
39 | border-bottom: 1px solid $border-light-color;
40 | }
41 | tr:last-of-type td {
42 | border-bottom: none;
43 | }
44 |
45 | a {
46 | .rawId {
47 | color: $highlight-default;
48 | }
49 |
50 | &:hover .rawId {
51 | text-decoration: underline;
52 | }
53 | }
54 | }
55 |
56 | .colLabel {
57 | color: $text-strong-color;
58 | text-align: right;
59 | width: 25%;
60 | }
61 |
62 | .txID{
63 | margin-bottom: $gutter-size;
64 | }
65 |
--------------------------------------------------------------------------------
/src/features/transactions/components/New/NewTransactionsContainer/TxContainer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ErrorBanner, FormSection, SubmitIndicator } from 'features/shared/components'
3 | import disableAutocomplete from 'utility/disableAutocomplete'
4 |
5 | import styles from './TxContainer.scss'
6 | import {withNamespaces} from 'react-i18next'
7 |
8 | class TxContainer extends React.Component {
9 | render() {
10 | const t = this.props.t
11 | return(
12 |
32 | )
33 | }
34 | }
35 |
36 | export default withNamespaces('translations') (TxContainer)
37 |
--------------------------------------------------------------------------------
/src/features/transactions/components/New/NewTransactionsContainer/TxContainer.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | background: $background-color;
3 | display: flex;
4 | flex-direction: row;
5 | padding: 0 $gutter-size;
6 | margin-top: $title-height;
7 | }
8 |
9 | .content {
10 | min-width: 400px;
11 | width: 55%;
12 | margin: 0 auto;
13 | }
14 |
15 | .submit {
16 | text-align: right;
17 | }
18 |
--------------------------------------------------------------------------------
/src/features/transactions/components/Summary/Summary.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | background: $background-color;
3 | width: 100%;
4 |
5 | code{
6 | font-size: $font-size-code;
7 | }
8 |
9 | thead {
10 | border-bottom: 1px solid $border-color;
11 | }
12 |
13 | td, th {
14 | padding-top: 6px;
15 | padding-bottom: 6px;
16 | padding-right: 10px;
17 | }
18 |
19 | td {
20 | border-bottom: 1px solid $border-light-color;
21 | }
22 | tr:last-of-type td {
23 | border-bottom: none;
24 | }
25 |
26 | a {
27 | .rawId {
28 | color: $highlight-default;
29 | }
30 |
31 | &:hover .rawId {
32 | text-decoration: underline;
33 | }
34 | }
35 | }
36 |
37 | .colAction {
38 | padding-left: $gutter-size ;
39 | }
40 |
41 | .colAction, .colAmount, .colAsset, .colAccount {
42 | color: $text-strong-color;
43 | width: 20%;
44 | }
45 |
46 | .amount {
47 | color: $text-strong-color;
48 | background: none;
49 | }
50 |
51 | .colLabel {
52 | color: $text-light-color;
53 | text-align: right;
54 | width: 7%;
55 | }
56 |
57 | .immature {
58 | margin-left: 5px;
59 | color: $text-danger;
60 | }
61 |
62 | .rawId {
63 | display: inline-block;
64 | overflow: hidden;
65 | text-overflow: ellipsis;
66 | max-width: 150px;
67 | vertical-align: middle;
68 | }
69 |
--------------------------------------------------------------------------------
/src/features/transactions/components/index.js:
--------------------------------------------------------------------------------
1 | import List from './List'
2 | import Summary from './Summary/Summary'
3 | import DetailSummary from './DetailSummary/DetailSummary'
4 | import New from './New/New'
5 | import Show from './Show'
6 | import GeneratedTxHex from './GeneratedTxHex/GeneratedTxHex'
7 |
8 | export {
9 | List,
10 | Summary,
11 | DetailSummary,
12 | New,
13 | Show,
14 | GeneratedTxHex,
15 | }
16 |
--------------------------------------------------------------------------------
/src/features/transactions/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import reducers from './reducers'
3 | import routes from './routes'
4 |
5 | export {
6 | actions,
7 | reducers,
8 | routes,
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/transactions/reducers.js:
--------------------------------------------------------------------------------
1 | import { reducers } from 'features/shared'
2 | import { combineReducers } from 'redux'
3 |
4 | const type = 'transaction'
5 | const maxGeneratedHistory = 50
6 |
7 | const decodedTx = (state = [], action) => {
8 | if (action.type == 'DECODE_TRANSACTION') {
9 | return action.data
10 | }
11 | return state
12 | }
13 |
14 | export default combineReducers({
15 | items: reducers.itemsReducer(type),
16 | queries: reducers.queriesReducer(type),
17 | generated: (state = [], action) => {
18 | if (action.type == 'GENERATED_TX_HEX') {
19 | return [action.generated, ...state].slice(0, maxGeneratedHistory)
20 | }
21 | return state
22 | },
23 | decodedTx
24 | })
25 |
--------------------------------------------------------------------------------
/src/features/transactions/routes.js:
--------------------------------------------------------------------------------
1 | import { List, New, Show, GeneratedTxHex } from './components'
2 | import { makeRoutes } from 'features/shared'
3 |
4 | export default (store) => {
5 | return makeRoutes(
6 | store,
7 | 'transaction',
8 | List,
9 | New,
10 | Show,
11 | {
12 | childRoutes: [
13 | {
14 | path: 'generated/:id',
15 | component: GeneratedTxHex,
16 | },
17 | ]
18 | }
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/src/features/tutorial/actions.js:
--------------------------------------------------------------------------------
1 | function tutorialNextStep(route){
2 | return { type: 'TUTORIAL_NEXT_STEP', route }
3 | }
4 | function submitTutorialForm(data, object){
5 | return { type: 'UPDATE_TUTORIAL', object, data }
6 | }
7 |
8 | let actions = {
9 | tutorialNextStep,
10 | submitTutorialForm
11 | }
12 |
13 | export default actions
14 |
--------------------------------------------------------------------------------
/src/features/tutorial/components/Tutorial.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import TutorialInfo from './TutorialInfo/TutorialInfo'
3 | import TutorialForm from './TutorialForm/TutorialForm'
4 |
5 | const components = {
6 | TutorialInfo,
7 | TutorialForm
8 | }
9 |
10 | class Tutorial extends React.Component {
11 | render() {
12 | const tutorialOpen = !this.props.tutorial.location.isVisited
13 | const tutorialTypes = this.props.types
14 | const TutorialComponent = this.props.content ? components[this.props.content['component']]: components['TutorialInfo']
15 |
16 | return (
17 |
18 | {this.props.content && tutorialOpen && (tutorialTypes.includes(this.props.content['component'])) &&
19 | }
23 |
24 | )
25 | }
26 | }
27 |
28 | import { connect } from 'react-redux'
29 |
30 | const mapStateToProps = (state) => ({
31 | content: state.tutorial.content,
32 | tutorial: state.tutorial,
33 | })
34 |
35 | export default connect(
36 | mapStateToProps
37 | )(Tutorial)
38 |
--------------------------------------------------------------------------------
/src/features/tutorial/components/TutorialHeader/TutorialHeader.scss:
--------------------------------------------------------------------------------
1 | .main {
2 | box-shadow: 0 1px 2px 0 rgba(0,0,0,0.25);
3 | margin-bottom: $gutter-size/2;
4 | border-bottom: solid $highlight-tutorial 1px;
5 | position: fixed;
6 | top: 0px;
7 | left: $sidebar-width;
8 | right: 0;
9 | z-index: 100;
10 | }
11 |
12 | .collapsed {
13 | border-bottom: 1px darken($highlight-tutorial, 15%) solid;
14 | box-shadow: none;
15 | }
16 |
17 | .header {
18 | background-color: $highlight-tutorial;
19 | box-sizing: border-box;
20 | color: $text-inverse-color;
21 | padding: 0 $gutter-size;
22 | height: $title-height;
23 | display: flex;
24 | align-items: center;
25 | font-size: 14px;
26 | font-weight: 500;
27 | width: 100%;
28 | }
29 |
30 | .skip {
31 | flex: 1;
32 | text-align: right;
33 |
34 | a {
35 | color: $text-inverse-color;
36 | font-size: 13px;
37 | font-weight: 500;
38 | line-height: 18px;
39 | text-decoration: underline;
40 | cursor: pointer;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/features/tutorial/components/TutorialInfo/TutorialInfo.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styles from './TutorialInfo.scss'
3 | import {withNamespaces} from 'react-i18next'
4 |
5 | class TutorialInfo extends React.Component {
6 |
7 | render() {
8 | const content = this.props.t(`tutorial.content.${this.props.title}`, { returnObjects: true } )
9 | let objectImage
10 | try {
11 | objectImage = require(`images/empty/${this.props.image}.svg`)
12 | } catch (err) { /* do nothing */ }
13 |
14 | return (
15 |
16 |
17 | {this.props.image &&

}
18 |
19 | {content.map(function (contentLine, i){
20 | let str = contentLine
21 | if (contentLine['line']) { str = contentLine['line'] }
22 | if(contentLine['list']){
23 | let list = []
24 | contentLine['list'].forEach(function(listItem, j){
25 | list.push(
26 | {j+1} |
27 | {listItem} |
28 |
)
29 | })
30 | return
33 | }
34 | return
{str}
35 | })}
36 |
37 |
38 |
39 | )
40 | }
41 | }
42 |
43 | export default withNamespaces('translations') (TutorialInfo)
44 |
--------------------------------------------------------------------------------
/src/features/tutorial/components/TutorialInfo/TutorialInfo.scss:
--------------------------------------------------------------------------------
1 | .container {
2 | width: 100%;
3 | background-color: $background-emphasis-color;
4 | padding: 25px $gutter-size;
5 | display: flex;
6 | align-items: center;
7 |
8 | .image {
9 | margin-right: 20px;
10 | margin-top: 5px;
11 | height: 30px;
12 | width: 30px;
13 | align-self: flex-start;
14 | }
15 |
16 | .text {
17 | flex: 10;
18 | font-size: 13px;
19 | line-height: 1.3;
20 | max-width: 600px;
21 | padding-right: $gutter-size;
22 |
23 | p:last-child {
24 | margin: 0;
25 | }
26 | }
27 |
28 | .listItemContainer {
29 | margin: 15px 0;
30 |
31 | .listBullet {
32 | margin-right: 10px;
33 | padding: 2px;
34 | display: inline-block;
35 | border-radius: 50%;
36 | background: $highlight-tutorial;
37 | width: 18px;
38 | height: 18px;
39 | font-size: 10px;
40 | font-weight: 500;
41 | text-align: center;
42 | line-height: 14px;
43 | color: #FFFFFF;
44 | }
45 |
46 | .listItemGroup {
47 | margin-bottom: 10px;
48 | font-size: 13px;
49 | font-weight: 500;
50 | line-height: 1.4;
51 | }
52 | }
53 |
54 | .nextWrapper {
55 | margin-left: $gutter-size;
56 | text-align: right;
57 | flex: 1;
58 | }
59 |
60 | .next {
61 | background-color: $background-color;
62 | background-image: url('images/chevron-blue.png');
63 | background-repeat: no-repeat;
64 | background-position: right 8px center;
65 | background-size: 5px 9px;
66 | border: 1px solid $highlight-tutorial;
67 | border-radius: 2px;
68 | color: $highlight-tutorial;
69 | padding-right: 20px;
70 | }
71 |
72 | button:active:focus {
73 | background-color: $highlight-tutorial;
74 | border-color: $highlight-tutorial;
75 | color: $background-color;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/features/tutorial/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import reducers from './reducers'
3 |
4 | export {
5 | actions,
6 | reducers,
7 | }
8 |
--------------------------------------------------------------------------------
/src/features/tutorial/introduction.json:
--------------------------------------------------------------------------------
1 | {
2 | "/transactions": {
3 | "component": "TutorialInfo",
4 | "title": "transaction",
5 | "image": "transaction"
6 | },
7 | "/keys": {
8 | "component": "TutorialInfo",
9 | "title": "key",
10 | "image": "key"
11 | },
12 | "/accounts": {
13 | "component": "TutorialInfo",
14 | "title": "account",
15 | "image": "account"
16 | },
17 | "/assets": {
18 | "component": "TutorialInfo",
19 | "title": "asset",
20 | "image": "asset"
21 | },
22 | "/balances": {
23 | "component": "TutorialInfo",
24 | "title": "balance",
25 | "image": "balance"
26 | },
27 | "/core": {
28 | "component": "TutorialInfo",
29 | "title": "coreStatus"
30 | },
31 | "/access-control": {
32 | "component": "TutorialInfo",
33 | "title": "accessControl",
34 | "image": "network_access_token"
35 | },
36 | "/unspents":{
37 | "component": "TutorialInfo",
38 | "title": "unspent",
39 | "image": "unspent"
40 | },
41 | "/backup": {
42 | "component": "TutorialInfo",
43 | "title": "backupRestore",
44 | "image": "client_access_token"
45 | },
46 | "/transactions/create":{
47 | "component": "TutorialForm",
48 | "title": "newTx"
49 | },
50 | "/accounts/create":{
51 | "component": "TutorialForm",
52 | "title": "newAccount"
53 | },
54 | "/assets/create":{
55 | "component": "TutorialForm",
56 | "title": "newAsset"
57 | },
58 | "/keys/create":{
59 | "component": "TutorialForm",
60 | "title": "newKey"
61 | },
62 | "/keys/:id/reset-password":{
63 | "component": "TutorialForm",
64 | "title": "resetPassword"
65 | },
66 | "/access-control/create-token": {
67 | "component": "TutorialForm",
68 | "title": "newToken"
69 | }
70 | }
--------------------------------------------------------------------------------
/src/features/tutorial/reducers.js:
--------------------------------------------------------------------------------
1 | import introduction from './introduction.json'
2 |
3 | const router = ['/backup','/access-control','/core','/keys/create','/keys','/balances','/accounts/create',
4 | '/assets/create','/assets','/accounts','/transactions/create','/transactions', '/access-control/create-token']
5 |
6 | export const location = (state = { visited: [], isVisited: false }, action) => {
7 | if (action.type == '@@router/LOCATION_CHANGE' ) {
8 | if ( !state.visited.includes(action.payload.pathname ) && router.includes(action.payload.pathname)){
9 | if(action.payload.pathname !== '/access-control' ||
10 | ( action.payload.search.includes('?type=token') && action.payload.pathname === '/access-control' )){
11 | return { ...state, visited: [ action.payload.pathname, ...state.visited ], isVisited: false }
12 | }
13 | }else if (action.payload.pathname.match(/^\/keys.*reset-password$/g) && !state.visited.includes('/keys/:id/reset-password'))
14 | {
15 | return { ...state, visited: [ '/keys/:id/reset-password', ...state.visited ], isVisited: false }
16 | } else{
17 | return{ ...state, isVisited:true }
18 | }
19 | }
20 | if (action.type == 'DISMISS_TUTORIAL') return{ ...state, isVisited:true }
21 |
22 | return state
23 | }
24 |
25 | export default (state = {}, action) => {
26 | const newState = {
27 | location: location(state.location, action)
28 | }
29 |
30 | newState.content = introduction[newState.location.visited[0]]
31 | return newState
32 | }
33 |
--------------------------------------------------------------------------------
/src/features/unspents/actions.js:
--------------------------------------------------------------------------------
1 | import { baseListActions } from 'features/shared/actions'
2 | import { chainClient } from 'utility/environment'
3 |
4 | export default baseListActions('unspent', {
5 | clientApi: () => chainClient().unspentOutputs
6 | })
7 |
--------------------------------------------------------------------------------
/src/features/unspents/components/List.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { BaseList } from 'features/shared/components'
3 | import ListItem from './ListItem'
4 |
5 | const type = 'unspent'
6 |
7 | const newStateToProps = (state, ownProps) => ({
8 | ...BaseList.mapStateToProps(type, ListItem)(state, ownProps),
9 | skipCreate: true,
10 | label: 'unspent outputs'
11 | })
12 |
13 | export default BaseList.connect(
14 | newStateToProps,
15 | BaseList.mapDispatchToProps(type)
16 | )
17 |
--------------------------------------------------------------------------------
/src/features/unspents/components/ListItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { KeyValueTable, RawJsonButton, } from 'features/shared/components'
3 | import { buildUnspentDisplay } from 'utility/buildInOutDisplay'
4 | import {withNamespaces} from 'react-i18next'
5 |
6 | class ListItem extends React.Component {
7 | render() {
8 | return(
11 | ID {this.props.item.id}
12 |
13 | }
14 | actions={[
15 |
16 | ]}
17 | items={buildUnspentDisplay(this.props.item, this.props.btmAmountUnit, this.props.t)} />)
18 | }
19 | }
20 |
21 | export default withNamespaces('translations') (ListItem)
22 |
--------------------------------------------------------------------------------
/src/features/unspents/components/index.js:
--------------------------------------------------------------------------------
1 | import List from './List'
2 |
3 | export {
4 | List,
5 | }
6 |
--------------------------------------------------------------------------------
/src/features/unspents/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import reducers from './reducers'
3 | import routes from './routes'
4 |
5 | export {
6 | actions,
7 | reducers,
8 | routes,
9 | }
10 |
--------------------------------------------------------------------------------
/src/features/unspents/reducers.js:
--------------------------------------------------------------------------------
1 | import { reducers } from 'features/shared'
2 | import { combineReducers } from 'redux'
3 |
4 | const type = 'unspent'
5 | const idFunc = item => `${item.id}`
6 |
7 | export default combineReducers({
8 | items: reducers.itemsReducer(type, idFunc),
9 | queries: reducers.queriesReducer(type, idFunc)
10 | })
11 |
--------------------------------------------------------------------------------
/src/features/unspents/routes.js:
--------------------------------------------------------------------------------
1 | import { List } from './components'
2 | import { makeRoutes } from 'features/shared'
3 |
4 | export default (store) => makeRoutes(store, 'unspent', List)
5 |
--------------------------------------------------------------------------------
/src/i18n.js:
--------------------------------------------------------------------------------
1 | import i18n from 'i18next'
2 | import LanguageDetector from 'i18next-browser-languagedetector'
3 |
4 | import transaction_zh from './locales/zh/translation.json'
5 | import transaction_en from './locales/en/translation.json'
6 |
7 | i18n.use(LanguageDetector).init({
8 | // we init with resources
9 | resources:{
10 | en: {
11 | translations: transaction_en
12 | },
13 | zh: {
14 | translations: transaction_zh
15 | },
16 | },
17 | fallbackLng: 'en',
18 | debug: false,
19 |
20 | // have a common namespace used around the full app
21 | ns: ['translations'],
22 | defaultNS: 'translations',
23 |
24 | interpolation: {
25 | escapeValue: false, // not needed for react!!
26 | prefix: '__',
27 | suffix: '__'
28 | },
29 |
30 | react: {
31 | wait: true,
32 | bindStore: false
33 | }
34 | })
35 |
36 | export default i18n
37 |
--------------------------------------------------------------------------------
/src/routes.js:
--------------------------------------------------------------------------------
1 | import { Container } from 'features/app/components'
2 | import { NotFound } from 'features/shared/components'
3 | import accessControl from 'features/accessControl/routes'
4 | import { routes as accounts } from 'features/accounts'
5 | import { routes as assets } from 'features/assets'
6 | import { routes as balances } from 'features/balances'
7 | import { routes as configuration } from 'features/configuration'
8 | import { routes as core } from 'features/core'
9 | import { routes as initialization } from 'features/initialization'
10 | import { routes as transactions } from 'features/transactions'
11 | import { routes as transactionFeeds } from 'features/transactionFeeds'
12 | import { routes as unspents } from 'features/unspents'
13 | import { routes as mockhsm } from 'features/mockhsm'
14 | import { routes as backup } from 'features/backup'
15 | import { routes as peers } from 'features/peers'
16 |
17 | const makeRoutes = (store) => ({
18 | path: '/',
19 | component: Container,
20 | childRoutes: [
21 | accessControl(store),
22 | accounts(store),
23 | assets(store),
24 | balances(store),
25 | configuration,
26 | core,
27 | peers(store),
28 | initialization,
29 | transactions(store),
30 | transactionFeeds(store),
31 | unspents(store),
32 | mockhsm(store),
33 | backup,
34 | {
35 | path: '*',
36 | component: NotFound
37 | }
38 | ]
39 | })
40 |
41 | export default makeRoutes
42 |
--------------------------------------------------------------------------------
/src/sdk/api/accessTokens.js:
--------------------------------------------------------------------------------
1 | const shared = require('../shared')
2 |
3 | const accessTokens = (client) => {
4 | return {
5 | create: (params, cb) =>
6 | shared.create(client, '/create-access-token', params, {skipArray: true, cb}),
7 |
8 | query: (params, cb) => shared.query(client, 'accessTokens', '/list-access-tokens', params, {cb}),
9 |
10 | queryAll: (params, processor, cb) => shared.queryAll(client, '/list-access-tokens', params, processor, cb),
11 |
12 | delete: (id, cb) => shared.tryCallback(
13 | client.request('/delete-access-token', {id: id}),
14 | cb
15 | ),
16 | }
17 | }
18 |
19 | module.exports = accessTokens
20 |
--------------------------------------------------------------------------------
/src/sdk/api/accounts.js:
--------------------------------------------------------------------------------
1 | const shared = require('../shared')
2 |
3 | const accountsAPI = (client) => {
4 | return {
5 | create: (params, cb) => shared.create(client, '/create-account', params, {cb, skipArray: true}),
6 |
7 | createBatch: (params, cb) => shared.createBatch(client, '/create-account', params, {cb}),
8 |
9 | updateTags: (params, cb) => {
10 | return shared.singletonBatchRequest(client, '/update-account-tags', {
11 | account_info: params.id,
12 | tags: params.tags
13 | }, cb)
14 | },
15 |
16 | updateTagsBatch: (params, cb) => shared.batchRequest(client, '/update-account-tags', params, cb),
17 |
18 | updateAlias: (params, cb) => {
19 | const finalParams = {
20 | account_id: params.id,
21 | new_alias: params.alias
22 | }
23 | return shared.singletonBatchRequest(client, '/update-account-alias', finalParams, cb)
24 | },
25 |
26 | query: (params, cb) => shared.query(client, 'accounts', '/list-accounts', params, {cb}),
27 |
28 | queryAll: (params, processor, cb) => shared.queryAll(client, 'accounts', params, processor, cb),
29 |
30 | createReceiver: (params, cb) => shared.create(client, '/create-account-receiver', params, {cb, skipArray: true}),
31 |
32 | createAddress: (params, cb) => shared.create(client, '/create-account-receiver', params, {cb, skipArray: true}),
33 |
34 | createReceiverBatch: (params, cb) => shared.createBatch(client, '/create-account-receiver', params, {cb}),
35 |
36 | listAddresses: (params) => shared.query(client, 'accounts', '/list-addresses', params),
37 |
38 | validateAddresses: (address, cb) => shared.query(client, 'accounts', '/validate-address', {'address': address}, {cb})
39 | }
40 | }
41 |
42 | module.exports = accountsAPI
43 |
--------------------------------------------------------------------------------
/src/sdk/api/assets.js:
--------------------------------------------------------------------------------
1 | const shared = require('../shared')
2 |
3 | const assetsAPI = (client) => {
4 |
5 | return {
6 | create: (params, cb) => shared.create(client, '/create-asset', params, {cb, skipArray: true}),
7 |
8 | createBatch: (params, cb) => shared.createBatch(client, '/create-asset', params, {cb}),
9 |
10 | updateTags: (params, cb) => {
11 | const finalParams = {
12 | asset_info: params.id,
13 | tags: params.tags
14 | }
15 | return shared.singletonBatchRequest(client, '/update-asset-tags', finalParams, cb)
16 | },
17 |
18 | updateAlias: (params, cb) => {
19 | const finalParams = {
20 | id: params.id,
21 | alias: params.alias
22 | }
23 | return shared.singletonBatchRequest(client, '/update-asset-alias', finalParams, cb)
24 | },
25 |
26 | updateTagsBatch: (params, cb) => shared.batchRequest(client, '/update-asset-tags', params, cb),
27 |
28 | query: (params, cb) => shared.query(client, 'assets', '/list-assets', params, {cb}),
29 |
30 | queryAll: (params, processor, cb) => shared.queryAll(client, 'assets', params, processor, cb),
31 | }
32 | }
33 |
34 | module.exports = assetsAPI
35 |
--------------------------------------------------------------------------------
/src/sdk/api/authorizationGrants.js:
--------------------------------------------------------------------------------
1 | const shared = require('../shared')
2 | const util = require('../util')
3 |
4 | const authorizationGrants = (client) => ({
5 | create: (params, cb) => {
6 | params = Object.assign({}, params)
7 | if (params.guardType == 'x509') {
8 | params.guardData = util.sanitizeX509GuardData(params.guardData)
9 | }
10 |
11 | return shared.create(
12 | client,
13 | '/create-authorization-grant',
14 | params,
15 | {skipArray: true, cb}
16 | )
17 | },
18 |
19 | delete: (params, cb) => shared.tryCallback(
20 | client.request('/delete-authorization-grant', params),
21 | cb
22 | ),
23 |
24 | list: (cb) =>
25 | shared.query(client, 'accessTokens', '/list-access-tokens', {}, {cb}),
26 | })
27 |
28 | module.exports = authorizationGrants
29 |
--------------------------------------------------------------------------------
/src/sdk/api/backUp.js:
--------------------------------------------------------------------------------
1 | const shared = require('../shared')
2 |
3 | const backUp = (client) => {
4 | return {
5 | backup: (cb) => shared.tryCallback(
6 | client.request('/backup-wallet', {}, true),
7 | cb
8 | ),
9 |
10 | restore: (opts = {}, cb) => shared.tryCallback(
11 | client.request('/restore-wallet', opts),
12 | cb
13 | ),
14 |
15 | recovery: (opts = {}, cb) => shared.tryCallback(
16 | client.request('/recovery-wallet', opts),
17 | cb
18 | ),
19 |
20 | rescan: (cb) => shared.tryCallback(
21 | client.request('/rescan-wallet'),
22 | cb
23 | ),
24 |
25 | info: (cb) => shared.tryCallback(
26 | client.request('/wallet-info'),
27 | cb
28 | ),
29 | }
30 | }
31 |
32 | module.exports = backUp
33 |
--------------------------------------------------------------------------------
/src/sdk/api/balances.js:
--------------------------------------------------------------------------------
1 | const shared = require('../shared')
2 |
3 | const balancesAPI = (client) => {
4 | return {
5 | query: (params, cb) => shared.query(client, 'balances', '/list-balances', params, {cb}),
6 |
7 | queryAll: (params, processor, cb) => shared.queryAll(client, 'balances', params, processor, cb),
8 | }
9 | }
10 |
11 | module.exports = balancesAPI
12 |
--------------------------------------------------------------------------------
/src/sdk/api/config.js:
--------------------------------------------------------------------------------
1 | const shared = require('../shared')
2 |
3 | const configAPI = (client) => {
4 | return {
5 | reset: (everything = false, cb) => shared.tryCallback(
6 | client.request('/reset', {everything: everything}),
7 | cb
8 | ),
9 |
10 | configure: (opts = {}, cb) => shared.tryCallback(
11 | client.request('/configure', opts),
12 | cb
13 | ),
14 |
15 | mining: (miningState = false, cb) => shared.tryCallback(
16 | client.request('/set-mining', {is_mining: miningState}),
17 | cb
18 | ),
19 |
20 | info: (cb) => shared.tryCallback(
21 | client.request('/net-info'),
22 | cb
23 | ),
24 | }
25 | }
26 |
27 | module.exports = configAPI
28 |
--------------------------------------------------------------------------------
/src/sdk/api/mockHsmKeys.js:
--------------------------------------------------------------------------------
1 | const shared = require('../shared')
2 |
3 | const mockHsmKeysAPI = (client) => {
4 | return {
5 | create: (params, cb) => {
6 | let body = Object.assign({}, params)
7 | const uri = body.xprv ? '/import-private-key' : '/create-key'
8 |
9 | return shared.tryCallback(
10 | client.request(uri, body).then(data => data),
11 | cb
12 | )
13 | },
14 |
15 | query: (params, cb) => {
16 | if (Array.isArray(params.aliases) && params.aliases.length > 0) {
17 | params.pageSize = params.aliases.length
18 | }
19 |
20 | return shared.query(client, 'mockHsm.keys', '/list-keys', params, {cb})
21 | },
22 |
23 | resetPassword: (params) => client.request('/reset-key-password', params),
24 |
25 | updateAlias: (params, cb) => {
26 | const finalParams = {
27 | xpub: params.id,
28 | new_alias: params.alias
29 | }
30 | return shared.singletonBatchRequest(client, '/update-key-alias', finalParams, cb)
31 | },
32 |
33 | checkPassword: (params) => client.request('/check-key-password', params),
34 |
35 | queryAll: (params, processor, cb) => shared.queryAll(client, 'mockHsm.keys', params, processor, cb),
36 |
37 | export: (params) => client.request('/export-private-key', params),
38 |
39 | progress: () => client.request('/import-key-progress')
40 | }
41 | }
42 |
43 | module.exports = mockHsmKeysAPI
44 |
--------------------------------------------------------------------------------
/src/sdk/api/peer.js:
--------------------------------------------------------------------------------
1 | const shared = require('../shared')
2 |
3 | const peersAPI = (client) => {
4 | return {
5 | query: (params, cb) => shared.query(client, 'peers', '/list-peers', params, {cb}),
6 |
7 | queryAll: (params, processor, cb) => shared.queryAll(client, 'peers', params, processor, cb),
8 |
9 | connect: (params, cb) => shared.query(client, 'peers', '/connect-peer', params, {cb}),
10 |
11 | disconnect: (params, cb) => shared.query(client, 'peers', '/disconnect-peer', params, {cb}),
12 | }
13 | }
14 |
15 | module.exports = peersAPI
16 |
--------------------------------------------------------------------------------
/src/sdk/api/unspentOutputs.js:
--------------------------------------------------------------------------------
1 | const shared = require('../shared')
2 |
3 | const unspentOutputsAPI = (client) => {
4 | return {
5 | query: (params, cb) => shared.query(client, 'unspentOutputs', '/list-unspent-outputs', params, {cb}),
6 |
7 | queryAll: (params, processor, cb) => shared.queryAll(client, 'unspentOutputs', params, processor, cb),
8 | }
9 | }
10 |
11 | module.exports = unspentOutputsAPI
12 |
--------------------------------------------------------------------------------
/src/sdk/errors.js:
--------------------------------------------------------------------------------
1 | const lib = {
2 | create: function(type, message, props = {}) {
3 | let err
4 | if (props.body) {
5 | err = lib.newBatchError(props.body, props.requestId)
6 | } else {
7 | err = new Error(message)
8 | }
9 |
10 | err = Object.assign(err, props, {
11 | chainClientError: true,
12 | type: type,
13 | })
14 | return err
15 | },
16 |
17 | isChainError: function(err) {
18 | return err && !!err.chainClientError
19 | },
20 |
21 | isBatchError: function (v) {
22 | return v && v.code && !v.stack
23 | },
24 |
25 | newBatchError: function (body, requestId = false) {
26 | let err = new Error(lib.formatErrMsg(body, requestId))
27 | err.code = body.code
28 | err.chainMessage = body.msg
29 | err.detail = body.errorDetail
30 | err.requestId = requestId
31 | err.resp = body.resp
32 | return err
33 | },
34 |
35 | // TODO: remove me in favor of ErrorBanner.jsx rendering
36 | formatErrMsg: function(body, requestId) {
37 | let tokens = []
38 |
39 | if (typeof body.code === 'string' && body.code.length > 0) {
40 | tokens.push('Code: ' + body.code)
41 | }
42 |
43 | tokens.push('Message: ' + body.msg)
44 |
45 | if (typeof body.errorDetail === 'string' && body.errorDetail.length > 0) {
46 | tokens.push('Detail: ' + body.errorDetail)
47 | }
48 |
49 | if (requestId) {
50 | tokens.push('Request-ID: ' + requestId)
51 | }
52 |
53 | return tokens.join(' ')
54 | },
55 |
56 | types: {
57 | FETCH: 'FETCH',
58 | CONNECTIVITY: 'CONNECTIVITY',
59 | JSON: 'JSON',
60 | UNAUTHORIZED: 'UNAUTHORIZED',
61 | NOT_FOUND: 'NOT_FOUND',
62 | BAD_REQUEST: 'BAD_REQUEST',
63 | SERVER_ERROR: 'SERVER_ERROR',
64 | }
65 | }
66 |
67 | module.exports = lib
68 |
--------------------------------------------------------------------------------
/src/sdk/index.js:
--------------------------------------------------------------------------------
1 | const Client = require('./client')
2 | const Connection = require('./connection')
3 | const HsmSigner = require('./api/hsmSigner')
4 |
5 | module.exports = {
6 | Client,
7 | Connection,
8 | HsmSigner
9 | }
10 |
--------------------------------------------------------------------------------
/src/sdk/page.js:
--------------------------------------------------------------------------------
1 | class Page {
2 |
3 | constructor(data, client, memberPath) {
4 | this.items = []
5 |
6 | this.next = {}
7 |
8 |
9 | this.lastPage = false
10 |
11 | Object.assign(this, data)
12 |
13 | this.client = client
14 | this.memberPath = memberPath
15 | }
16 |
17 | nextPage(cb) {
18 | let queryOwner = this.client
19 | this.memberPath.split('.').forEach((member) => {
20 | queryOwner = queryOwner[member]
21 | })
22 |
23 | return queryOwner.query(this.next, cb)
24 | }
25 | }
26 |
27 | module.exports = Page
28 |
--------------------------------------------------------------------------------
/src/sdk/util.js:
--------------------------------------------------------------------------------
1 | const x509SubjectAttributes = {
2 | C: {array: true},
3 | O: {array: true},
4 | OU: {array: true},
5 | L: {array: true},
6 | ST: {array: true},
7 | STREET: {array: true},
8 | POSTALCODE: {array: true},
9 | SERIALNUMBER: {array: false},
10 | CN: {array: false},
11 | }
12 |
13 | const sanitizeX509GuardData = guardData => {
14 | const keys = Object.keys(guardData)
15 | if (keys.length !== 1 || keys[0].toLowerCase() !== 'subject') {
16 | throw new Error('X509 guard data must contain exactly one key, "subject"')
17 | }
18 |
19 | const newSubject = {}
20 | const oldSubject = guardData[keys[0]]
21 | for (let k in oldSubject) {
22 | const attrib = x509SubjectAttributes[k.toUpperCase()]
23 | if (!attrib) {
24 | throw new Error(`X509 guard data contains invalid subject attribute: ${k}`)
25 | }
26 |
27 | let v = oldSubject[k]
28 | if (!attrib.array && Array.isArray(v)) {
29 | throw new Error(`X509 guard data contains invalid array for attribute ${k}: ${v.toString()}`)
30 | } else if (attrib.array && !Array.isArray(v)) {
31 | newSubject[k] = [v]
32 | } else {
33 | newSubject[k] = v
34 | }
35 | }
36 |
37 | return {subject: newSubject}
38 | }
39 |
40 | module.exports = {
41 | sanitizeX509GuardData,
42 | }
43 |
--------------------------------------------------------------------------------
/src/utility/clipboard.js:
--------------------------------------------------------------------------------
1 | // Assumes the presence of an input element with ID #_copyInput
2 | export const copyToClipboard = value => {
3 | const listener = e => {
4 | e.clipboardData.setData('text/plain', value)
5 | e.preventDefault()
6 | document.removeEventListener('copy', listener)
7 | }
8 |
9 | // Required for Safari. Contents of selection are not used.
10 | document.getElementById('_copyInput').select()
11 |
12 | document.addEventListener('copy', listener)
13 | document.execCommand('copy')
14 | }
15 |
--------------------------------------------------------------------------------
/src/utility/componentClassNames.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import classNames from 'classnames'
3 |
4 | const componentClassNames = (owner, ...args) => {
5 | if (!React.Component.prototype.isPrototypeOf(owner)) {
6 | throw new Error('Component class must descend from React.Component')
7 | }
8 |
9 | return classNames(owner.constructor.name, args)
10 | }
11 |
12 | export default componentClassNames
13 |
--------------------------------------------------------------------------------
/src/utility/disableAutocomplete.js:
--------------------------------------------------------------------------------
1 | export default {
2 | autoComplete: 'off',
3 | autoCorrect: 'off',
4 | autoCapitalize: 'none',
5 | spellCheck: 'false'
6 | }
7 |
--------------------------------------------------------------------------------
/src/utility/environment.js:
--------------------------------------------------------------------------------
1 | /* global process */
2 |
3 | import chainSdk from 'sdk'
4 | import { store } from 'app'
5 |
6 | import { useRouterHistory } from 'react-router'
7 | import { createHistory } from 'history'
8 |
9 | let apiHost, basename
10 | if (process.env.NODE_ENV === 'production') {
11 | apiHost = window.location.origin
12 | basename = '/dashboard'
13 | } else {
14 | apiHost = process.env.API_URL || 'http://localhost:9888'
15 | basename = ''
16 | }
17 |
18 | export const chainClient = () => new chainSdk.Client({
19 | url: apiHost,
20 | accessToken: store.getState().core.clientToken
21 | })
22 |
23 | export const chainSigner = () => new chainSdk.HsmSigner()
24 |
25 | // react-router history object
26 | export const history = useRouterHistory(createHistory)({
27 | basename: basename
28 | })
29 |
30 | export const btmID = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
31 |
32 | export const pageSize = 25
33 | export const UTXOpageSize = 10
34 |
35 | export const testnetInfoUrl = process.env.TESTNET_INFO_URL || 'https://testnet-info.chain.com'
36 | export const testnetUrl = process.env.TESTNET_GENERATOR_URL || 'https://testnet.chain.com'
37 | export const docsRoot = 'https://github.com/bytom/bytom/wiki'
38 |
39 | export const releaseUrl = 'https://github.com/Bytom/bytom/releases'
40 |
--------------------------------------------------------------------------------
/src/utility/localStorage.js:
--------------------------------------------------------------------------------
1 | export const clear = () => {
2 | try {
3 | localStorage.clear()
4 | } catch (err) {
5 | // Local storage is not available.
6 | }
7 | }
8 |
9 | export const exportState = (store) => () => {
10 | const state = store.getState()
11 | const exportable = {
12 | core: {
13 | clientToken: (state.core || {}).clientToken,
14 |
15 | // TODO: If the dashboard has a way of probing the core for a token
16 | // requirement, we won't need to store these anymore.
17 | requireClientToken: (state.core || {}).requireClientToken,
18 | validToken: (state.core || {}).validToken,
19 | btmAmountUnit: state.core.btmAmountUnit,
20 | },
21 | app:{
22 | navAdvancedState : state.app.navAdvancedState,
23 | },
24 | transaction: {
25 | generated: (state.transaction || {}).generated,
26 | },
27 | tutorial: state.tutorial
28 | }
29 |
30 | try {
31 | localStorage.setItem('reduxState', JSON.stringify(exportable))
32 | } catch (err) { /* localstorage not available */ }
33 | }
34 |
35 | export const importState = () => {
36 | let state
37 | try {
38 | state = localStorage.getItem('reduxState')
39 | } catch (err) { /* localstorage not available */ }
40 |
41 | if (!state) return {}
42 |
43 | try {
44 | return JSON.parse(state)
45 | } catch (_) {
46 | return {}
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/utility/math.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 |
3 | export const sum = function(items, prop){
4 | return items.reduce( function(a, b){
5 | return a + Number(_.get(b,prop))
6 | }, 0)
7 | }
8 |
9 | export const formatBytes = function(bytes,decimals) {
10 | if (bytes == 0) return '0 Bytes'
11 | let k = 1024,
12 | dm = decimals <= 0 ? 0 : decimals || 2,
13 | sizes = ['B', 'KB', 'MB', 'GB', 'TB'],
14 | i = Math.floor(Math.log(bytes) / Math.log(k))
15 | return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
16 | }
17 |
18 | export const splitSlice = function (str, len) {
19 | let ret = [ ]
20 | for (let offset = 0, strLen = str.length; offset < strLen; offset += len) {
21 | ret.push(str.slice(offset, len + offset))
22 | }
23 | return ret
24 | }
25 |
--------------------------------------------------------------------------------
/src/utility/string.js:
--------------------------------------------------------------------------------
1 | import _pluralize from 'pluralize'
2 | import { snakeCase } from 'lodash'
3 |
4 | export const pluralize = _pluralize
5 |
6 | export const capitalize = (string) => {
7 | return string.charAt(0).toUpperCase() + string.slice(1)
8 | }
9 |
10 | export const humanize = (string) => {
11 | return snakeCase(string)
12 | .replace(/_/g, ' ')
13 | }
14 |
15 | export const parseNonblankJSON = (json) => {
16 | json = json || ''
17 | json = json.trim()
18 |
19 | if (json == '') {
20 | return null
21 | }
22 |
23 | return JSON.parse(json)
24 | }
25 |
26 | String.prototype.isUpperCase = function() {
27 | return this.valueOf().toUpperCase() === this.valueOf();
28 | }
29 |
--------------------------------------------------------------------------------
/static/fonts/dotsfont/dotsfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/fonts/dotsfont/dotsfont.eot
--------------------------------------------------------------------------------
/static/fonts/dotsfont/dotsfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/fonts/dotsfont/dotsfont.ttf
--------------------------------------------------------------------------------
/static/fonts/dotsfont/dotsfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/fonts/dotsfont/dotsfont.woff
--------------------------------------------------------------------------------
/static/fonts/nitti-normal.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/fonts/nitti-normal.woff
--------------------------------------------------------------------------------
/static/fonts/nittigrotesk-medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/fonts/nittigrotesk-medium.woff
--------------------------------------------------------------------------------
/static/fonts/nittigrotesk-normal.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/fonts/nittigrotesk-normal.woff
--------------------------------------------------------------------------------
/static/images/backup/backup-active.svg:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/static/images/capslock-green.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/static/images/capslock.svg:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/static/images/chain-favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/chain-favicon.png
--------------------------------------------------------------------------------
/static/images/chevron-blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/chevron-blue.png
--------------------------------------------------------------------------------
/static/images/chevron-green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/chevron-green.png
--------------------------------------------------------------------------------
/static/images/chevron.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/chevron.png
--------------------------------------------------------------------------------
/static/images/config/join-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/config/join-active.png
--------------------------------------------------------------------------------
/static/images/config/join.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/config/join.png
--------------------------------------------------------------------------------
/static/images/config/new-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/config/new-active.png
--------------------------------------------------------------------------------
/static/images/config/new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/config/new.png
--------------------------------------------------------------------------------
/static/images/config/testnet-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/config/testnet-active.png
--------------------------------------------------------------------------------
/static/images/config/testnet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/config/testnet.png
--------------------------------------------------------------------------------
/static/images/console-window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/copy.svg:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/static/images/empty/account.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/images/empty/asset.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/images/empty/transaction.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/favicon.png
--------------------------------------------------------------------------------
/static/images/logo-bytom-white.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/static/images/logo-shadowed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/logo-shadowed.png
--------------------------------------------------------------------------------
/static/images/logo-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/logo-white.png
--------------------------------------------------------------------------------
/static/images/navigation/account-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/account-active.png
--------------------------------------------------------------------------------
/static/images/navigation/account.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/account.png
--------------------------------------------------------------------------------
/static/images/navigation/asset-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/asset-active.png
--------------------------------------------------------------------------------
/static/images/navigation/asset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/asset.png
--------------------------------------------------------------------------------
/static/images/navigation/balance-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/balance-active.png
--------------------------------------------------------------------------------
/static/images/navigation/balance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/balance.png
--------------------------------------------------------------------------------
/static/images/navigation/client-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/client-active.png
--------------------------------------------------------------------------------
/static/images/navigation/client.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/client.png
--------------------------------------------------------------------------------
/static/images/navigation/core-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/core-active.png
--------------------------------------------------------------------------------
/static/images/navigation/core.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/core.png
--------------------------------------------------------------------------------
/static/images/navigation/docs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/docs.png
--------------------------------------------------------------------------------
/static/images/navigation/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/error.png
--------------------------------------------------------------------------------
/static/images/navigation/feed-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/feed-active.png
--------------------------------------------------------------------------------
/static/images/navigation/feed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/feed.png
--------------------------------------------------------------------------------
/static/images/navigation/help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/help.png
--------------------------------------------------------------------------------
/static/images/navigation/logout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/logout.png
--------------------------------------------------------------------------------
/static/images/navigation/mockhsm-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/mockhsm-active.png
--------------------------------------------------------------------------------
/static/images/navigation/mockhsm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/mockhsm.png
--------------------------------------------------------------------------------
/static/images/navigation/network-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/network-active.png
--------------------------------------------------------------------------------
/static/images/navigation/network.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/network.png
--------------------------------------------------------------------------------
/static/images/navigation/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/settings.png
--------------------------------------------------------------------------------
/static/images/navigation/transaction-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/transaction-active.png
--------------------------------------------------------------------------------
/static/images/navigation/transaction.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/transaction.png
--------------------------------------------------------------------------------
/static/images/navigation/tutorial-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/tutorial-active.png
--------------------------------------------------------------------------------
/static/images/navigation/tutorial.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/tutorial.png
--------------------------------------------------------------------------------
/static/images/navigation/unspent-active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/unspent-active.png
--------------------------------------------------------------------------------
/static/images/navigation/unspent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/navigation/unspent.png
--------------------------------------------------------------------------------
/static/images/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/search.png
--------------------------------------------------------------------------------
/static/images/sum.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BytomDAO/bytom-dashboard/69d4d4b5d445456c4f473631510b2801a0fb06b0/static/images/sum.png
--------------------------------------------------------------------------------
/static/images/transactions/coinbase.svg:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/static/images/transactions/fail.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/transactions/issue.svg:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/static/images/transactions/received.svg:
--------------------------------------------------------------------------------
1 |
40 |
--------------------------------------------------------------------------------
/static/images/transactions/retire.svg:
--------------------------------------------------------------------------------
1 |
29 |
--------------------------------------------------------------------------------
/static/images/transactions/sent.svg:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/static/images/transactions/success.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/images/warning.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/static/styles/_bootstrap-overrides.scss:
--------------------------------------------------------------------------------
1 | $grid-gutter-width: 30px;
2 |
3 | $font-size-caps: 12px;
4 | $font-size-chrome: 12px;
5 | $font-size-base: 13px;
6 | $font-size-code: $font-size-base;
7 | $font-size-nav: 14px;
8 | $font-size-form-section-title: $font-size-nav;
9 | $font-size-section-title: 16px;
10 | $font-size-btn: $font-size-nav;
11 | $font-size-btn-lg: $font-size-section-title;
12 | $font-size-page-title: 20px;
13 | $font-size-close: 24px;
14 | $font-size-h2: 26px;
15 | $line-height-base: 1.7;
16 | $font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
17 | "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
18 | SimSun, sans-serif;
19 | $font-family-monospace: "Nitti", Menlo, Monaco, Consolas, "Courier New", monospace;
20 |
21 | // Disable responsive styles below 900px wide
22 | $screen-xs: 1px;
23 | $screen-sm: 2px;
24 | $container-sm: 900px;
25 | $container-desktop: 1040px + $grid-gutter-width;
26 | $screen-md: $container-desktop;
27 |
28 | $brand-primary: #1977d6;
29 | $brand-danger: #EF5354;
30 |
31 | $text-color: #8c8e94;
32 | $headings-color: #222834;
33 |
34 | $navbar-default-bg: #20252D;
35 |
36 | $link-color: $brand-primary;
37 | $navbar-default-link-color: transparentize(white, 0.1);
38 | $navbar-default-link-active-color: white;
39 | $navbar-default-link-hover-color: white;
40 |
41 | $input-border-focus: $brand-primary;
42 |
43 | $page-header-border-color: #e3e3e3;
44 | $pre-bg: #fff;
45 | $panel-footer-bg: #fff;
46 | $pre-border-color: transparent;
47 |
48 | $code-color: #747c89;
49 | $code-bg: none;
50 |
51 | $border-radius-base: 3px;
52 | $border-radius-large: 3px;
53 |
54 | $form-group-margin-bottom: 30px;
55 |
56 | $input-border-focus: #257bf2;
57 |
--------------------------------------------------------------------------------
/static/styles/app.scss:
--------------------------------------------------------------------------------
1 | @import 'resources';
2 | @import 'body';
3 |
--------------------------------------------------------------------------------
/webpack/webpack.dll.js:
--------------------------------------------------------------------------------
1 | // Original from https://github.com/mxstbr/react-boilerplate/
2 |
3 | /*eslint-env node*/
4 |
5 | /**
6 | * WEBPACK DLL GENERATOR
7 | *
8 | * This profile is used to cache webpack's module
9 | * contexts for external library and framework type
10 | * dependencies which will usually not change often enough
11 | * to warrant building them from scratch every time we use
12 | * the webpack process.
13 | */
14 |
15 | const { join } = require('path')
16 | const webpack = require('webpack')
17 | const pkg = require(join(process.cwd(), 'package.json'))
18 |
19 | const outputPath = join(process.cwd(), 'node_modules/dashboard-dlls')
20 |
21 | const config = require('./webpack.base')({
22 | context: process.cwd(),
23 | entry: {dependencies: Object.keys(pkg.dependencies)},
24 | devtool: 'eval',
25 | output: {
26 | filename: '[name].dll.js',
27 | path: outputPath,
28 | library: '[name]',
29 | },
30 | plugins: [
31 | new webpack.DllPlugin({
32 | name: '[name]',
33 | path: join(outputPath, 'manifest.json')
34 | }),
35 | ],
36 | })
37 |
38 | module.exports = config
39 |
--------------------------------------------------------------------------------