From fe03fc2374da7ff4a6a51ab95fd58a3f70c5d1c0 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Mon, 20 Jan 2020 18:46:39 -0800 Subject: [PATCH] Initial commit --- .babelrc | 29 + .editorconfig | 12 + .eslintignore | 56 + .eslintrc | 56 + .gitattributes | 7 + .github/ISSUE_TEMPLATE/bug_report.md | 31 + .gitignore | 51 + CHANGELOG.md | 13 + CONTRIBUTORS.md | 4 + LICENSE | 674 + README.md | 37 + app/app.html | 15 + app/assets/images/alert-triangle.svg | 1 + app/assets/images/alice.png | Bin 0 -> 92082 bytes app/assets/images/arrow-back-blue.svg | 1 + app/assets/images/arrow-down-circle-green.svg | 13 + app/assets/images/arrow-left-blue.svg | 1 + app/assets/images/arrow-right-blue.svg | 1 + app/assets/images/arrow-up-circle-red.svg | 13 + app/assets/images/baseline-timer-24px.svg | 1 + app/assets/images/bob-logo-circle.svg | 13 + app/assets/images/bob-logo.svg | 14 + app/assets/images/brick-loader.svg | 1 + app/assets/images/calendar-icon.svg | 14 + app/assets/images/check-circle-blue.svg | 12 + app/assets/images/check-circle-white.svg | 12 + app/assets/images/check-circle.svg | 1 + app/assets/images/check-green.svg | 1 + app/assets/images/check-icon-white.svg | 14 + app/assets/images/check-square-blue.svg | 4 + app/assets/images/check-square.svg | 4 + app/assets/images/delete-gray.svg | 1 + app/assets/images/download-blue.png | Bin 0 -> 556 bytes app/assets/images/edit-gray.svg | 1 + app/assets/images/goosig-1.svg | 23 + app/assets/images/goosig-2.svg | 43 + app/assets/images/goosig-3.svg | 25 + app/assets/images/heart-red-outline.svg | 11 + app/assets/images/heart-red.svg | 11 + app/assets/images/heart.svg | 11 + app/assets/images/icon-128.png | Bin 0 -> 5198 bytes app/assets/images/icon-16.png | Bin 0 -> 590 bytes app/assets/images/icon-48.png | Bin 0 -> 1547 bytes app/assets/images/info.svg | 1 + app/assets/images/notification-24.png | Bin 0 -> 288 bytes app/assets/images/open-new-window.svg | 1 + app/assets/images/pending-black.svg | 13 + app/assets/images/search.svg | 1 + app/assets/images/settings-gray.svg | 4 + app/assets/images/settings.svg | 4 + app/assets/images/the-cat.png | Bin 0 -> 29004 bytes app/assets/images/trash-red.svg | 1 + app/assets/images/unchecked-square-gray.svg | 4 + app/assets/images/x-gray.svg | 12 + app/assets/images/x-red.svg | 12 + app/assets/images/x-square.svg | 9 + app/assets/images/x-white.svg | 12 + app/background/analytics/client.js | 8 + app/background/analytics/service.js | 98 + app/background/db/client.js | 9 + app/background/db/service.js | 65 + app/background/ipc/ipc.js | 156 + app/background/ipc/service.js | 14 + app/background/ledger/client.js | 6 + app/background/ledger/service.js | 70 + app/background/logger/client.js | 9 + app/background/logger/service.js | 115 + app/background/node/client.js | 7 + app/background/node/service.js | 146 + app/components/Alert/alert.scss | 13 + app/components/Alert/index.js | 27 + .../AuctionGraph/auction-graph.scss | 54 + app/components/AuctionGraph/index.js | 152 + app/components/AuctionPanel/index.js | 86 + app/components/AuctionPanel/index.scss | 47 + .../BidSearchInput/bid-search-input.scss | 23 + app/components/BidSearchInput/index.js | 16 + app/components/Blocktime/index.js | 104 + app/components/Checkbox/index.js | 30 + app/components/Checkbox/index.scss | 30 + app/components/Collapsible/collapsible.scss | 50 + app/components/Collapsible/index.js | 73 + .../connect-ledger-step.scss | 33 + .../ConnectLedgerStep/defaultSteps.js | 24 + app/components/ConnectLedgerStep/index.js | 29 + app/components/CopyButton/copy-btn.scss | 56 + app/components/CopyButton/index.js | 39 + app/components/Dropdown/index.js | 69 + app/components/Dropdown/index.scss | 107 + app/components/Hash/index.js | 9 + app/components/Header/header.scss | 72 + app/components/Header/index.js | 39 + app/components/IdleModal/idle-modal.scss | 44 + app/components/IdleModal/index.js | 114 + app/components/LedgerModal/index.js | 107 + app/components/LedgerModal/ledger-modal.scss | 59 + app/components/Logo/index.js | 16 + app/components/Logo/logo.scss | 26 + app/components/Modal/MiniModal.js | 41 + app/components/Modal/index.js | 33 + app/components/Modal/mini-modal.scss | 43 + app/components/Modal/modal.scss | 22 + app/components/Notification/index.js | 91 + app/components/Notification/notification.scss | 52 + app/components/ProgressBar/index.js | 12 + app/components/ProgressBar/progress-bar.scss | 17 + app/components/ProofModal/index.js | 159 + app/components/ProofModal/proof-modal.scss | 113 + app/components/ReceiveModal/index.js | 82 + app/components/ReceiveModal/receive.scss | 150 + app/components/SendModal/index.js | 351 + app/components/SendModal/send.scss | 395 + app/components/Sidebar/index.js | 138 + app/components/Sidebar/sidebar.scss | 134 + app/components/SplashScreen/index.js | 76 + app/components/StatusBar/index.js | 54 + app/components/StatusBar/status.scss | 41 + app/components/Submittable/index.js | 18 + app/components/Submittable/index.scss | 3 + app/components/SuccessModal/SuccessModal.scss | 89 + app/components/SuccessModal/index.js | 52 + app/components/TLDInput/TLDInput.scss | 86 + app/components/TLDInput/index.js | 95 + app/components/Table/index.js | 55 + app/components/Table/index.scss | 57 + app/components/Tooltipable/index.js | 32 + app/components/Tooltipable/tooltipable.scss | 35 + app/components/Topbar/index.js | 199 + app/components/Topbar/topbar.scss | 182 + .../Transactions/Transaction/index.js | 141 + app/components/Transactions/index.js | 252 + app/components/Transactions/index.scss | 281 + app/components/WizardHeader/index.js | 50 + app/components/WizardHeader/index.scss | 33 + app/constants/networks.js | 12 + app/constants/routes.json | 4 + app/db/names.js | 25 + app/db/system.js | 47 + app/db/watching.js | 15 + app/ducks/backgroundMonitor.js | 100 + app/ducks/bids.js | 29 + app/ducks/index.js | 32 + app/ducks/ledger.js | 44 + app/ducks/ledgerManager.js | 34 + app/ducks/myDomains.js | 138 + app/ducks/names.js | 182 + app/ducks/namesReducer.js | 94 + app/ducks/node.js | 75 + app/ducks/nodeReducer.js | 50 + app/ducks/notifications.js | 44 + app/ducks/walletActions.js | 329 + app/ducks/walletReducer.js | 79 + app/ducks/watching.js | 69 + app/global.scss | 53 + app/hsd.html | 9 + app/hsdMain.js | 54 + app/index.js | 23 + app/main.js | 61 + app/mainWindow.js | 45 + app/menu.js | 249 + app/pages/Account/account.scss | 160 + app/pages/Account/index.js | 65 + app/pages/AcountLogin/index.js | 79 + app/pages/AcountLogin/login.scss | 61 + app/pages/App/app.scss | 121 + app/pages/App/index.js | 235 + app/pages/Auction/BidActionPanel/BidNow.js | 431 + app/pages/Auction/BidActionPanel/OpenBid.js | 179 + app/pages/Auction/BidActionPanel/Owned.js | 88 + app/pages/Auction/BidActionPanel/Reserved.js | 30 + app/pages/Auction/BidActionPanel/Reveal.js | 290 + app/pages/Auction/BidActionPanel/Sold.js | 107 + app/pages/Auction/BidActionPanel/index.js | 158 + app/pages/Auction/BidHistory.js | 73 + app/pages/Auction/BidReminder.js | 30 + app/pages/Auction/VickreyProcess.js | 29 + app/pages/Auction/add-to-calendar.scss | 65 + app/pages/Auction/bid-history.scss | 34 + app/pages/Auction/domains.scss | 456 + app/pages/Auction/index.js | 258 + app/pages/Auction/vickrey-process.scss | 6 + app/pages/ContentArea/index.js | 16 + app/pages/ContentArea/index.scss | 16 + app/pages/CopySeed/copy.scss | 66 + app/pages/CopySeed/index.js | 72 + app/pages/DomainManager/domain-manager.scss | 39 + app/pages/DomainManager/index.js | 86 + app/pages/Footer/index.js | 84 + app/pages/Footer/index.scss | 69 + app/pages/GetCoins/get-coins.scss | 201 + app/pages/GetCoins/index.js | 141 + app/pages/MyDomain/CreateRecord.js | 130 + app/pages/MyDomain/DomainDetails.js | 88 + app/pages/MyDomain/EditableRecord.js | 173 + app/pages/MyDomain/Records.js | 314 + app/pages/MyDomain/index.js | 147 + app/pages/MyDomain/my-domain.scss | 365 + app/pages/NetworkPicker/index.js | 75 + app/pages/NetworkPicker/network-picker.scss | 3 + .../Onboarding/BackUpSeedWarning/index.js | 66 + .../Onboarding/BackUpSeedWarning/index.scss | 46 + .../Onboarding/ConfirmSeed/confirm-seed.scss | 26 + app/pages/Onboarding/ConfirmSeed/index.js | 102 + .../Onboarding/ConnectLedger/connect.scss | 35 + app/pages/Onboarding/ConnectLedger/index.js | 112 + .../Onboarding/ConnectLedgerFlow/index.js | 39 + .../Onboarding/CreateNewAccount/index.js | 142 + .../Onboarding/CreatePassword/create.scss | 97 + app/pages/Onboarding/CreatePassword/index.js | 134 + .../ExistingAccountOptions/existing.scss | 83 + .../ExistingAccountOptions/index.js | 67 + .../Onboarding/FundAccessOptions/access.scss | 77 + .../Onboarding/FundAccessOptions/index.js | 57 + .../ImportSeedEnterMnemonic/importenter.scss | 107 + .../ImportSeedEnterMnemonic/index.js | 80 + app/pages/Onboarding/ImportSeedFlow/index.js | 136 + .../ImportSeedWarning/importwarning.scss | 93 + .../Onboarding/ImportSeedWarning/index.js | 76 + app/pages/Onboarding/OptInAnalytics/index.js | 67 + .../OptInAnalytics/opt-in-analytics.scss | 50 + app/pages/Onboarding/Terms/index.js | 57 + app/pages/Onboarding/Terms/terms.scss | 97 + app/pages/Root.js | 18 + app/pages/SearchTLD/index.js | 25 + app/pages/SearchTLD/searchTLD.scss | 19 + app/pages/Settings/AccountIndexModal.js | 25 + .../Settings/InterstitialWarningModal.js | 114 + app/pages/Settings/RevealSeedModal.js | 110 + app/pages/Settings/account-index-modal.scss | 24 + app/pages/Settings/index.js | 100 + app/pages/Settings/index.scss | 70 + .../Settings/interstitial-warning-modal.scss | 61 + app/pages/Settings/reveal-seed-modal.scss | 40 + app/pages/Terms/index.js | 56 + app/pages/Terms/terms.scss | 92 + app/pages/Watching/index.js | 223 + app/pages/Watching/watching.scss | 207 + app/pages/YourBids/BidAction.js | 142 + app/pages/YourBids/BidStatus.js | 165 + app/pages/YourBids/BidTimeLeft.js | 65 + app/pages/YourBids/index.js | 112 + app/pages/YourBids/your-bids.scss | 159 + app/preload.js | 25 + app/sentry.js | 9 + app/store/configureStore.dev.js | 67 + app/store/configureStore.js | 13 + app/store/configureStore.prod.js | 16 + app/utils/balances.js | 19 + app/utils/client.js | 49 + app/utils/ellipsify.js | 7 + app/utils/fs.js | 32 + app/utils/helpers.js | 54 + app/utils/isDev.js | 14 + app/utils/logClient.js | 23 + app/utils/nameChecker.js | 231 + app/utils/nameHelpers.js | 72 + app/utils/nodeClient.js | 74 + app/utils/pify.js | 9 + app/utils/recordHelpers.js | 310 + app/utils/timeConverter.js | 49 + app/utils/verifyAddress.js | 10 + app/utils/walletClient.js | 288 + app/variables.scss | 417 + .../fuse/bitap/bitap_matched_indices.js | 26 + .../fuse/bitap/bitap_pattern_alphabet.js | 14 + app/vendor/fuse/bitap/bitap_regex_search.js | 22 + app/vendor/fuse/bitap/bitap_score.js | 11 + app/vendor/fuse/bitap/bitap_search.js | 155 + app/vendor/fuse/bitap/index.js | 84 + app/vendor/fuse/helpers/deep_value.js | 39 + app/vendor/fuse/helpers/is_array.js | 1 + app/vendor/fuse/index.js | 414 + configs/webpack.config.renderer.dev.babel.js | 211 + configs/webpack.config.renderer.prod.babel.js | 195 + package-lock.json | 11310 ++++++++++++++++ package.json | 148 + resources/icon.icns | Bin 0 -> 139867 bytes resources/icon.ico | Bin 0 -> 130786 bytes resources/icon.png | Bin 0 -> 13493 bytes resources/icons/1024x1024.png | Bin 0 -> 48386 bytes resources/icons/128x128.png | Bin 0 -> 5755 bytes resources/icons/16x16.png | Bin 0 -> 592 bytes resources/icons/24x24.png | Bin 0 -> 973 bytes resources/icons/256x256.png | Bin 0 -> 13493 bytes resources/icons/32x32.png | Bin 0 -> 1307 bytes resources/icons/48x48.png | Bin 0 -> 1986 bytes resources/icons/512x512.png | Bin 0 -> 27071 bytes resources/icons/64x64.png | Bin 0 -> 2768 bytes resources/icons/96x96.png | Bin 0 -> 4093 bytes scripts/clean.sh | 5 + scripts/package.sh | 18 + scripts/remove-appdata-osx.sh | 18 + 292 files changed, 32289 insertions(+) create mode 100644 .babelrc create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc create mode 100644 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTORS.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 app/app.html create mode 100644 app/assets/images/alert-triangle.svg create mode 100644 app/assets/images/alice.png create mode 100644 app/assets/images/arrow-back-blue.svg create mode 100644 app/assets/images/arrow-down-circle-green.svg create mode 100644 app/assets/images/arrow-left-blue.svg create mode 100644 app/assets/images/arrow-right-blue.svg create mode 100644 app/assets/images/arrow-up-circle-red.svg create mode 100644 app/assets/images/baseline-timer-24px.svg create mode 100644 app/assets/images/bob-logo-circle.svg create mode 100644 app/assets/images/bob-logo.svg create mode 100644 app/assets/images/brick-loader.svg create mode 100644 app/assets/images/calendar-icon.svg create mode 100644 app/assets/images/check-circle-blue.svg create mode 100644 app/assets/images/check-circle-white.svg create mode 100644 app/assets/images/check-circle.svg create mode 100644 app/assets/images/check-green.svg create mode 100644 app/assets/images/check-icon-white.svg create mode 100644 app/assets/images/check-square-blue.svg create mode 100644 app/assets/images/check-square.svg create mode 100644 app/assets/images/delete-gray.svg create mode 100644 app/assets/images/download-blue.png create mode 100644 app/assets/images/edit-gray.svg create mode 100644 app/assets/images/goosig-1.svg create mode 100644 app/assets/images/goosig-2.svg create mode 100644 app/assets/images/goosig-3.svg create mode 100644 app/assets/images/heart-red-outline.svg create mode 100644 app/assets/images/heart-red.svg create mode 100644 app/assets/images/heart.svg create mode 100644 app/assets/images/icon-128.png create mode 100644 app/assets/images/icon-16.png create mode 100644 app/assets/images/icon-48.png create mode 100644 app/assets/images/info.svg create mode 100644 app/assets/images/notification-24.png create mode 100644 app/assets/images/open-new-window.svg create mode 100644 app/assets/images/pending-black.svg create mode 100644 app/assets/images/search.svg create mode 100644 app/assets/images/settings-gray.svg create mode 100644 app/assets/images/settings.svg create mode 100644 app/assets/images/the-cat.png create mode 100644 app/assets/images/trash-red.svg create mode 100644 app/assets/images/unchecked-square-gray.svg create mode 100644 app/assets/images/x-gray.svg create mode 100644 app/assets/images/x-red.svg create mode 100644 app/assets/images/x-square.svg create mode 100644 app/assets/images/x-white.svg create mode 100644 app/background/analytics/client.js create mode 100644 app/background/analytics/service.js create mode 100644 app/background/db/client.js create mode 100644 app/background/db/service.js create mode 100644 app/background/ipc/ipc.js create mode 100644 app/background/ipc/service.js create mode 100644 app/background/ledger/client.js create mode 100644 app/background/ledger/service.js create mode 100644 app/background/logger/client.js create mode 100644 app/background/logger/service.js create mode 100644 app/background/node/client.js create mode 100644 app/background/node/service.js create mode 100644 app/components/Alert/alert.scss create mode 100644 app/components/Alert/index.js create mode 100644 app/components/AuctionGraph/auction-graph.scss create mode 100644 app/components/AuctionGraph/index.js create mode 100644 app/components/AuctionPanel/index.js create mode 100644 app/components/AuctionPanel/index.scss create mode 100644 app/components/BidSearchInput/bid-search-input.scss create mode 100644 app/components/BidSearchInput/index.js create mode 100644 app/components/Blocktime/index.js create mode 100644 app/components/Checkbox/index.js create mode 100644 app/components/Checkbox/index.scss create mode 100644 app/components/Collapsible/collapsible.scss create mode 100644 app/components/Collapsible/index.js create mode 100644 app/components/ConnectLedgerStep/connect-ledger-step.scss create mode 100644 app/components/ConnectLedgerStep/defaultSteps.js create mode 100644 app/components/ConnectLedgerStep/index.js create mode 100644 app/components/CopyButton/copy-btn.scss create mode 100644 app/components/CopyButton/index.js create mode 100644 app/components/Dropdown/index.js create mode 100644 app/components/Dropdown/index.scss create mode 100644 app/components/Hash/index.js create mode 100644 app/components/Header/header.scss create mode 100644 app/components/Header/index.js create mode 100644 app/components/IdleModal/idle-modal.scss create mode 100644 app/components/IdleModal/index.js create mode 100644 app/components/LedgerModal/index.js create mode 100644 app/components/LedgerModal/ledger-modal.scss create mode 100644 app/components/Logo/index.js create mode 100644 app/components/Logo/logo.scss create mode 100644 app/components/Modal/MiniModal.js create mode 100644 app/components/Modal/index.js create mode 100644 app/components/Modal/mini-modal.scss create mode 100644 app/components/Modal/modal.scss create mode 100644 app/components/Notification/index.js create mode 100644 app/components/Notification/notification.scss create mode 100644 app/components/ProgressBar/index.js create mode 100644 app/components/ProgressBar/progress-bar.scss create mode 100644 app/components/ProofModal/index.js create mode 100644 app/components/ProofModal/proof-modal.scss create mode 100644 app/components/ReceiveModal/index.js create mode 100644 app/components/ReceiveModal/receive.scss create mode 100644 app/components/SendModal/index.js create mode 100644 app/components/SendModal/send.scss create mode 100644 app/components/Sidebar/index.js create mode 100644 app/components/Sidebar/sidebar.scss create mode 100644 app/components/SplashScreen/index.js create mode 100644 app/components/StatusBar/index.js create mode 100644 app/components/StatusBar/status.scss create mode 100644 app/components/Submittable/index.js create mode 100644 app/components/Submittable/index.scss create mode 100644 app/components/SuccessModal/SuccessModal.scss create mode 100644 app/components/SuccessModal/index.js create mode 100644 app/components/TLDInput/TLDInput.scss create mode 100644 app/components/TLDInput/index.js create mode 100644 app/components/Table/index.js create mode 100644 app/components/Table/index.scss create mode 100644 app/components/Tooltipable/index.js create mode 100644 app/components/Tooltipable/tooltipable.scss create mode 100644 app/components/Topbar/index.js create mode 100644 app/components/Topbar/topbar.scss create mode 100644 app/components/Transactions/Transaction/index.js create mode 100644 app/components/Transactions/index.js create mode 100644 app/components/Transactions/index.scss create mode 100644 app/components/WizardHeader/index.js create mode 100644 app/components/WizardHeader/index.scss create mode 100644 app/constants/networks.js create mode 100644 app/constants/routes.json create mode 100644 app/db/names.js create mode 100644 app/db/system.js create mode 100644 app/db/watching.js create mode 100644 app/ducks/backgroundMonitor.js create mode 100644 app/ducks/bids.js create mode 100644 app/ducks/index.js create mode 100644 app/ducks/ledger.js create mode 100644 app/ducks/ledgerManager.js create mode 100644 app/ducks/myDomains.js create mode 100644 app/ducks/names.js create mode 100644 app/ducks/namesReducer.js create mode 100644 app/ducks/node.js create mode 100644 app/ducks/nodeReducer.js create mode 100644 app/ducks/notifications.js create mode 100644 app/ducks/walletActions.js create mode 100644 app/ducks/walletReducer.js create mode 100644 app/ducks/watching.js create mode 100644 app/global.scss create mode 100644 app/hsd.html create mode 100644 app/hsdMain.js create mode 100644 app/index.js create mode 100644 app/main.js create mode 100644 app/mainWindow.js create mode 100644 app/menu.js create mode 100644 app/pages/Account/account.scss create mode 100644 app/pages/Account/index.js create mode 100644 app/pages/AcountLogin/index.js create mode 100644 app/pages/AcountLogin/login.scss create mode 100644 app/pages/App/app.scss create mode 100644 app/pages/App/index.js create mode 100644 app/pages/Auction/BidActionPanel/BidNow.js create mode 100644 app/pages/Auction/BidActionPanel/OpenBid.js create mode 100644 app/pages/Auction/BidActionPanel/Owned.js create mode 100644 app/pages/Auction/BidActionPanel/Reserved.js create mode 100644 app/pages/Auction/BidActionPanel/Reveal.js create mode 100644 app/pages/Auction/BidActionPanel/Sold.js create mode 100644 app/pages/Auction/BidActionPanel/index.js create mode 100644 app/pages/Auction/BidHistory.js create mode 100644 app/pages/Auction/BidReminder.js create mode 100644 app/pages/Auction/VickreyProcess.js create mode 100644 app/pages/Auction/add-to-calendar.scss create mode 100644 app/pages/Auction/bid-history.scss create mode 100644 app/pages/Auction/domains.scss create mode 100644 app/pages/Auction/index.js create mode 100644 app/pages/Auction/vickrey-process.scss create mode 100644 app/pages/ContentArea/index.js create mode 100644 app/pages/ContentArea/index.scss create mode 100644 app/pages/CopySeed/copy.scss create mode 100644 app/pages/CopySeed/index.js create mode 100644 app/pages/DomainManager/domain-manager.scss create mode 100644 app/pages/DomainManager/index.js create mode 100644 app/pages/Footer/index.js create mode 100644 app/pages/Footer/index.scss create mode 100644 app/pages/GetCoins/get-coins.scss create mode 100644 app/pages/GetCoins/index.js create mode 100644 app/pages/MyDomain/CreateRecord.js create mode 100644 app/pages/MyDomain/DomainDetails.js create mode 100644 app/pages/MyDomain/EditableRecord.js create mode 100644 app/pages/MyDomain/Records.js create mode 100644 app/pages/MyDomain/index.js create mode 100644 app/pages/MyDomain/my-domain.scss create mode 100644 app/pages/NetworkPicker/index.js create mode 100644 app/pages/NetworkPicker/network-picker.scss create mode 100644 app/pages/Onboarding/BackUpSeedWarning/index.js create mode 100644 app/pages/Onboarding/BackUpSeedWarning/index.scss create mode 100644 app/pages/Onboarding/ConfirmSeed/confirm-seed.scss create mode 100644 app/pages/Onboarding/ConfirmSeed/index.js create mode 100644 app/pages/Onboarding/ConnectLedger/connect.scss create mode 100644 app/pages/Onboarding/ConnectLedger/index.js create mode 100644 app/pages/Onboarding/ConnectLedgerFlow/index.js create mode 100644 app/pages/Onboarding/CreateNewAccount/index.js create mode 100644 app/pages/Onboarding/CreatePassword/create.scss create mode 100644 app/pages/Onboarding/CreatePassword/index.js create mode 100644 app/pages/Onboarding/ExistingAccountOptions/existing.scss create mode 100644 app/pages/Onboarding/ExistingAccountOptions/index.js create mode 100644 app/pages/Onboarding/FundAccessOptions/access.scss create mode 100644 app/pages/Onboarding/FundAccessOptions/index.js create mode 100644 app/pages/Onboarding/ImportSeedEnterMnemonic/importenter.scss create mode 100644 app/pages/Onboarding/ImportSeedEnterMnemonic/index.js create mode 100644 app/pages/Onboarding/ImportSeedFlow/index.js create mode 100644 app/pages/Onboarding/ImportSeedWarning/importwarning.scss create mode 100644 app/pages/Onboarding/ImportSeedWarning/index.js create mode 100644 app/pages/Onboarding/OptInAnalytics/index.js create mode 100644 app/pages/Onboarding/OptInAnalytics/opt-in-analytics.scss create mode 100644 app/pages/Onboarding/Terms/index.js create mode 100644 app/pages/Onboarding/Terms/terms.scss create mode 100644 app/pages/Root.js create mode 100644 app/pages/SearchTLD/index.js create mode 100644 app/pages/SearchTLD/searchTLD.scss create mode 100644 app/pages/Settings/AccountIndexModal.js create mode 100644 app/pages/Settings/InterstitialWarningModal.js create mode 100644 app/pages/Settings/RevealSeedModal.js create mode 100644 app/pages/Settings/account-index-modal.scss create mode 100644 app/pages/Settings/index.js create mode 100644 app/pages/Settings/index.scss create mode 100644 app/pages/Settings/interstitial-warning-modal.scss create mode 100644 app/pages/Settings/reveal-seed-modal.scss create mode 100644 app/pages/Terms/index.js create mode 100644 app/pages/Terms/terms.scss create mode 100644 app/pages/Watching/index.js create mode 100644 app/pages/Watching/watching.scss create mode 100644 app/pages/YourBids/BidAction.js create mode 100644 app/pages/YourBids/BidStatus.js create mode 100644 app/pages/YourBids/BidTimeLeft.js create mode 100644 app/pages/YourBids/index.js create mode 100644 app/pages/YourBids/your-bids.scss create mode 100644 app/preload.js create mode 100644 app/sentry.js create mode 100644 app/store/configureStore.dev.js create mode 100644 app/store/configureStore.js create mode 100644 app/store/configureStore.prod.js create mode 100644 app/utils/balances.js create mode 100644 app/utils/client.js create mode 100644 app/utils/ellipsify.js create mode 100644 app/utils/fs.js create mode 100644 app/utils/helpers.js create mode 100644 app/utils/isDev.js create mode 100644 app/utils/logClient.js create mode 100644 app/utils/nameChecker.js create mode 100644 app/utils/nameHelpers.js create mode 100644 app/utils/nodeClient.js create mode 100644 app/utils/pify.js create mode 100644 app/utils/recordHelpers.js create mode 100644 app/utils/timeConverter.js create mode 100644 app/utils/verifyAddress.js create mode 100644 app/utils/walletClient.js create mode 100644 app/variables.scss create mode 100644 app/vendor/fuse/bitap/bitap_matched_indices.js create mode 100644 app/vendor/fuse/bitap/bitap_pattern_alphabet.js create mode 100644 app/vendor/fuse/bitap/bitap_regex_search.js create mode 100644 app/vendor/fuse/bitap/bitap_score.js create mode 100644 app/vendor/fuse/bitap/bitap_search.js create mode 100644 app/vendor/fuse/bitap/index.js create mode 100644 app/vendor/fuse/helpers/deep_value.js create mode 100644 app/vendor/fuse/helpers/is_array.js create mode 100644 app/vendor/fuse/index.js create mode 100644 configs/webpack.config.renderer.dev.babel.js create mode 100644 configs/webpack.config.renderer.prod.babel.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 resources/icon.icns create mode 100644 resources/icon.ico create mode 100644 resources/icon.png create mode 100644 resources/icons/1024x1024.png create mode 100644 resources/icons/128x128.png create mode 100644 resources/icons/16x16.png create mode 100644 resources/icons/24x24.png create mode 100644 resources/icons/256x256.png create mode 100644 resources/icons/32x32.png create mode 100644 resources/icons/48x48.png create mode 100644 resources/icons/512x512.png create mode 100644 resources/icons/64x64.png create mode 100644 resources/icons/96x96.png create mode 100755 scripts/clean.sh create mode 100755 scripts/package.sh create mode 100755 scripts/remove-appdata-osx.sh diff --git a/.babelrc b/.babelrc new file mode 100644 index 000000000..8e2766796 --- /dev/null +++ b/.babelrc @@ -0,0 +1,29 @@ +{ + "presets": [ + [ + "@babel/env", + { + "targets": { + "electron": "6.0.7" + }, + "debug": true + } + ], + "@babel/preset-react" + ], + "plugins": [ + [ + "@babel/plugin-proposal-decorators", + { + "legacy": true + } + ], + [ + "@babel/plugin-proposal-class-properties", + { + "loose": true + } + ], + "@babel/plugin-syntax-bigint" + ] +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..4a7ea3036 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..f59d2f062 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,56 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release +.eslintcache + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +node_modules + +# OSX +.DS_Store + +# flow-typed +flow-typed/npm/* +!flow-typed/npm/module_vx.x.x.js + +# App packaged +release +app/main.prod.js +app/main.prod.js.map +app/renderer.prod.js +app/renderer.prod.js.map +app/style.css +app/style.css.map +dist +dll +main.js +main.js.map + +.idea +npm-debug.log.* +__snapshots__ + +# Package.json +package.json +.travis.yml diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..6ba464918 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,56 @@ +{ + "parser": "babel-eslint", + "parserOptions": { + "sourceType": "module", + "allowImportExportEverywhere": true + }, + "extends": ["airbnb", "prettier", "prettier/react"], + "env": { + "browser": true, + "node": true + }, + "rules": { + "arrow-parens": ["off"], + "compat/compat": "error", + "consistent-return": "off", + "comma-dangle": "off", + "generator-star-spacing": "off", + "import/no-unresolved": "error", + "import/no-extraneous-dependencies": "off", + "jsx-a11y/anchor-is-valid": "off", + "no-console": "off", + "no-use-before-define": "off", + "no-multi-assign": "off", + "promise/param-names": "error", + "promise/always-return": "error", + "promise/catch-or-return": "error", + "promise/no-native": "off", + "react/sort-comp": [ + "error", + { + "order": [ + "type-annotations", + "static-methods", + "lifecycle", + "everything-else" + ] + } + ], + "react/jsx-no-bind": "off", + "react/jsx-filename-extension": [ + "error", + { "extensions": [".js", ".jsx"] } + ], + "react/prefer-stateless-function": "off", + "react/destructuring-assignment": "off", + "spaced-comment": "off" + }, + "plugins": ["import", "promise", "compat", "react"], + "settings": { + "import/resolver": { + "webpack": { + "config": "configs/webpack.config.eslint.js" + } + } + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..b273d58d1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +* text eol=lf +*.png binary +*.jpg binary +*.jpeg binary +*.ico binary +*.icns binary +*.tgz binary diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..2c062a0bb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug Report +about: Create a bug report to help us improve Bob. +--- + + + +## Expected Behavior + + +## Current Behavior + + +## Steps to Reproduce + + +1. +1. +1. +1. + +## Context (Environment) + + + +## Logs + + + +## Screencap + diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..662db13f1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release +.eslintcache + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +node_modules + +# OSX +.DS_Store + +# flow-typed +flow-typed/npm/* +!flow-typed/npm/module_vx.x.x.js + +# App packaged +release +app/main.prod.js +app/main.prod.js.map +app/renderer.prod.js +app/renderer.prod.js.map +app/style.css +app/style.css.map +dist +dll +*.map + +.idea +*.iml +npm-debug.log.* diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..c6eccbf91 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.1.0] - 2020-01-21 +### Added + +- Initial public beta release diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 000000000..9ebdfbcf7 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,4 @@ +- Chi Kei Chan [@chikeichan](https://github.com/chikeichan) +- Michael Arnold [@makzent](https://github.com/makzent) +- Matthew Slipper [@mslipper](https://github.com/mslipper) +- Erin Lish for our iconography diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 000000000..a6809a52a --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +

+ +# Bob: A Handshake Wallet and Full Node + +Bob is a [Handshake](https://handshake.org) wallet with an integrated full node. + +Most discussion around Bob is on the [forum](https://forum.kyokan.io). Please go there to report issues and send feedback. The forum is also where we'll post announcements. + +**This is beta software, so use it at your own risk!** We expect to exit beta soon. + +## Features + +Bob supports all of the following features: + +1. Name auctions +2. DNS record management +3. Send/receive coins +4. Airdrop claims +5. Name watchlists + +## Reporting Issues + +### Non-Security Issues + +Please report issues to the [#bob-support](https://forum.kyokan.io/c/bob/support/5) topic on our forum. + +### Security Issues + +Please don't report security issues to GitHub. Instead, send an e-mail to [security@kyokan.io](mailto:security@kyokan.io) describing your issue. Our PGP key's fingerprint is `9FDB 9D49 4A60 87E8 E61A 3F9E 2DCA AB4D D4B6 04F1`. + +## Contributing + +### Building From Source + +To build Bob from source, clone the repo and run `npm install`. Then, run `npm run dev` to start a local development server. Note that changes to code running in Electron's main process will require a restart of the development server. + +To package the application for Mac, run `npm run package`. This will create a DMG and app bundle. Similarly, to bundle for Windows or Linux run `npm run package-win` or `npm run package-linux`, respectively. diff --git a/app/app.html b/app/app.html new file mode 100644 index 000000000..262b086ec --- /dev/null +++ b/app/app.html @@ -0,0 +1,15 @@ + + + + + + Bob + + + + + +
+ + + diff --git a/app/assets/images/alert-triangle.svg b/app/assets/images/alert-triangle.svg new file mode 100644 index 000000000..59e65b15b --- /dev/null +++ b/app/assets/images/alert-triangle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/alice.png b/app/assets/images/alice.png new file mode 100644 index 0000000000000000000000000000000000000000..ba64daba1cce5301b251e1de57e666408c76c6d5 GIT binary patch literal 92082 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 Z{|6V@N4mt1mKy*7002ovPDHLkV1kHlELH#j literal 0 HcmV?d00001 diff --git a/app/assets/images/arrow-back-blue.svg b/app/assets/images/arrow-back-blue.svg new file mode 100644 index 000000000..7488eec76 --- /dev/null +++ b/app/assets/images/arrow-back-blue.svg @@ -0,0 +1 @@ + diff --git a/app/assets/images/arrow-down-circle-green.svg b/app/assets/images/arrow-down-circle-green.svg new file mode 100644 index 000000000..9340ee912 --- /dev/null +++ b/app/assets/images/arrow-down-circle-green.svg @@ -0,0 +1,13 @@ + + + + arrow-down-circle + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/app/assets/images/arrow-left-blue.svg b/app/assets/images/arrow-left-blue.svg new file mode 100644 index 000000000..4ef0c2b99 --- /dev/null +++ b/app/assets/images/arrow-left-blue.svg @@ -0,0 +1 @@ + diff --git a/app/assets/images/arrow-right-blue.svg b/app/assets/images/arrow-right-blue.svg new file mode 100644 index 000000000..dca0d71ea --- /dev/null +++ b/app/assets/images/arrow-right-blue.svg @@ -0,0 +1 @@ + diff --git a/app/assets/images/arrow-up-circle-red.svg b/app/assets/images/arrow-up-circle-red.svg new file mode 100644 index 000000000..4a0ff1208 --- /dev/null +++ b/app/assets/images/arrow-up-circle-red.svg @@ -0,0 +1,13 @@ + + + + arrow-down-circle + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/app/assets/images/baseline-timer-24px.svg b/app/assets/images/baseline-timer-24px.svg new file mode 100644 index 000000000..2c4375864 --- /dev/null +++ b/app/assets/images/baseline-timer-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/bob-logo-circle.svg b/app/assets/images/bob-logo-circle.svg new file mode 100644 index 000000000..9c1f2ce45 --- /dev/null +++ b/app/assets/images/bob-logo-circle.svg @@ -0,0 +1,13 @@ + + + + Group 26 + Created with Sketch. + + + + + + + + diff --git a/app/assets/images/bob-logo.svg b/app/assets/images/bob-logo.svg new file mode 100644 index 000000000..8dd053133 --- /dev/null +++ b/app/assets/images/bob-logo.svg @@ -0,0 +1,14 @@ + + + + Page 1 + Created with Sketch. + + + + + + + + + diff --git a/app/assets/images/brick-loader.svg b/app/assets/images/brick-loader.svg new file mode 100644 index 000000000..86ce7c4e9 --- /dev/null +++ b/app/assets/images/brick-loader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/calendar-icon.svg b/app/assets/images/calendar-icon.svg new file mode 100644 index 000000000..1aadc6828 --- /dev/null +++ b/app/assets/images/calendar-icon.svg @@ -0,0 +1,14 @@ + + + + calendar-outline + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/app/assets/images/check-circle-blue.svg b/app/assets/images/check-circle-blue.svg new file mode 100644 index 000000000..6d8d71ba8 --- /dev/null +++ b/app/assets/images/check-circle-blue.svg @@ -0,0 +1,12 @@ + + + + check-circle + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/app/assets/images/check-circle-white.svg b/app/assets/images/check-circle-white.svg new file mode 100644 index 000000000..f707df7b0 --- /dev/null +++ b/app/assets/images/check-circle-white.svg @@ -0,0 +1,12 @@ + + + + check-circle + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/app/assets/images/check-circle.svg b/app/assets/images/check-circle.svg new file mode 100644 index 000000000..187649eec --- /dev/null +++ b/app/assets/images/check-circle.svg @@ -0,0 +1 @@ + diff --git a/app/assets/images/check-green.svg b/app/assets/images/check-green.svg new file mode 100644 index 000000000..711eac1b6 --- /dev/null +++ b/app/assets/images/check-green.svg @@ -0,0 +1 @@ + diff --git a/app/assets/images/check-icon-white.svg b/app/assets/images/check-icon-white.svg new file mode 100644 index 000000000..f91841722 --- /dev/null +++ b/app/assets/images/check-icon-white.svg @@ -0,0 +1,14 @@ + + + + check-circle-green + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/app/assets/images/check-square-blue.svg b/app/assets/images/check-square-blue.svg new file mode 100644 index 000000000..f2960f61b --- /dev/null +++ b/app/assets/images/check-square-blue.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/check-square.svg b/app/assets/images/check-square.svg new file mode 100644 index 000000000..e84fc92c1 --- /dev/null +++ b/app/assets/images/check-square.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/delete-gray.svg b/app/assets/images/delete-gray.svg new file mode 100644 index 000000000..492d646fd --- /dev/null +++ b/app/assets/images/delete-gray.svg @@ -0,0 +1 @@ + diff --git a/app/assets/images/download-blue.png b/app/assets/images/download-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..6dba461506856cdfe5f4571de20500d91305659a GIT binary patch literal 556 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGoEX7WqAsj$Z!;#Vf4nJ zu)YUj#)hQ5bAW=9C9V-A!TD(=<%vb94CUqJdYO6I#mR{Use1WE>9gP2NHH)luJm+q z45^s&_O4;jVFMAihpjP3%Uk%CHd{Z_lDpK>#>Bksi@oaauaZaek83VE!RR?j#k2pJ z=3lmLI=O%EeE#KaJ$2u)=U=2NO4vAz9cC~d;t@z|Fko(xR5-(sNRd#)_s_xJ(f1pt z-+Nv1sW5Z-^?BSS`w?Q9?R>--%)Zaq{r;g`?yh*(KO2tQ zr0{>wxO|r}eqXJO@7ImR=F_%qi{=1@<|LJsd(P%NfB)BieevyI@|$0NV}93*5{@86 bCO1TF80&?exVeP`W0t|w)z4*}Q$iB}=!e?_ literal 0 HcmV?d00001 diff --git a/app/assets/images/edit-gray.svg b/app/assets/images/edit-gray.svg new file mode 100644 index 000000000..ddb04980e --- /dev/null +++ b/app/assets/images/edit-gray.svg @@ -0,0 +1 @@ + diff --git a/app/assets/images/goosig-1.svg b/app/assets/images/goosig-1.svg new file mode 100644 index 000000000..9e3cca600 --- /dev/null +++ b/app/assets/images/goosig-1.svg @@ -0,0 +1,23 @@ + + + + goosig-1 + Created with Sketch. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/images/goosig-2.svg b/app/assets/images/goosig-2.svg new file mode 100644 index 000000000..060bd0622 --- /dev/null +++ b/app/assets/images/goosig-2.svg @@ -0,0 +1,43 @@ + + + + goosig-2 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/images/goosig-3.svg b/app/assets/images/goosig-3.svg new file mode 100644 index 000000000..6cb72eae7 --- /dev/null +++ b/app/assets/images/goosig-3.svg @@ -0,0 +1,25 @@ + + + + goosig-3 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/images/heart-red-outline.svg b/app/assets/images/heart-red-outline.svg new file mode 100644 index 000000000..e05f17ad3 --- /dev/null +++ b/app/assets/images/heart-red-outline.svg @@ -0,0 +1,11 @@ + + + + Path + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/app/assets/images/heart-red.svg b/app/assets/images/heart-red.svg new file mode 100644 index 000000000..276f966cf --- /dev/null +++ b/app/assets/images/heart-red.svg @@ -0,0 +1,11 @@ + + + + Path + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/app/assets/images/heart.svg b/app/assets/images/heart.svg new file mode 100644 index 000000000..9e78ce3ab --- /dev/null +++ b/app/assets/images/heart.svg @@ -0,0 +1,11 @@ + + + + Path + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/app/assets/images/icon-128.png b/app/assets/images/icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..5b2c298cf3b7d5f450327776afffc9b236b7daf6 GIT binary patch literal 5198 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/icon-48.png b/app/assets/images/icon-48.png new file mode 100644 index 0000000000000000000000000000000000000000..263ae09e58e8036c2e44923d09ef8d6ab8f862ba GIT binary patch literal 1547 zcmV+m2K4!fP)69c|NQ9>Z}p}{H`!xsn*wA)hNgqG5Rv|A}< zcdj40TiSip*=4tt`1>^Xo-^nEPv*|tbML@?jzlZaoV*EBbxjp3B?X{zfiZ%l04UTA zftw0xQlc6}X@{$vt>>dL?mZ>oD6~)4JjPNblmS!2y#1_FQlVL7XXUBZ`bafIlz=18 z{-lVp7I-?+f&E1M492EPXZvpv+-sf%xv7)fx@8MWm4!QyIWg*hLSGTi^fH~m+}_w>*5V$@Ngv8sHhJ52`!YN4D_y~-ftDMAU66;vB;Hldv{3*mQW5M z6X?pbeGWVm6Io%PkOhgY>7RzO2c1HWJX^VltdE_nu$hr#vz}{i?>^yg2zZI-=G&)$ zagx}7ZNkOn)-BV2Id$dYz|J63$ny#RJ@EiZ7LR+2f9Iel;9X%4g%fqd7vD2^$~4Y4 zo?^p_@K%vG*aep7cl$?Aj8?Ht- zsG-Kg-LFtmbfXUg8p%NDIy;(lTy0_VyDLrkK3o1eGmD=x9ov_HD*|1n>FK$}@jrhz zZn=Cf|=S5zErWY@39L&woVZt7x;(FDG)kf;Vx zfi9y)TS*!-j)cU~hzLprI$4v)0HBl+OSJ)rAx`i<0UbT)1ijVW_iDt&$CHwl9$=48 zNMLMADnQV3I+lp09|3Oy2OC;00B8N3&NVgkUl-3^6cD7yu@FmK8~{yq$6}*22m+_Q zng>*)N-F#v5AENB+kFRsr7Kr6YyM*56B5vLoyTU+W7*nG0QBCzP0ijPhDSYMh}!MD z?q3Hqi|kY$wR)gq>!k~P`O$iot=-7zq-54?-hs#C#_$-##m57n41?_(*3jO1W!Mr> z9^)I|b^ozS^EKDCoHXm>z_fwRjdjO3>pV$Tc0S`$ZD^W?uJ_&6Twlk>YgcikdVkP? z#-^mwa_Kycb;mhz^oS{O*5t9=yxu|csXC6<)&zMzZZ ze`xAEJKd8X{JX;9vf6K(iq{6uG|82S=D?>xRd@_zS4~rgGg1vvI}Dm%kddVfPZ6RDm1sFA xl>#!Z&#L}H \ No newline at end of file diff --git a/app/assets/images/notification-24.png b/app/assets/images/notification-24.png new file mode 100644 index 0000000000000000000000000000000000000000..61e4b1bf38b4e63a6bb030bf711ea3827cd03be6 GIT binary patch literal 288 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1quc!L6Pyjv*f2PcIm99dZz9dAL@jsBKD>Sh1vM z>hoBe9+x9-*`2-f9HZwp-c{q#|6RhZXkT-7mvvWSknX~MF&mA$Z1*lsV_!P!pwzkv z`yPm$|8dj*YC)rkO$pn*pQ|h*9&^6`ux_{5l?8|PpT5g^WkwN8xqd+MDd9US6}zoX zlM0u+>AbtradfWK#vQ@R)81&^Xq4Lcf%kr28H4GB#myJLEs%Tu;P(Hwj4!9(s|?u1 hX5phP=d)12?uEs6cOH`+?m!PPc)I$ztaD0e0sv*6acuwq literal 0 HcmV?d00001 diff --git a/app/assets/images/open-new-window.svg b/app/assets/images/open-new-window.svg new file mode 100644 index 000000000..5135f62b4 --- /dev/null +++ b/app/assets/images/open-new-window.svg @@ -0,0 +1 @@ + diff --git a/app/assets/images/pending-black.svg b/app/assets/images/pending-black.svg new file mode 100644 index 000000000..aa960abc5 --- /dev/null +++ b/app/assets/images/pending-black.svg @@ -0,0 +1,13 @@ + + + + refresh-cw + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/app/assets/images/search.svg b/app/assets/images/search.svg new file mode 100644 index 000000000..ce8bf9dde --- /dev/null +++ b/app/assets/images/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/settings-gray.svg b/app/assets/images/settings-gray.svg new file mode 100644 index 000000000..25a0497bb --- /dev/null +++ b/app/assets/images/settings-gray.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/settings.svg b/app/assets/images/settings.svg new file mode 100644 index 000000000..bf07b95ee --- /dev/null +++ b/app/assets/images/settings.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/the-cat.png b/app/assets/images/the-cat.png new file mode 100644 index 0000000000000000000000000000000000000000..e604d6a40a0c4f1b20f4c091bc314cf5d8cad544 GIT binary patch literal 29004 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/trash-red.svg b/app/assets/images/trash-red.svg new file mode 100644 index 000000000..a9ae6d55e --- /dev/null +++ b/app/assets/images/trash-red.svg @@ -0,0 +1 @@ + diff --git a/app/assets/images/unchecked-square-gray.svg b/app/assets/images/unchecked-square-gray.svg new file mode 100644 index 000000000..f7d927b63 --- /dev/null +++ b/app/assets/images/unchecked-square-gray.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/x-gray.svg b/app/assets/images/x-gray.svg new file mode 100644 index 000000000..8ad77cedb --- /dev/null +++ b/app/assets/images/x-gray.svg @@ -0,0 +1,12 @@ + + + + x (1) + Created with Sketch. + + + + + + + diff --git a/app/assets/images/x-red.svg b/app/assets/images/x-red.svg new file mode 100644 index 000000000..42d6be03d --- /dev/null +++ b/app/assets/images/x-red.svg @@ -0,0 +1,12 @@ + + + + x (1) + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/app/assets/images/x-square.svg b/app/assets/images/x-square.svg new file mode 100644 index 000000000..d93c8b691 --- /dev/null +++ b/app/assets/images/x-square.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/app/assets/images/x-white.svg b/app/assets/images/x-white.svg new file mode 100644 index 000000000..85e366f21 --- /dev/null +++ b/app/assets/images/x-white.svg @@ -0,0 +1,12 @@ + + + + x (1) + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/app/background/analytics/client.js b/app/background/analytics/client.js new file mode 100644 index 000000000..78f697cd6 --- /dev/null +++ b/app/background/analytics/client.js @@ -0,0 +1,8 @@ +import { makeClient } from '../ipc/ipc'; + +export const clientStub = (ipcRendererInjector) => makeClient(ipcRendererInjector, 'Analytics', [ + 'setOptIn', + 'getOptIn', + 'track', + 'screenView', +]); diff --git a/app/background/analytics/service.js b/app/background/analytics/service.js new file mode 100644 index 000000000..f7899e774 --- /dev/null +++ b/app/background/analytics/service.js @@ -0,0 +1,98 @@ +import mixpanel from 'mixpanel'; +import uuid from 'uuid'; +import { del, get, put } from '../db/service'; +import * as os from 'os'; + +const pkg = require('../../../package.json'); +const electron = require('electron'); + +const MIXPANEL_TOKEN = '03bfcccfa5463ac6017761207e8cb0e3'; +const USER_ID_KEY = 'analytics:userId'; +const OPT_IN_STATE_KEY = 'analytics:optIn'; + +let mp; +let userId; + +export async function setOptIn(state) { + if (!state) { + await put(OPT_IN_STATE_KEY, '0'); + await del(USER_ID_KEY); + return; + } + + const u = uuid.v4(); + await put(USER_ID_KEY, u); + await put(OPT_IN_STATE_KEY, '1'); + userId = u; + mp = mixpanel.init(MIXPANEL_TOKEN); + mp.people.set(u, { + $name: u, + platform: os.platform(), + arch: os.arch(), + appVersion: pkg.version, + isDev: electron.app.isPackaged || false, + }); +} + +export async function getOptIn() { + const state = await get(OPT_IN_STATE_KEY); + if (state === '1') { + return 'OPTED_IN'; + } + if (state === '0') { + return 'OPTED_OUT'; + } + return 'NOT_CHOSEN'; +} + +export async function startTracking() { + const u = await get(USER_ID_KEY); + if (!u) { + return; + } + + userId = u; + mp = mixpanel.init(MIXPANEL_TOKEN); + mp.people.set(u, { + platform: os.platform(), + arch: os.arch(), + appVersion: pkg.version, + }); +} + +export async function track(name, data) { + if (!mp) { + return; + } + + mp.track( + name, + { + ...data, + distinct_id: userId, + }, + ); +} + +export async function screenView(name, opts) { + await track( + 'viewed page', + { + name, + ...opts, + }, + ); +} + +const sName = 'Analytics'; +const methods = { + setOptIn, + getOptIn, + track, + screenView, +}; + +export async function start(server) { + await startTracking(); + server.withService(sName, methods); +} diff --git a/app/background/db/client.js b/app/background/db/client.js new file mode 100644 index 000000000..cf1e97c0a --- /dev/null +++ b/app/background/db/client.js @@ -0,0 +1,9 @@ +import { makeClient } from '../ipc/ipc'; + +export const clientStub = (ipcRendererInjector) => makeClient(ipcRendererInjector, 'DB', [ + 'open', + 'close', + 'put', + 'get', + 'del', +]); diff --git a/app/background/db/service.js b/app/background/db/service.js new file mode 100644 index 000000000..3480fd0ea --- /dev/null +++ b/app/background/db/service.js @@ -0,0 +1,65 @@ +import { app } from 'electron'; +import bdb from 'bdb'; +import path from 'path'; +import { awaitFSNotBusy } from '../../utils/fs'; + +let db; + +export async function open() { + if (db) { + return; + } + + const loc = path.join(app.getPath('userData'), 'db'); + await awaitFSNotBusy(path.join(loc, 'LOCK')); + let tdb = bdb.create(loc); + await tdb.open(); + db = tdb; +} + +export async function close() { + ensureDB(); + await db.close(); + db = null; +} + +export async function put(key, value) { + ensureDB(); + return db.put(Buffer.from(key, 'utf-8'), Buffer.from(JSON.stringify(value), 'utf-8')); +} + +export async function get(key) { + ensureDB(); + const data = await db.get(Buffer.from(key, 'utf-8')); + if (data === null) { + return null; + } + + return JSON.parse(data.toString('utf-8')); +} + +export async function del(key) { + ensureDB(); + return db.del(Buffer.from(key, 'utf-8')); +} + +function ensureDB() { + if (!db) { + throw new Error('db not open'); + } +} + +const sName = 'DB'; +const methods = { + open, + close, + put, + get, + del +}; + +export async function start(server) { + await open(); + server.withService(sName, methods); +} + diff --git a/app/background/ipc/ipc.js b/app/background/ipc/ipc.js new file mode 100644 index 000000000..7a378e178 --- /dev/null +++ b/app/background/ipc/ipc.js @@ -0,0 +1,156 @@ +export const SIGIL = '@@RPC@@'; + +let lastId = 0; + +export const nullServer = { + withMethod() {}, + withService() {}, + start() {}, + stop() {} +}; + +function log() { + if (process.env.NODE_ENV === 'production') { + return; + } + + console.log(...arguments) +} + +// used to scrub sensitive info from RPC calls +function sanitizeData(data, method) { + if (method.sanitizedArgs) { + const copy = JSON.parse(JSON.stringify(data)); + for (let i = 0; i < method.sanitizedArgs.length; i++) { + const pIdx = method.sanitizedArgs[i]; + if (copy.params[pIdx]) { + copy.params[pIdx] = ''; + } + } + + return copy; + } + + return data; +} + +export function makeServer(ipcMain) { + const methods = {}; + + const handler = (event, data) => { + const method = methods[data.method]; + + if (!data.id) { + log('IPC method call has no ID, aborting.'); + return event.sender.send(SIGIL, makeError(-32600, 'id not provided', null)); + } + + if (!method) { + log('IPC method does not exist, aborting.'); + return event.sender.send(SIGIL, makeError(-32601, 'method not found', data.id)); + } + + let params; + if (!Array.isArray(data.params)) { + params = [data.params]; + } else { + params = data.params; + } + + const cb = (err, res) => { + if (err) { + log('Sending IPC method error.', sanitizeData(data, method), err); + log('Stack:', err.stack); + return event.sender.send(SIGIL, makeError(err.code || -1, `${err.message}${err.stack ? '\n' + err.stack : ''}`, data.id)); + } + + log('Sending IPC method response.', sanitizeData(data, method), res); + return event.sender.send(SIGIL, makeResponse(res, data.id)); + }; + + log('Executing IPC method.', sanitizeData(data, method)); + const maybePromise = method.apply(null, [...params, cb]); + if (maybePromise.then) { + maybePromise.then((res) => cb(null, res)) + .catch((err) => cb(err)); + } + }; + + const server = { + withMethod(service, name, method) { + methods[`${service}.${name}`] = method + }, + withService(sName, methods) { + Object.keys(methods).forEach((mName) => { + server.withMethod(sName, mName, methods[mName]); + }); + }, + start() { + ipcMain.on(SIGIL, handler); + }, + stop() { + ipcMain.removeListener(SIGIL, handler); + } + }; + return server; +} + +export function makeClient(ipcRendererInjector, sName, methods) { + const mkSend = mName => (...params) => { + const ipcRenderer = ipcRendererInjector(); + const id = ++lastId; + + log('Dispatching IPC method call.', id, mName); + + return new Promise((resolve, reject) => { + const handler = (event, data) => { + const jsonData = JSON.parse(data); + if (jsonData.id !== id) { + return; + } + + ipcRenderer.off(SIGIL, handler); + + if (jsonData.error) { + log('Received IPC error.', id, mName, jsonData.result); + return reject(jsonData.error); + } + + log('Received IPC response.', id, mName, jsonData.result); + return resolve(jsonData.result); + }; + + ipcRenderer.send(SIGIL, { + jsonrpc: '2.0', + method: `${sName}.${mName}`, + params, + id + }); + ipcRenderer.on(SIGIL, handler); + }); + }; + + return methods.reduce((acc, curr) => { + acc[curr] = mkSend(curr); + return acc; + }, {}); +} + +function makeResponse(result, id) { + return JSON.stringify({ + jsonrpc: '2.0', + result, + id + }); +} + +function makeError(code, message, id) { + return JSON.stringify({ + jsonrpc: '2.0', + error: { + code, + message + }, + id + }); +} diff --git a/app/background/ipc/service.js b/app/background/ipc/service.js new file mode 100644 index 000000000..67ff196ac --- /dev/null +++ b/app/background/ipc/service.js @@ -0,0 +1,14 @@ +import { makeServer, nullServer } from './ipc'; + +export let defaultServer; + +export function start() { + if (!require('electron').ipcMain) { + defaultServer = nullServer + } else { + defaultServer = makeServer(require('electron').ipcMain); + } + + defaultServer.start(); + return defaultServer; +} diff --git a/app/background/ledger/client.js b/app/background/ledger/client.js new file mode 100644 index 000000000..fc08ec600 --- /dev/null +++ b/app/background/ledger/client.js @@ -0,0 +1,6 @@ +import { makeClient } from '../ipc/ipc'; + +export const clientStub = (ipcRendererInjector) => makeClient(ipcRendererInjector, 'Ledger', [ + 'getXPub', + 'signTransaction' +]); diff --git a/app/background/ledger/service.js b/app/background/ledger/service.js new file mode 100644 index 000000000..cd54a7095 --- /dev/null +++ b/app/background/ledger/service.js @@ -0,0 +1,70 @@ +// import hsdLedger from 'hsd-ledger'; +import * as logger from './logger/logger'; +import { defaultServer, makeClient } from '../ipc/ipc'; + +const MTX = require('hsd/lib/primitives/mtx'); +// const {LedgerHSD} = hsdLedger; +// const {Device} = hsdLedger.HID; +const ONE_MINUTE = 60000; + +export async function withLedger(network, action) { + let device; + let ledger; + + try { + const devices = await Device.getDevices(); + device = new Device({ + device: devices[0], + timeout: ONE_MINUTE + }); + await device.open(); + // TODO: this network parameter should be passed dynamically. + ledger = new LedgerHSD({device, network}); + } catch (e) { + logger.error('failed to open ledger', e); + throw e; + } + + try { + return await action(ledger); + } finally { + try { + await device.close() + } catch (e) { + logger.error('failed to close ledger', e); + } + } +} + +export async function getXPub(network) { + return withLedger(network, async (ledger) => { + return (await ledger.getAccountXPUB(0)).xpubkey(network); + }); +} + +export async function signTransaction(network, txJSON) { + const mtx = MTX.fromJSON(txJSON); + + return withLedger(network, async (ledger) => { + const retMTX = await ledger.signTransaction(mtx); + + try { + retMTX.check(); + } catch (e) { + logger.error('transaction failed to verify:' + e.message); + throw e + } + + return retMTX + }); +} + +const sName = 'Ledger'; +const methods = { + getXPub, + signTransaction +}; + +export function start(server) { + server.withService(sName, methods); +} diff --git a/app/background/logger/client.js b/app/background/logger/client.js new file mode 100644 index 000000000..a80d7045e --- /dev/null +++ b/app/background/logger/client.js @@ -0,0 +1,9 @@ +import { makeClient } from '../ipc/ipc'; + +export const clientStub = (ipcRendererInjector) => makeClient(ipcRendererInjector, 'Logger', [ + 'info', + 'warn', + 'error', + 'log', + 'download' +]); diff --git a/app/background/logger/service.js b/app/background/logger/service.js new file mode 100644 index 000000000..e503b73e7 --- /dev/null +++ b/app/background/logger/service.js @@ -0,0 +1,115 @@ +import winston from 'winston'; +import { app } from 'electron'; +import fs from 'fs'; +import path from 'path'; + +// Set up logger format +const {combine, timestamp, printf, colorize} = winston.format; + +const loggerFormat = printf(info => { + let {timestamp, level, message} = info; + + if (typeof info.message === 'object') { + message = JSON.stringify(message); + } + + return `${timestamp} [${level}] - ${message}`; +}); + +// Declare local variables +let logger; +let queue = []; + +export async function warn(message) { + if (!logger) { + queue.push(() => warn(message)); + } else { + logger.warn(message); + } +} + +export async function error(message) { + if (!logger) { + queue.push(() => error(message)); + } else { + logger.error(message); + } +} + +export async function info(message) { + if (!logger) { + queue.push(() => info(message)); + } else { + logger.info(message); + } +} + +export async function log() { + if (!logger) { + queue.push(() => log(...arguments)); + } else { + logger.log(...arguments); + } +} + +export async function download() { + const udPath = app.getPath('userData'); + const outputDir = path.join(udPath, 'hsd_output'); + + let content = ''; + + content += read(`${outputDir}/combined1.log`, 'utf8'); + content += read(`${outputDir}/combined.log`, 'utf8'); + + return content; +} + +function read(filepath) { + try { + return fs.readFileSync(filepath, 'utf8'); + } catch (e) { + return '\n'; + } +} + +export function startLogger() { + const udPath = app.getPath('userData'); + const outputDir = path.join(udPath, 'hsd_output'); + + const transport = new (winston.transports.File)({ + filename: `${outputDir}/combined.log`, + maxsize: '10000000', + maxFiles: '0', + tailable: true, + }); + + logger = winston.createLogger({ + format: combine( + timestamp(), + loggerFormat, + ), + transports: [ + transport, + ] + }); + + // if (process.env.NODE_ENV !== 'production') { + logger.add(new winston.transports.Console()); + // } + + queue.forEach(fn => fn()); + queue = []; +} + +const sName = 'Logger'; +const methods = { + info, + warn, + error, + log, + download, +}; + +export function start(server) { + server.withService(sName, methods); +} diff --git a/app/background/node/client.js b/app/background/node/client.js new file mode 100644 index 000000000..a83a36a31 --- /dev/null +++ b/app/background/node/client.js @@ -0,0 +1,7 @@ +import { makeClient } from '../ipc/ipc'; + +export const clientStub = ipcRendererInjector => makeClient(ipcRendererInjector, 'Node', [ + 'start', + 'stop', + 'reset', +]); diff --git a/app/background/node/service.js b/app/background/node/service.js new file mode 100644 index 000000000..f86bfb835 --- /dev/null +++ b/app/background/node/service.js @@ -0,0 +1,146 @@ +import pify from '../../utils/pify'; +import { app, BrowserWindow } from 'electron'; +import rimraf from 'rimraf'; +import { NETWORKS, VALID_NETWORKS } from '../../constants/networks'; +import path from 'path'; +import fs from 'fs'; +import { awaitFSNotBusy } from '../../utils/fs'; +import crypto from 'crypto'; +import { NodeClient } from 'hs-client'; + +const Network = require('hsd/lib/protocol/network'); + +export const SEEDS = { + [NETWORKS.REGTEST]: [ + 'aorsxa4ylaacshipyjkfbvzfkh3jhh4yowtoqdt64nzemqtiw2whk@107.170.249.165', + ], +}; + +let udPath; +let hsdBinDir; +let hsdPrefixDir; +let outputDir; + +export async function setPaths() { + udPath = app.getPath('userData'); + hsdBinDir = path.join(udPath, 'hsd'); + hsdPrefixDir = path.join(udPath, 'hsd_data'); + outputDir = path.join(udPath, 'hsd_output'); + + if (!fs.existsSync(hsdPrefixDir)) { + await pify(cb => fs.mkdir(hsdPrefixDir, {recursive: true}, cb)); + } + if (!fs.existsSync(outputDir)) { + await pify(cb => fs.mkdir(outputDir, {recursive: true}, cb)); + } +} + +let hsd; +let network; +let apiKey = crypto.randomBytes(20).toString('hex'); + +export async function reset() { + await stop(); + const walletDir = path.join(hsdPrefixDir, network, 'wallet'); + await new Promise((resolve, reject) => { + rimraf(walletDir, error => { + if (error) { + return reject(error); + } + resolve(); + }); + }); + return startNode(network); +} + +export async function startNode(net) { + if (hsd && network === net) { + return apiKey; + } + const newNetwork = VALID_NETWORKS[net]; + if (!newNetwork) { + throw new Error('invalid network'); + } + if (hsd) { + await stop(); + } + + hsd = new BrowserWindow({ + width: 400, + height: 400, + show: false, + webPreferences: { + nodeIntegration: true, + }, + }); + network = newNetwork; + await hsd.loadURL(`file://${path.join(__dirname, '../../hsd.html')}`); + hsd.webContents.send('start', hsdPrefixDir, network, SEEDS[network], apiKey); + await new Promise((resolve, reject) => { + const lis = (_, channel, ...args) => { + if (channel !== 'started' && channel !== 'error') { + return; + } + + hsd.webContents.removeListener('started', lis); + hsd.webContents.removeListener('error', lis); + + if (channel === 'error') { + console.error(args[0]); + return reject(args[0]); + } + + resolve(); + }; + hsd.webContents.on('ipc-message', lis); + }); + + const netConfig = Network.get(net); + const networkOptions = { + network: netConfig.type, + port: netConfig.rpcPort, + apiKey, + }; + const client = new NodeClient(networkOptions); + + for (let i = 0; i < 10; i++) { + try { + await client.getInfo(); + return apiKey; + } catch (e) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + } + + throw new Error('timed out'); +} + +export async function stop() { + if (!hsd) { + return; + } + + const netConfig = Network.get(network); + const networkOptions = { + network: netConfig.type, + port: netConfig.rpcPort, + apiKey, + }; + const client = new NodeClient(networkOptions); + await client.execute('stop'); + await awaitFSNotBusy(path.join(hsdPrefixDir, network, 'chain', 'LOCK')); + hsd.close(); + hsd = null; +} + +const sName = 'Node'; +const methods = { + start: startNode, + stop, + reset, +}; + +export async function start(server) { + await setPaths(); + server.withService(sName, methods); +} diff --git a/app/components/Alert/alert.scss b/app/components/Alert/alert.scss new file mode 100644 index 000000000..bc89af726 --- /dev/null +++ b/app/components/Alert/alert.scss @@ -0,0 +1,13 @@ +@import "../../variables"; + +.alert { + padding: 0.5rem; + border-radius: 0.5rem; + margin-bottom: 1rem; + font-size: 0.9rem; + + &--error { + border: 1px solid $orange-red; + color: $orange-red; + } +} diff --git a/app/components/Alert/index.js b/app/components/Alert/index.js new file mode 100644 index 000000000..9c34a9245 --- /dev/null +++ b/app/components/Alert/index.js @@ -0,0 +1,27 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import './alert.scss'; + +export default class Alert extends Component { + static propTypes = { + type: PropTypes.oneOf([ + 'error', + 'success' + ]).isRequired, + message: PropTypes.string.isRequired + }; + + render() { + if (!this.props.message) { + return null; + } + + const name = `alert alert--${this.props.type}`; + + return ( +
+ {this.props.message} +
+ ); + } +} diff --git a/app/components/AuctionGraph/auction-graph.scss b/app/components/AuctionGraph/auction-graph.scss new file mode 100644 index 000000000..e1eb67486 --- /dev/null +++ b/app/components/AuctionGraph/auction-graph.scss @@ -0,0 +1,54 @@ +@import '../../variables'; + +.auction-graph { + @extend %row-nowrap; + position: relative; + justify-content: center; + flex: 1 1 auto; + background-color: rgba($athens-gray, .6); + padding: 3.5rem 3rem 2rem 3rem; + border-radius: 1.5rem; + + &__current-block { + @extend %h7; + position: absolute; + top: 1.5rem; + left: 3rem; + padding: 0.25rem 0.75rem; + background-color: rgba($azure-blue, 0.15); + color: $azure-blue; + font-weight: 700; + border-radius: 0.5rem; + transition: background-color 200ms ease-in-out; + } + + &__column { + @extend %col-nowrap; + flex: 1 1 auto; + margin: 0 0.15rem; + + &__headline { + @extend %h3; + font-weight: 600; + } + + &__text { + @extend %h5; + font-weight: 500; + opacity: 0.5; + + &__row { + @extend %row-nowrap; + justify-content: space-between; + } + + &__col { + @extend %col-nowrap; + + &--right { + text-align: right; + } + } + } + } +} \ No newline at end of file diff --git a/app/components/AuctionGraph/index.js b/app/components/AuctionGraph/index.js new file mode 100644 index 000000000..6421dba4a --- /dev/null +++ b/app/components/AuctionGraph/index.js @@ -0,0 +1,152 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { withRouter } from 'react-router-dom'; +import c from 'classnames'; +import ProgressBar from '../ProgressBar'; +import Blocktime from '../Blocktime'; +import { isBidding, isOpening, isReveal, } from '../../utils/nameHelpers'; +import './auction-graph.scss'; + +const OPENING = 0; +const ISBIDDING = 1; +const REVEALING = 2; + +@withRouter +@connect( + (state, ownProps) => { + const {name} = ownProps.match.params; + return { + domain: state.names[name], + chain: state.node.chain, + }; + }) +export default class AuctionGraph extends Component { + static propTypes = { + domain: PropTypes.object.isRequired, + } + + static defaultProps = { + domain: {}, + } + + state = { + currentStep: 0, + openProgress: 0, + biddingProgress: 0, + revealProgress: 0, + } + + async componentDidMount() { + await this.getStatusInfo(this.props.domain); + } + + getStatusInfo(domain) { + const stats = domain.info && domain.info.stats || {}; + + if (isOpening(domain)) { + this.setState({ + currentStep: OPENING, + openProgress: (1 - (stats.openPeriodEnd - this.props.chain.height) / (stats.openPeriodEnd - stats.openPeriodStart)) * 100, + }); + } else if (isBidding(domain)) { + this.setState({ + currentStep: ISBIDDING, + openProgress: 100, + biddingProgress: (1 - (stats.bidPeriodEnd - this.props.chain.height) / (stats.bidPeriodEnd - stats.bidPeriodStart)) * 100, + }); + } else if (isReveal(domain)) { + this.setState({ + currentStep: REVEALING, + openProgress: 100, + biddingProgress: 100, + revealProgress: (1 - (stats.revealPeriodEnd - this.props.chain.height) / (stats.revealPeriodEnd - stats.revealPeriodStart)) * 100, + }); + } + return; + } + + renderDetailBlock(date, block, alignRight) { + return ( +
+
{date}
+
{block}
+
+ ); + } + + renderDateBlock(height, adjust, alignRight) { + if (!height) { + return null; + } + + return this.renderDetailBlock( + moment(d).add(adjust, 'h')} + />, + `#${height}`, + alignRight + ); + } + + maybeRenderDateBlock(condition, height, adjust, alignRight) { + if (!condition()) { + return ( + null + ) + } + + return this.renderDateBlock(height, adjust, alignRight); + } + + renderPlaceholder(isDone, alignRight) { + return ( +
+
+ {isDone ? '' : 'TBA'} +
+
+ ) + } + + render() { + const domain = this.props.domain; + const stats = domain.info && domain.info.stats || {}; + const { openProgress, biddingProgress, revealProgress, currentStep } = this.state; + const chain = this.props.chain || {}; + const currentBlock = chain.height; + + if (!this.props.domain) { + return null; + } + + return ( +
+
Current Block: #{currentBlock}
+
+
Open
+ + { this.maybeRenderDateBlock(() => isOpening(domain), stats.openPeriodStart, stats.hoursUntilBidding) || this.renderPlaceholder(OPENING < currentStep)} +
+
+
Bidding Period
+ + { this.maybeRenderDateBlock(() => isOpening(domain), stats.openPeriodEnd, stats.hoursUntilBidding) || this.maybeRenderDateBlock(() => isBidding(domain), stats.bidPeriodStart, stats.hoursUntilReveal)|| this.renderPlaceholder(ISBIDDING < currentStep)} +
+
+
Reveal Period
+ +
+ { this.maybeRenderDateBlock(() => isBidding(domain), stats.bidPeriodEnd, stats.hoursUntilReveal) || this.maybeRenderDateBlock(() => isReveal(domain), stats.revealPeriodStart, stats.hoursUntilClose) || this.renderPlaceholder(REVEALING < currentStep)} + { this.maybeRenderDateBlock(() => isReveal(domain), stats.revealPeriodEnd, stats.hoursUntilClose, true) || this.renderPlaceholder(REVEALING < currentStep, true)} +
+
+
+ ) + } +} diff --git a/app/components/AuctionPanel/index.js b/app/components/AuctionPanel/index.js new file mode 100644 index 000000000..67ba0a993 --- /dev/null +++ b/app/components/AuctionPanel/index.js @@ -0,0 +1,86 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import './index.scss'; + +export class AuctionPanel extends Component { + static propTypes = { + children: PropTypes.node, + className: PropTypes.string, + }; + + static defaultProps = { + className: '', + }; + + render() { + return ( +
+ {this.props.children} +
+ ) + } +} + +export class AuctionPanelHeader extends Component { + static propTypes = { + children: PropTypes.node, + title: PropTypes.string.isRequired, + }; + + render() { + return ( +
+
+ { this.props.title } +
+
+ { this.props.children } +
+
+ ); + } +} + +export class AuctionPanelHeaderRow extends Component { + static propTypes = { + label: PropTypes.string.isRequired, + children: PropTypes.node.isRequired, + className: PropTypes.string, + }; + + static defaultProps = { + className: '', + }; + + render() { + return ( +
+
+ {this.props.label} +
+
+ {this.props.children} +
+
+ ) + } +} + +export class AuctionPanelFooter extends Component { + static propTypes = { + children: PropTypes.node, + className: PropTypes.string, + }; + + static defaultProps = { + className: '', + }; + + render() { + return ( +
+ {this.props.children} +
+ ); + } +} diff --git a/app/components/AuctionPanel/index.scss b/app/components/AuctionPanel/index.scss new file mode 100644 index 000000000..b634388f7 --- /dev/null +++ b/app/components/AuctionPanel/index.scss @@ -0,0 +1,47 @@ +@import "../../variables"; + +.auction-panel { + @extend %col-nowrap; + + flex: 0 0 auto; + box-shadow: 0 2px 4px 2px rgba($black, 0.1); + border-radius: 1rem; + width: 22rem; + + &__title { + @extend %h2; + + font-weight: 600; + padding: 1rem 1.5rem; + box-shadow: 0 1px 0 0 rgba($black, 0.1); + } + + &__header { + @extend %col-nowrap; + + &__content { + padding: .75rem 0; + } + + &__row { + @extend %row-nowrap; + + padding: .25rem 1.5rem; + font-size: .9rem; + + &__label { + + } + + &__value { + flex: 1 0 auto; + text-align: right; + } + } + } + + &__footer { + border-top: 1px solid rgba($black, .1); + + } +} diff --git a/app/components/BidSearchInput/bid-search-input.scss b/app/components/BidSearchInput/bid-search-input.scss new file mode 100644 index 000000000..a19276539 --- /dev/null +++ b/app/components/BidSearchInput/bid-search-input.scss @@ -0,0 +1,23 @@ +@import '../../variables'; + +.bid-search-input { + @extend %box-input-secondary; + @extend %row-nowrap; + + padding: .25rem .5rem; + + &__icon { + background-image: url('../../assets/images/search.svg'); + height: 1.25rem; + width: 1.25rem; + background-size: contain; + background-repeat: no-repeat; + transition: 200ms ease-in-out; + margin-right: .25rem; + } + + &__input { + width: 100%; + font-size: .75rem !important; + } +} diff --git a/app/components/BidSearchInput/index.js b/app/components/BidSearchInput/index.js new file mode 100644 index 000000000..d8b117a8b --- /dev/null +++ b/app/components/BidSearchInput/index.js @@ -0,0 +1,16 @@ +import React from 'react'; +import './bid-search-input.scss'; + +export default function BidSearchInput(props) { + return ( +
+
+ +
+ ); +} diff --git a/app/components/Blocktime/index.js b/app/components/Blocktime/index.js new file mode 100644 index 000000000..1e54140a4 --- /dev/null +++ b/app/components/Blocktime/index.js @@ -0,0 +1,104 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import moment from 'moment'; +import { connect } from 'react-redux'; +import nodeClient from '../../utils/nodeClient'; + +// 5 minute blocks +const BLOCK_TIME = 5 * 60 * 1000; + +let firstBlock = null; +let deferred = null; +let cachedNet = null; + +// use this function to make sure that multiple components +// rendered at once don't fire off multiple requests at once. +async function getFirstBlockTime(net) { + if (firstBlock && net === cachedNet) { + return firstBlock + } + + if (deferred) { + return deferred; + } + + cachedNet = net; + deferred = new Promise((resolve, reject) => nodeClient.getBlockByHeight(1, true).then((res) => { + firstBlock = res; + deferred = null; + resolve(firstBlock); + }).catch(() => { + deferred = null; + reject(); + })); + return deferred; +} + +@connect( + (state) => ({ + network: state.node.network, + }) +) +export default class Blocktime extends Component { + static propTypes = { + network: PropTypes.string.isRequired, + height: PropTypes.number.isRequired, + className: PropTypes.string, + fromNow: PropTypes.bool, + }; + + static defaultProps = { + className: '', + fromNow: false, + }; + + state = { + time: null, + }; + + async componentWillMount() { + await this.getBlockTime(); + } + + render() { + return ( + + {this.renderTime()} + + ); + } + + renderTime() { + if (!this.state.time) { + return 'Loading...'; + } + + return this.state.time; + } + + async getBlockTime() { + const block = await getFirstBlockTime(this.props.network); + const start = moment.unix(block.time); + const delta = this.props.height * BLOCK_TIME; + const end = start.add(delta); + + if (this.props.fromNow) { + this.setState({ + time: '~' + end.toNow(true) + }); + return; + } + + this.setState({ + time: end.format('YYYY-MM-DD') + }); + } +} + +export const returnBlockTime = async (height, network) => { + const block = await getFirstBlockTime(network); + const start = moment.unix(block.time); + const delta = height * BLOCK_TIME; + const end = start.add(delta); + return end +} diff --git a/app/components/Checkbox/index.js b/app/components/Checkbox/index.js new file mode 100644 index 000000000..95b5861c2 --- /dev/null +++ b/app/components/Checkbox/index.js @@ -0,0 +1,30 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import c from 'classnames'; +import './index.scss'; + +export default class Checkbox extends Component { + static propTypes = { + checked: PropTypes.bool, + onChange: PropTypes.func, + className: PropTypes.string, + }; + + static defaultProps = { + className: '', + }; + + render() { + const { className, checked, onChange } = this.props; + + return ( +
+ +
+ ); + } +} diff --git a/app/components/Checkbox/index.scss b/app/components/Checkbox/index.scss new file mode 100644 index 000000000..a0b6aae25 --- /dev/null +++ b/app/components/Checkbox/index.scss @@ -0,0 +1,30 @@ +@import '../../variables'; + +.checkbox { + position: relative; + height: 1.75rem; + width: 1.75rem; + border-radius: 0.1875rem; + cursor: pointer; + background-repeat: no-repeat; + background-size: cover; + background-image: url('../../assets/images/unchecked-square-gray.svg'); + transition: opacity 400ms ease-in-out; + opacity: 1; + + input { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + cursor: pointer; + margin: 0; + padding: 0; + opacity: 0; + } + + &--checked { + background-image: url('../../assets/images/check-square-blue.svg'); + } +} diff --git a/app/components/Collapsible/collapsible.scss b/app/components/Collapsible/collapsible.scss new file mode 100644 index 000000000..74a91d1bc --- /dev/null +++ b/app/components/Collapsible/collapsible.scss @@ -0,0 +1,50 @@ +@import '../../variables.scss'; + +.collapsible { + @extend %col-nowrap; + + &__header { + @extend %row-nowrap; + align-items: center; + + &__title { + @extend %row-nowrap; + flex: 1 1 auto; + width: 0; + } + + &__toggle { + @extend %link; + font-size: .8rem; + font-weight: 400; + } + + &__pill { + font-size: 0.7rem; + padding: 0.25rem 0.75rem; + background-color: rgba($azure-blue, 0.15); + color: $azure-blue; + font-weight: 700; + border-radius: 0.5rem; + transition: background-color 200ms ease-in-out; + margin-left: 0.5rem; + } + + } + + &__content { + opacity: 1; + // padding: 1rem; + position: relative; + max-height: 50rem; + height: auto; + transition: 0.5s ease-out; + + &--hidden { + opacity: 0; + max-height: 0; + overflow: hidden; + transition: 0.5s ease-out; + } + } +} diff --git a/app/components/Collapsible/index.js b/app/components/Collapsible/index.js new file mode 100644 index 000000000..63f4774d0 --- /dev/null +++ b/app/components/Collapsible/index.js @@ -0,0 +1,73 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import cn from 'classnames'; +import './collapsible.scss'; + +export default class Collapsible extends Component { + static propTypes = { + title: PropTypes.string.isRequired, + className: PropTypes.string, + defaultCollapsed: PropTypes.bool, + children: PropTypes.node.isRequired, + }; + + static defaultProps = { + className: '', + defaultCollapsed: false, + }; + + constructor(props) { + super(props); + this.state = { + isCollapsed: props.defaultCollapsed, + }; + } + + toggle = () => { + const { isCollapsed } = this.state; + this.setState({ isCollapsed: !isCollapsed }); + }; + + render() { + const { title, className, pillContent } = this.props; + const { isCollapsed } = this.state; + + return ( +
+
+
+ {title} + {!!pillContent && +
+ {pillContent} +
+ } +
+
+ { isCollapsed ? 'Show': 'Hide' } +
+
+ { this.renderContent() } +
+ ); + } + + renderContent() { + return ( +
+ { this.props.children } +
+ ) + } +} + + diff --git a/app/components/ConnectLedgerStep/connect-ledger-step.scss b/app/components/ConnectLedgerStep/connect-ledger-step.scss new file mode 100644 index 000000000..f17495068 --- /dev/null +++ b/app/components/ConnectLedgerStep/connect-ledger-step.scss @@ -0,0 +1,33 @@ +@import "../../variables"; + +.connect { + &__status-pill { + height: 60px; + border-radius: 27.5px; + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + align-items: center; + padding: 15px 20px; + margin: 5px 0px; + background-color: #f7f7f7; + } + + &__status-number { + height: 19px; + color: #111111; + font-weight: 600; + line-height: 19px; + } + + &__status-text { + @extend %subheader_text; + width: 180px; + padding: 1rem 1.45rem; + height: 28px; + color: #111111; + font-size: 0.75rem; + font-weight: 400; + line-height: 14px; + } +} diff --git a/app/components/ConnectLedgerStep/defaultSteps.js b/app/components/ConnectLedgerStep/defaultSteps.js new file mode 100644 index 000000000..36e5bc8ab --- /dev/null +++ b/app/components/ConnectLedgerStep/defaultSteps.js @@ -0,0 +1,24 @@ +import React, { Component } from 'react'; +import ConnectLedgerStep from './index'; + +export default function DefaultConnectLedgerSteps(props) { + return ( + + + + + + ) +} diff --git a/app/components/ConnectLedgerStep/index.js b/app/components/ConnectLedgerStep/index.js new file mode 100644 index 000000000..0e0d3045d --- /dev/null +++ b/app/components/ConnectLedgerStep/index.js @@ -0,0 +1,29 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import './connect-ledger-step.scss'; + +export default class ConnectLedgerStep extends Component { + static propTypes = { + stepNumber: PropTypes.number.isRequired, + stepDescription: PropTypes.string.isRequired, + stepCompleted: PropTypes.bool.isRequired, + }; + + render() { + const { stepNumber, stepDescription, stepCompleted } = this.props; + return ( +
+ {stepNumber} + {stepDescription} + +
+ +
+ ); + } +} diff --git a/app/components/CopyButton/copy-btn.scss b/app/components/CopyButton/copy-btn.scss new file mode 100644 index 000000000..032f69255 --- /dev/null +++ b/app/components/CopyButton/copy-btn.scss @@ -0,0 +1,56 @@ +@import '../../variables'; + +.copy-btn { + @extend %row-nowrap; + + position: relative; + align-items: center; + border: none; + outline: none; + font-size: 0.7rem; + padding: 0.25rem 0.75rem; + background-color: rgba($azure-blue, 0.15); + color: $azure-blue; + font-weight: 700; + border-radius: 0.5rem; + cursor: pointer; + user-select: none; + transition: background-color 200ms ease-in-out; + + &:hover { + background-color: rgba($azure-blue, 0.13); + + &::after { + content: 'Copy to Clipboard'; + position: absolute; + top: -1.8rem; + left: -2rem; + width: 7rem; + background-color: rgba($black, 0.8); + color: $white; + padding: 0.3rem 0.4rem; + border-radius: 0.25rem; + } + + &::before { + content: ' '; + top: -0.375rem; + left: 1.45rem; + position: absolute; + border: 0.25rem solid transparent; + border-top-color: rgba($black, 0.8); + } + } + + &:active { + background-color: rgba($azure-blue, 0.17); + } + + &--copied { + &:hover { + &::after { + content: 'Copied!'; + } + } + } +} diff --git a/app/components/CopyButton/index.js b/app/components/CopyButton/index.js new file mode 100644 index 000000000..330c20a2f --- /dev/null +++ b/app/components/CopyButton/index.js @@ -0,0 +1,39 @@ +import React, { Component } from 'react'; +import c from 'classnames'; +import copy from 'copy-to-clipboard'; +import PropTypes from 'prop-types'; +import './copy-btn.scss'; + +export default class CopyButton extends Component { + static propTypes = { + content: PropTypes.string.isRequired, + className: PropTypes.string.isRequired, + }; + + static defaultProps = { + className: '', + }; + + state = { + hasCopied: false, + }; + + copyAddress = () => { + copy(this.props.content); + this.setState({ hasCopied: true }); + setTimeout(() => this.setState({ hasCopied: false }), 2500); + }; + + render() { + return ( + + ) + } +} diff --git a/app/components/Dropdown/index.js b/app/components/Dropdown/index.js new file mode 100644 index 000000000..32e50c462 --- /dev/null +++ b/app/components/Dropdown/index.js @@ -0,0 +1,69 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import c from 'classnames'; +import './index.scss'; + +export default class Dropdown extends Component { + static propTypes = { + items: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string.isRequired, + disabled: PropTypes.bool, + }), + ).isRequired, + className: PropTypes.string, + currentIndex: PropTypes.number, + onChange: PropTypes.func, + reversed: PropTypes.bool, + }; + + static defaultProps = { + currentIndex: 0, + onChange() {}, + className: '', + }; + + state = { + isOpen: false, + }; + + toggle = () => this.setState({ isOpen: !this.state.isOpen }); + + select(i) { + this.setState({ isOpen: false }); + this.props.onChange(i); + } + + render() { + const { items, currentIndex, className } = this.props; + const { label: currentLabel } = items[currentIndex] || {}; + + return ( +
+
+
+ {currentLabel} +
+
+
+ {items.map(({ label, disabled, value }, i) => ( +
!disabled && this.select(value || i)} + > + {label} +
+ ))} +
+
+ ); + } +} diff --git a/app/components/Dropdown/index.scss b/app/components/Dropdown/index.scss new file mode 100644 index 000000000..534e9d4cc --- /dev/null +++ b/app/components/Dropdown/index.scss @@ -0,0 +1,107 @@ +@import '../../variables'; + +.dropdown { + position: relative; + + &__current-item { + position: relative; + background-color: lighten($grey, 5); + font-size: .85rem; + font-weight: 600; + padding: .7rem 1.25rem; + border-radius: .5rem; + cursor: pointer; + + &::after { + position: absolute; + top: .75rem; + right: 1.25rem; + content: ''; + height: .5rem; + width: .5rem; + border-top: 1px solid $black; + border-left: 1px solid $black; + transform: rotate(-135deg); + transition: 200ms ease-in-out; + } + + &__text { + user-select: none; + } + } + + &__options { + position: absolute; + top: 100%; + left: 0; + width: 100%; + display: none; + border-top: 1px solid $white; + padding: .5rem 0; + border-bottom-left-radius: .5rem; + border-bottom-right-radius: .5rem; + box-sizing: border-box; + } + + &__option { + user-select: none; + cursor: pointer; + padding: .7rem 1.25rem; + font-size: .85rem; + transition: background-color 200ms ease-in-out; + + &:hover { + background-color: $grey; + } + + &:active { + background-color: darken($grey, 5); + } + + &--disabled { + opacity: .25; + cursor: default; + &:hover { + background-color: lighten($grey, 5); + } + + &:active { + background-color: lighten($grey, 5); + } + } + } + + &--opened { + .dropdown { + &__current-item { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + &::after { + top: 1rem; + transform: rotate(45deg); + } + } + + &__options { + @extend %col-nowrap; + + background-color: lighten($grey, 5); + z-index: 200; + } + } + } + + &--reversed { + .dropdown { + &__current-item { + background-color: white; + border: 1px solid $grey; + } + + &__options { + background-color: white; + border: 1px solid $grey; + } + } + } +} diff --git a/app/components/Hash/index.js b/app/components/Hash/index.js new file mode 100644 index 000000000..991a741bc --- /dev/null +++ b/app/components/Hash/index.js @@ -0,0 +1,9 @@ +import React from 'react'; + +export default function Hash({ value, start, end }) { + return ( + + {value.slice(0, start || 10)}...{value.slice(end || -10)} + + ); +} diff --git a/app/components/Header/header.scss b/app/components/Header/header.scss new file mode 100644 index 000000000..ff40ad676 --- /dev/null +++ b/app/components/Header/header.scss @@ -0,0 +1,72 @@ +@import '../../variables'; + +.header { + background-color: $azure-blue; + color: $white; + padding: .5rem 1rem; + font-size: .9rem; + + &__content { + @extend %row-nowrap; + + flex: 1 0 auto; + align-items: center; + max-width: 42.5rem; + margin: 0 auto; + } + + &__title { + flex: 1 0 auto; + font-weight: 600; + } + + &__block-height { + @extend %row-nowrap; + + flex: 0 0 auto; + } + + &__block-height-label { + color: rgba($white, .5); + font-weight: 600; + font-size: .8rem; + } + + &__block-height-text { + font-weight: 600; + margin-left: .5rem; + } + + &__status { + flex: 0 0 auto; + @extend %row-nowrap; + + position: relative; + font-size: .75rem; + padding: .6rem 1rem; + border-radius: 2rem; + background-color: rgba($white, .15); + margin-left: 1rem; + font-weight: 600; + align-items: center; + + &::before { + @extend %col-nowrap; + + flex: 0 0 auto; + align-items: center; + justify-content: center; + content: '\2713'; + width: .8rem; + height: .8rem; + line-height: .75rem; + font-size: .75rem; + background-color: $white; + border-radius: 1rem; + color: mix($white, $azure-blue, 25); + margin-right: .5rem; + } + + } +} + diff --git a/app/components/Header/index.js b/app/components/Header/index.js new file mode 100644 index 000000000..b77e04c4c --- /dev/null +++ b/app/components/Header/index.js @@ -0,0 +1,39 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import './header.scss'; + +export default class Header extends Component { + static propTypes = { + blockHeight: PropTypes.number.isRequired, + status: PropTypes.string.isRequired, + }; + + static defaultProps = { + blockHeight: 1234, + }; + + render() { + const { blockHeight } = this.props; + + return ( +
+
+
+ Handshake Wallet +
+
+
+ Current Height: +
+
+ {blockHeight} +
+
+
+ Synced +
+
+
+ ); + } +} diff --git a/app/components/IdleModal/idle-modal.scss b/app/components/IdleModal/idle-modal.scss new file mode 100644 index 000000000..0c9297721 --- /dev/null +++ b/app/components/IdleModal/idle-modal.scss @@ -0,0 +1,44 @@ +@import "../../variables"; + +.idle-modal { + &__wrapper { + width: 15rem; + padding: 2rem 3rem; + } + + &__title { + font-weight: 500; + font-size: 1.2rem; + text-align: center; + } + + &__time { + padding: 1.5rem 2rem; + margin: 1rem 0; + background-color: rgba($black, .1); + color: $manatee-gray; + font-weight: 500; + font-size: 2rem; + text-align: center; + border-radius: 1rem; + } + + &__actions { + @extend %row-nowrap; + + &__extend { + @extend %btn-secondary; + + flex: 1 0 auto; + margin-right: .25rem; + } + + &__logout { + @extend %btn-primary; + + flex: 1 0 auto; + margin-left: .25rem; + } + + } +} diff --git a/app/components/IdleModal/index.js b/app/components/IdleModal/index.js new file mode 100644 index 000000000..dea403e10 --- /dev/null +++ b/app/components/IdleModal/index.js @@ -0,0 +1,114 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import Modal from '../Modal'; +import * as walletAction from '../../ducks/walletActions'; +import './idle-modal.scss'; + +const MAX_IDLE = process.env.NODE_ENV === 'production' ? 5 : 1; + +class IdleModal extends Component { + static propTypes = { + idle: PropTypes.number.isRequired, + resetIdle: PropTypes.func.isRequired, + logout: PropTypes.func.isRequired, + }; + + state = { + isShowing: false, + timeRemaining: 60, + }; + + componentWillReceiveProps(nextProps) { + if (nextProps.isLocked && this.state.isShowing) { + this.close(); + return; + } + + if (!nextProps.isLocked && nextProps.idle >= MAX_IDLE && !this.state.isShowing) { + if (!this.interval) { + this.interval = setInterval(this.countDown, 1000); + } + } + } + + componentWillUnmount() { + if (this.interval) { + clearInterval(this.interval); + this.interval = null; + } + } + + logout = async () => { + await this.props.logout(); + this.close(); + }; + + extend = async () => { + this.props.resetIdle(); + this.close(); + }; + + close = () => { + clearInterval(this.interval); + this.interval = null; + this.setState({ + timeRemaining: 60, + isShowing: false, + }); + }; + + countDown = async () => { + const {timeRemaining} = this.state; + + if (timeRemaining < 1) { + await this.logout(); + return; + } + + this.setState({ + isShowing: true, + timeRemaining: timeRemaining - 1, + }); + }; + + render() { + if (!this.state.isShowing) { + return