├── .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 | 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 => )} 11 | 12 | 13 | 14 | 15 | {this.props.children} 16 | 17 |
{title}
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 |