├── .gitignore ├── CONTRIBUTING.md ├── Dockerfile-traefik ├── README.md ├── app ├── .dockerignore ├── .eslintrc ├── Dockerfile ├── nginx.conf ├── package.json ├── postinstall │ ├── config │ │ └── webpack.config.js │ └── scripts │ │ └── watch.js └── sources │ ├── extensionStoresMedia │ ├── chromestore_icon_128.jpg │ ├── chromestore_promo_1400x560.jpg │ ├── chromestore_promo_1400x560.psd │ ├── chromestore_promo_440x280.jpg │ └── chromestore_promo_920x680.jpg │ ├── public │ ├── _locales │ │ └── en │ │ │ └── messages.json │ ├── assets │ │ ├── icon128.png │ │ ├── icon16.png │ │ ├── icon48.png │ │ └── icons │ │ │ └── vendors │ │ │ ├── bear.svg │ │ │ ├── bird.svg │ │ │ ├── cat.svg │ │ │ ├── chicken.svg │ │ │ ├── dog.svg │ │ │ ├── elephant.svg │ │ │ ├── fish.svg │ │ │ ├── frog.svg │ │ │ ├── gorilla.svg │ │ │ ├── horse.svg │ │ │ ├── lion.svg │ │ │ ├── penguin.svg │ │ │ ├── shark.svg │ │ │ └── squirrel.svg │ ├── content.js │ ├── favicon.ico │ ├── index.html │ └── manifest.json │ └── src │ ├── assets │ ├── fonts │ │ ├── Montserrat-Bold.ttf │ │ ├── Montserrat-Italic.ttf │ │ ├── Montserrat-Light.ttf │ │ ├── Montserrat-Medium.ttf │ │ ├── Montserrat-Regular.ttf │ │ └── Montserrat-Semibold.ttf │ ├── icons │ │ ├── android-alert.svg │ │ ├── android.svg │ │ ├── apple.svg │ │ ├── arrow-left.svg │ │ ├── arrow-right-c.svg │ │ ├── arrow-right.svg │ │ ├── arrow-swap.svg │ │ ├── assets │ │ │ ├── altcoins.svg │ │ │ ├── bch.svg │ │ │ ├── btc.svg │ │ │ ├── dash.svg │ │ │ ├── eth.svg │ │ │ ├── lightning.svg │ │ │ ├── lnbtc.svg │ │ │ └── ltc.svg │ │ ├── back.svg │ │ ├── bookmark.svg │ │ ├── check-circle.svg │ │ ├── checkmark-circled.svg │ │ ├── chevron-down.svg │ │ ├── chevron-right.svg │ │ ├── chevron-up.svg │ │ ├── chevrons-down.svg │ │ ├── chrome.svg │ │ ├── clock.svg │ │ ├── close-circled.svg │ │ ├── close.svg │ │ ├── copy.svg │ │ ├── forward.svg │ │ ├── hamburger.svg │ │ ├── help-circled.svg │ │ ├── info.svg │ │ ├── information-circled.svg │ │ ├── linux.svg │ │ ├── log-in.svg │ │ ├── log-out.svg │ │ ├── loop.svg │ │ ├── macos.svg │ │ ├── message-square.svg │ │ ├── paper.svg │ │ ├── pause.svg │ │ ├── play.svg │ │ ├── plus-circle.svg │ │ ├── receive.svg │ │ ├── reply.svg │ │ ├── send.svg │ │ ├── settings.svg │ │ ├── social │ │ │ ├── github.svg │ │ │ ├── medium.svg │ │ │ ├── slack.svg │ │ │ ├── twitter.svg │ │ │ └── youtube.svg │ │ ├── url.svg │ │ ├── vendors │ │ │ ├── bear.svg │ │ │ ├── bird.svg │ │ │ ├── cat.svg │ │ │ ├── chicken.svg │ │ │ ├── dog.svg │ │ │ ├── elephant.svg │ │ │ ├── fish.svg │ │ │ ├── frog.svg │ │ │ ├── gorilla.svg │ │ │ ├── horse.svg │ │ │ ├── lion.svg │ │ │ ├── penguin.svg │ │ │ ├── shark.svg │ │ │ └── squirrel.svg │ │ ├── windows.svg │ │ └── x-circle.svg │ └── img │ │ └── logo │ │ ├── compact.png │ │ ├── full.png │ │ ├── full_clean.png │ │ └── textOnly.png │ ├── background.js │ ├── components │ ├── AssetChip │ │ ├── index.js │ │ ├── index.module.css │ │ └── view.js │ ├── BalanceSummary │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── CheckDestination │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── LoginForm │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── PaymentConfirmation │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── PaymentDetails │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── PaymentsGroup │ │ ├── PaymentItem │ │ │ ├── index.js │ │ │ ├── styles.js │ │ │ └── view.js │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── ReceivePayment │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── RestoreForm │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── SelectReceiveAsset │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── SettingsForm │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── SignupForm │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ └── common │ │ ├── BackButton.js │ │ ├── Button.js │ │ ├── CopyButton.js │ │ ├── Form.js │ │ ├── Header.js │ │ ├── Icon.js │ │ ├── Input.js │ │ ├── Message.js │ │ ├── Nav.js │ │ ├── Select.js │ │ ├── index.js │ │ └── utils.js │ ├── index.js │ ├── locales │ ├── en.js │ └── index.js │ ├── registerServiceWorker.js │ ├── scenes │ ├── Check │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── Confirm │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── Landing │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── Login │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── Payment │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── Payments │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── Receive │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── Restore │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── Select │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── Settings │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ └── Signup │ │ ├── index.js │ │ ├── styles.js │ │ └── view.js │ ├── setupProxy.js │ ├── stores │ ├── accounts.js │ ├── dataGeneric.js │ ├── denominations.js │ ├── index.js │ ├── info.js │ ├── payments.js │ ├── settings.js │ ├── ui.js │ ├── vendors.js │ └── wallets.js │ ├── styles.js │ └── utils │ ├── GA.js │ ├── HA.js │ ├── LiveChat.js │ ├── cryptonetChecker.js │ └── logging.js ├── docker-compose-build.yml ├── docker-compose-dev.yml ├── docker-compose.yml ├── package.json └── traefik.toml /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/node 2 | 3 | ### Node ### 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (https://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # TypeScript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | # parcel-bundler cache (https://parceljs.org/) 64 | .cache 65 | 66 | # next.js build output 67 | .next 68 | 69 | # nuxt.js build output 70 | .nuxt 71 | 72 | # vuepress build output 73 | .vuepress/dist 74 | 75 | # Serverless directories 76 | .serverless 77 | 78 | 79 | # End of https://www.gitignore.io/api/node 80 | 81 | # Misc 82 | .DS_Store 83 | .env.local 84 | .env.development.local 85 | .env.test.local 86 | .env.production.local 87 | .vscode 88 | 89 | build* 90 | 91 | *token.txt 92 | -------------------------------------------------------------------------------- /Dockerfile-traefik: -------------------------------------------------------------------------------- 1 | FROM traefik:alpine 2 | 3 | # ----------------------------------------------------------------------------- 4 | # Configure proxy 5 | # ----------------------------------------------------------------------------- 6 | 7 | COPY ./traefik.toml /etc/traefik/traefik.toml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Bitlum, get it now at bitlum.io 3 |

4 | 5 | ## Overview 6 | 7 | [bitlum.io](https://bitlum.io) - custodial Bitcoin Lightning Network wallet created in the form of a Chrome extension. It does not require any node installation, synchronisation and locking funds in channels: you can sign up, top up and start sending payments through the Lightning Network straight away. 8 | 9 | Wallet aimed on non-technical users who would like to experience Lightning Network and at same time would tolerate risk keeping small amounts of funds on custodial wallet without the technical knowledge needed to maintain a lightning node. 10 | 11 | To simplify on-boarding of new beta users we deposit $1 on every account during beta stage. If you are interested in beta testing fill up the form on [bitlum.io](https://bitlum.io), at the end you will be prompted to join our Telegram group. 12 | 13 | **WARNING: Despite we have balance limits of 0.008 BTC you can top up more through onchain. But we encourage you not to keep more than $50 on your balance: wallet still in beta stage and it is custodial. In case of errors we will not be able to refund any amount higher than our limits.** 14 | 15 |

16 | First beta tester page 17 | Receive funds page 18 | Payment confirmation page 19 | Payment list page 20 |

21 | 22 | ## Building from source 23 | 24 | Coming soon. 25 | -------------------------------------------------------------------------------- /app/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .eslintrc 3 | *Dockerfile* 4 | *docker-compose* 5 | /docs 6 | /node_modules 7 | /test 8 | -------------------------------------------------------------------------------- /app/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "webextensions": true 6 | }, 7 | "rules": { 8 | "linebreak-style": "off", 9 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], 10 | "no-unused-vars": ["warn"], 11 | "import/no-unresolved": "off", 12 | "react/require-default-props": "warn", 13 | "react/no-array-index-key": "warn", 14 | "arrow-parens": ["error", "as-needed"], 15 | "no-nested-ternary": ["warn"], 16 | "jsx-a11y/no-static-element-interactions": ["warn"], 17 | "jsx-a11y/label-has-for": [ 18 | 2, 19 | { 20 | "required": { 21 | "every": ["id"] 22 | } 23 | } 24 | ] 25 | }, 26 | "extends": "airbnb" 27 | } 28 | -------------------------------------------------------------------------------- /app/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | location / { 4 | root /usr/share/nginx/html; 5 | index index.html index.htm; 6 | try_files $uri $uri/ /index.html =404; 7 | } 8 | } -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bitlum-ui", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "i18next": "12.1.0", 7 | "styled-components": "^4.1.2", 8 | "react": "16.8.1", 9 | "react-dom": "^16.8.1", 10 | "react-scripts": "2.1.5", 11 | "prop-types": "^15.7.1", 12 | "react-router-dom": "^4.3.1", 13 | "react-i18next": "^8.3.8", 14 | "react-virtualized": "^9.21.0", 15 | "react-select": "^2.3.0", 16 | "qrcode.react": "^0.8.0", 17 | "react-tooltip": "^3.9.2", 18 | "mobx": "^5.8.0", 19 | "mobx-react": "^5.4.3", 20 | "mobx-react-devtools": "^6.0.3", 21 | "date-fns": "^1.30.1", 22 | "loglevel": "^1.6.1" 23 | }, 24 | "devDependencies": { 25 | "babel-plugin-styled-components": "^1.7.1", 26 | "http-proxy-middleware": "^0.19.0", 27 | "webpack-cli": "^3.2.3" 28 | }, 29 | "scripts": { 30 | "postinstall": "echo y | react-scripts eject && cp -rf postinstall/. ./", 31 | "start": "webpack --config scripts/watch.js --watch", 32 | "build": "react-scripts build", 33 | "test": "react-scripts test --env=jsdom", 34 | "eject": "react-scripts eject" 35 | }, 36 | "browserslist": { 37 | "development": [ 38 | "last 2 chrome versions", 39 | "last 2 firefox versions", 40 | "last 2 edge versions" 41 | ], 42 | "production": [ 43 | ">1%", 44 | "last 4 versions", 45 | "Firefox ESR", 46 | "not ie < 11" 47 | ] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/postinstall/scripts/watch.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | const paths = require('../config/paths'); 3 | 4 | function copyPublicFolder() { 5 | fs.copySync(paths.appPublic, '/opt/app/dist', { 6 | dereference: true, 7 | filter: file => file !== paths.appHtml, 8 | }); 9 | } 10 | 11 | copyPublicFolder(); 12 | 13 | module.exports = () => require('../config/webpack.config')('development'); 14 | -------------------------------------------------------------------------------- /app/sources/extensionStoresMedia/chromestore_icon_128.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/extensionStoresMedia/chromestore_icon_128.jpg -------------------------------------------------------------------------------- /app/sources/extensionStoresMedia/chromestore_promo_1400x560.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/extensionStoresMedia/chromestore_promo_1400x560.jpg -------------------------------------------------------------------------------- /app/sources/extensionStoresMedia/chromestore_promo_1400x560.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/extensionStoresMedia/chromestore_promo_1400x560.psd -------------------------------------------------------------------------------- /app/sources/extensionStoresMedia/chromestore_promo_440x280.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/extensionStoresMedia/chromestore_promo_440x280.jpg -------------------------------------------------------------------------------- /app/sources/extensionStoresMedia/chromestore_promo_920x680.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/extensionStoresMedia/chromestore_promo_920x680.jpg -------------------------------------------------------------------------------- /app/sources/public/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "Bitlum Wallet" 4 | }, 5 | "appDescription": { 6 | "message": "Lightning Network mainnet web wallet. Fast, easy, no node install and synchronisation, 2 minute access." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/sources/public/assets/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/public/assets/icon128.png -------------------------------------------------------------------------------- /app/sources/public/assets/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/public/assets/icon16.png -------------------------------------------------------------------------------- /app/sources/public/assets/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/public/assets/icon48.png -------------------------------------------------------------------------------- /app/sources/public/assets/icons/vendors/bird.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 17 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/sources/public/assets/icons/vendors/cat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 13 | 21 | 23 | 25 | 27 | 36 | 38 | 39 | -------------------------------------------------------------------------------- /app/sources/public/assets/icons/vendors/fish.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 16 | 24 | 26 | 27 | -------------------------------------------------------------------------------- /app/sources/public/assets/icons/vendors/penguin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 17 | 31 | 33 | 34 | -------------------------------------------------------------------------------- /app/sources/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/public/favicon.ico -------------------------------------------------------------------------------- /app/sources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 33 | Bitlum 34 | 35 | 36 | 37 | 38 |
39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /app/sources/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "__MSG_appName__", 3 | "short_name": "__MSG_appName__", 4 | "version": "0.2.7", 5 | "manifest_version": 2, 6 | "author": "https://bitlum.io", 7 | "description": "__MSG_appDescription__", 8 | "commands": { 9 | "_execute_browser_action": { 10 | "suggested_key": { 11 | "windows": "Alt+Shift+B", 12 | "mac": "Alt+Shift+B", 13 | "chromeos": "Alt+Shift+B", 14 | "linux": "Alt+Shift+B" 15 | } 16 | } 17 | }, 18 | "applications": { 19 | "gecko": { 20 | "id": "webextension@bitlum.io" 21 | } 22 | }, 23 | "default_locale": "en", 24 | "browser_action": { 25 | "default_title": "Bitlum", 26 | "default_popup": "index.html" 27 | }, 28 | "icons": { "16": "assets/icon16.png", "48": "assets/icon48.png", "128": "assets/icon128.png" }, 29 | "background": { 30 | "page": "background.html", 31 | "persistent": true 32 | }, 33 | "permissions": [ 34 | "cookies", 35 | "storage", 36 | "unlimitedStorage", 37 | "notifications", 38 | "activeTab", 39 | "*://*.bitlum.io/*", 40 | "webRequest", 41 | "webRequestBlocking", 42 | "contextMenus", 43 | "clipboardRead" 44 | ], 45 | "optional_permissions": ["", "tabs", "http://lvh.me/*"], 46 | "web_accessible_resources": ["inpage.js", "phishing.html", "index.html", "assets/*"], 47 | "externally_connectable": { 48 | "matches": ["*://*.bitlum.io/*"], 49 | "ids": ["*"] 50 | }, 51 | "content_security_policy": "script-src 'self' https://*.intercomcdn.com https://*.intercom.io https://www.google-analytics.com https://*.heapanalytics.com; object-src 'self';img-src * 'self' data:; style-src * 'unsafe-inline';" 52 | } 53 | -------------------------------------------------------------------------------- /app/sources/src/assets/fonts/Montserrat-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/src/assets/fonts/Montserrat-Bold.ttf -------------------------------------------------------------------------------- /app/sources/src/assets/fonts/Montserrat-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/src/assets/fonts/Montserrat-Italic.ttf -------------------------------------------------------------------------------- /app/sources/src/assets/fonts/Montserrat-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/src/assets/fonts/Montserrat-Light.ttf -------------------------------------------------------------------------------- /app/sources/src/assets/fonts/Montserrat-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/src/assets/fonts/Montserrat-Medium.ttf -------------------------------------------------------------------------------- /app/sources/src/assets/fonts/Montserrat-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/src/assets/fonts/Montserrat-Regular.ttf -------------------------------------------------------------------------------- /app/sources/src/assets/fonts/Montserrat-Semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/src/assets/fonts/Montserrat-Semibold.ttf -------------------------------------------------------------------------------- /app/sources/src/assets/icons/android-alert.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/android.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/apple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 11 | 12 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/arrow-right-c.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/arrow-swap.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/assets/altcoins.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/assets/bch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/assets/btc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/assets/dash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/assets/eth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/assets/lightning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/assets/lnbtc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/assets/ltc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/back.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/bookmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/check-circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/checkmark-circled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/chevron-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/chevron-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/chevron-up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/chevrons-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/chrome.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 11 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/clock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/close-circled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/copy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/forward.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/hamburger.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/help-circled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/info.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/information-circled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/linux.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/log-in.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/log-out.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/loop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/message-square.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/paper.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/pause.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/plus-circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/receive.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/reply.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/send.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/settings.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/social/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 13 | 14 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/social/medium.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Monogram 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/social/slack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/social/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/social/youtube.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/url.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/vendors/bird.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 17 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/vendors/cat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 13 | 21 | 23 | 25 | 27 | 36 | 38 | 39 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/vendors/fish.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 16 | 24 | 26 | 27 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/vendors/frog.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 13 | 20 | 30 | 33 | 34 | 36 | 38 | 40 | 41 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/vendors/penguin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 17 | 31 | 33 | 34 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/windows.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/sources/src/assets/icons/x-circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/sources/src/assets/img/logo/compact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/src/assets/img/logo/compact.png -------------------------------------------------------------------------------- /app/sources/src/assets/img/logo/full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/src/assets/img/logo/full.png -------------------------------------------------------------------------------- /app/sources/src/assets/img/logo/full_clean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/src/assets/img/logo/full_clean.png -------------------------------------------------------------------------------- /app/sources/src/assets/img/logo/textOnly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlum/bitlum-front/9308a14c3dca01f8269ef6b617bac61485162c45/app/sources/src/assets/img/logo/textOnly.png -------------------------------------------------------------------------------- /app/sources/src/components/AssetChip/index.js: -------------------------------------------------------------------------------- 1 | import view from './view'; 2 | 3 | export default view; 4 | -------------------------------------------------------------------------------- /app/sources/src/components/AssetChip/index.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | display: flex; 3 | align-items: center; 4 | font-weight: 600; 5 | font-size: 2em; 6 | } 7 | 8 | .icon { 9 | height: 1.4em; 10 | width: 1.4em; 11 | margin-right: 0.5em; 12 | } 13 | 14 | .name { 15 | opacity: 0.85; 16 | } -------------------------------------------------------------------------------- /app/sources/src/components/AssetChip/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | 13 | // More assets icons here 14 | // https://github.com/cjdowner/cryptocurrency-icons/tree/master/svg/color 15 | import { ReactComponent as BTC } from 'assets/icons/assets/btc.svg'; 16 | import { ReactComponent as LNBTC } from 'assets/icons/assets/lightning.svg'; 17 | import { ReactComponent as ETH } from 'assets/icons/assets/eth.svg'; 18 | import { ReactComponent as DASH } from 'assets/icons/assets/dash.svg'; 19 | import { ReactComponent as LTC } from 'assets/icons/assets/ltc.svg'; 20 | import { ReactComponent as BCH } from 'assets/icons/assets/bch.svg'; 21 | 22 | import styles from './index.module.css'; 23 | 24 | // ----------------------------------------------------------------------------- 25 | // Code 26 | // ----------------------------------------------------------------------------- 27 | 28 | const icons = { 29 | BTC: { 30 | blockchain: , 31 | lightning: , 32 | }, 33 | ETH: { 34 | blockchain: , 35 | }, 36 | DASH: { 37 | blockchain: , 38 | }, 39 | LTC: { 40 | blockchain: , 41 | }, 42 | BCH: { 43 | blockchain: , 44 | }, 45 | }; 46 | 47 | const AssetChip = ({ asset, type, onlyIcon, option, onSelect, onFocus, isFocused, className }) => { 48 | const currentAsset = option ? JSON.parse(option.value).asset : asset; 49 | const currentType = option ? JSON.parse(option.value).type : type; 50 | return ( 51 |
52 | {currentAsset && currentType && icons[currentAsset][currentType]} 53 | {onlyIcon ? null : {currentAsset}} 54 |
55 | ); 56 | }; 57 | 58 | AssetChip.propTypes = { 59 | asset: PropTypes.oneOf(Object.keys(icons)), 60 | type: PropTypes.string, 61 | option: PropTypes.shape({ 62 | value: PropTypes.string, 63 | }), 64 | onSelect: PropTypes.func, 65 | onFocus: PropTypes.func, 66 | className: PropTypes.string, 67 | isFocused: PropTypes.bool, 68 | }; 69 | 70 | AssetChip.defaultProps = { 71 | asset: 'BTC', 72 | type: 'lightning', 73 | option: null, 74 | onSelect: null, 75 | onFocus: null, 76 | className: '', 77 | isFocused: false, 78 | }; 79 | 80 | export default AssetChip; 81 | -------------------------------------------------------------------------------- /app/sources/src/components/BalanceSummary/index.js: -------------------------------------------------------------------------------- 1 | import { withNamespaces } from 'react-i18next'; 2 | 3 | import view from './view'; 4 | 5 | export default withNamespaces()(view); 6 | -------------------------------------------------------------------------------- /app/sources/src/components/BalanceSummary/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | import { NavLink } from 'react-router-dom'; 13 | 14 | import { P, Img, Span } from 'components/common'; 15 | 16 | // ----------------------------------------------------------------------------- 17 | // Code 18 | // ----------------------------------------------------------------------------- 19 | 20 | export * from 'components/common'; 21 | 22 | export const Main = styled(P)` 23 | opacity: 0.9; 24 | font-size: 2.3em; 25 | font-weight: 400; 26 | `; 27 | 28 | export const Additional = styled(P)` 29 | font-size: 1em; 30 | font-weight: 600; 31 | opacity: 0.5; 32 | position: absolute; 33 | bottom: 0.8em; 34 | left: 1em; 35 | `; 36 | 37 | export const Receive = styled(NavLink)` 38 | display: flex; 39 | justify-content: center; 40 | align-items: center; 41 | opacity: 0.75; 42 | position: absolute; 43 | top: 0.8em; 44 | right: 1em; 45 | font-weight: 500; 46 | font-size: 0.9em; 47 | &:hover { 48 | opacity: 1; 49 | } 50 | & ${Img} { 51 | filter: invert(100%); 52 | height: 1em; 53 | width: 1em; 54 | margin-left: 0.3em; 55 | margin-top: 0.1em; 56 | } 57 | `; 58 | 59 | export const Send = styled(NavLink)` 60 | font: var(--fonts__header_bold); 61 | margin-top: 1em; 62 | width: 100%; 63 | `; 64 | 65 | export const Root = styled.div` 66 | position: relative; 67 | display: flex; 68 | flex-direction: column; 69 | justify-content: center; 70 | align-items: center; 71 | text-align: center; 72 | font-weight: 200; 73 | & > ${Span} { 74 | padding-left: 1em; 75 | margin-top: 1em; 76 | } 77 | ${({ appearance }) => 78 | appearance !== 'onlyBalance' && 79 | ` &:before { 80 | content: 'BALANCE'; 81 | position: absolute; 82 | top: 0.8em; 83 | left: 1em; 84 | opacity: 0.6; 85 | } 86 | & ${Main} { 87 | padding-left: 0.4em; 88 | } 89 | align-items: flex-start; 90 | // background: var(--colors__bg_dark); 91 | background: linear-gradient(45deg, #461a99 0%, #249ade 65%, #41d4cb 100%); 92 | filter: grayscale(1); 93 | opacity: 0.9; 94 | border-top-right-radius: 0.7em; 95 | border-top-left-radius: 0.7em; 96 | // box-shadow: 0 0.6em 0.7em 0.2em rgba(21, 24, 46, 0.16); 97 | color: var(--colors__text_accent); 98 | `} 99 | `; 100 | 101 | export default Root; 102 | -------------------------------------------------------------------------------- /app/sources/src/components/BalanceSummary/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | 13 | import logger from 'utils/logging'; 14 | 15 | import receiveIcon from 'assets/icons/plus-circle.svg'; 16 | 17 | import { Root, Main, Additional, Receive, Send, Img, Span } from './styles'; 18 | 19 | const log = logger(); 20 | 21 | // ----------------------------------------------------------------------------- 22 | // Code 23 | // ----------------------------------------------------------------------------- 24 | 25 | export const BalanceSummary = ({ accounts, className, appearance = 'normal', denomination, t }) => { 26 | 27 | if (!accounts.get.data && accounts.get.loading) { 28 | return ( 29 | 30 | Loading balance info... 31 | 32 | ); 33 | } 34 | 35 | if ( 36 | (accounts.get.error && !accounts.get.data) || 37 | (!accounts.get.error && (!accounts.get.data || !accounts.get.data.balances)) 38 | ) { 39 | return ( 40 | 41 | Unable to load balance info 42 | 43 | ); 44 | } 45 | 46 | if (appearance === 'onlyBalance') { 47 | return ( 48 | 49 | Available 50 | {Object.keys(accounts.get.data.balances).map(asset => [ 51 |
52 | {accounts.get.data.balances[asset].denominationsAvailable[denomination].toString()} 53 |
, 54 | ])} 55 |
56 | ); 57 | } 58 | return ( 59 | 60 | 61 | Receive 62 | 63 | 64 | {Object.keys(accounts.get.data.balances).map(asset => [ 65 |
66 | {accounts.get.data.balances[asset].denominationsAvailable.main.toString()} 67 |
, 68 | 69 | {accounts.get.data.balances[asset].denominationsAvailable.additional.toString()} 70 | , 71 | ])} 72 | {/* 73 | {totalBalance === 0 ? 'Receive' : 'Pay'} 74 | */} 75 |
76 | ); 77 | }; 78 | 79 | BalanceSummary.propTypes = {}; 80 | 81 | BalanceSummary.defaultProps = {}; 82 | 83 | export default BalanceSummary; 84 | -------------------------------------------------------------------------------- /app/sources/src/components/CheckDestination/index.js: -------------------------------------------------------------------------------- 1 | import { withNamespaces } from 'react-i18next'; 2 | import { withRouter } from 'react-router'; 3 | import { observer } from 'mobx-react'; 4 | 5 | import view from './view'; 6 | 7 | export default withNamespaces()(withRouter(observer(view))); 8 | -------------------------------------------------------------------------------- /app/sources/src/components/CheckDestination/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | import { withLoader, Form, Span, P, A, Input, Message as MessageRaw } from 'components/common'; 12 | 13 | // ----------------------------------------------------------------------------- 14 | // Code 15 | // ----------------------------------------------------------------------------- 16 | 17 | export * from 'components/common'; 18 | 19 | export const DestinationInfo = styled.div` 20 | display: flex; 21 | flex-direction: column; 22 | & ${P} { 23 | margin-bottom: 0.5em; 24 | } 25 | & ${Span} { 26 | font-weight: 600; 27 | } 28 | `; 29 | 30 | export const Message = styled(MessageRaw)` 31 | flex-direction: column; 32 | & ${Span} { 33 | font-weight: 600; 34 | margin-top: 0.5em; 35 | cursor: pointer; 36 | } 37 | `; 38 | 39 | export const Maintenance = styled(MessageRaw)` 40 | position: relative; 41 | display: flex; 42 | flex-direction: column; 43 | justify-content: center; 44 | align-items: center; 45 | white-space: pre-wrap; 46 | text-align: center; 47 | background: var(--colors__bg_warn); 48 | font-weight: 400; 49 | & * { 50 | word-break: break-word; 51 | } 52 | & > ${Span} { 53 | font-weight: 600; 54 | padding: 0!important; 55 | } 56 | `; 57 | 58 | export const Root = withLoader(styled(Form)` 59 | height: 100%; 60 | 61 | & > ${P} { 62 | display: flex; 63 | flex-direction: column; 64 | } 65 | 66 | & > ${P}:first-child { 67 | margin-bottom: 1em; 68 | margin-left: 1.2em; 69 | } 70 | 71 | & ${P}:last-of-type { 72 | margin-top: auto; 73 | margin-bottom: 1em; 74 | border-top: 0.05em solid var(--colors__bg_dark); 75 | font-size: 0.8em; 76 | padding: 0.5em 1.5em; 77 | } 78 | 79 | & ${P}:last-of-type * { 80 | margin-top: 1em; 81 | word-break: break-word; 82 | } 83 | 84 | & ${P}:last-of-type ${Span}:first-of-type { 85 | font-weight: 500; 86 | } 87 | 88 | & input::placeholder { 89 | font-size: 0.8em; 90 | } 91 | & > ${Span} { 92 | word-break: initial; 93 | opacity: 0.8; 94 | } 95 | 96 | & ${A} { 97 | font-weight: 700; 98 | color: var(--colors__bg_accent); 99 | cursor: pointer; 100 | } 101 | 102 | & > ${A} { 103 | font-size: 0.6em; 104 | font-weight: 500; 105 | text-decoration: underline; 106 | margin-left: 2em; 107 | margin-top: 1em; 108 | } 109 | `); 110 | 111 | export default Root; 112 | -------------------------------------------------------------------------------- /app/sources/src/components/LoginForm/index.js: -------------------------------------------------------------------------------- 1 | import { observer, inject } from 'mobx-react'; 2 | import { withNamespaces } from 'react-i18next'; 3 | 4 | import view from './view'; 5 | 6 | export default withNamespaces()(inject('accounts')(observer(view))); 7 | -------------------------------------------------------------------------------- /app/sources/src/components/LoginForm/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | import { withLoader, Form, Button, Message } from 'components/common'; 12 | 13 | // ----------------------------------------------------------------------------- 14 | // Code 15 | // ----------------------------------------------------------------------------- 16 | 17 | export * from 'components/common'; 18 | 19 | export const Root = withLoader(styled(Form)` 20 | & ${Button} { 21 | height: 2.7em; 22 | } 23 | & ${Message} { 24 | margin: 1em 0; 25 | } 26 | & input { 27 | font-size: 0.9em; 28 | } 29 | & label { 30 | left: 1.4em; 31 | } 32 | `); 33 | 34 | export default Root; 35 | -------------------------------------------------------------------------------- /app/sources/src/components/LoginForm/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | 13 | import logger from 'utils/logging'; 14 | 15 | import { Root, Input, Button, Message } from './styles'; 16 | 17 | const log = logger(); 18 | 19 | // ----------------------------------------------------------------------------- 20 | // Code 21 | // ----------------------------------------------------------------------------- 22 | 23 | export const LoginForm = ({ accounts, className, t }) => ( 24 | { 27 | e.preventDefault(); 28 | const emailElement = e.target.querySelector('#authEmail'); 29 | const email = emailElement && emailElement.value; 30 | const passwordElement = e.target.querySelector('#authPassword'); 31 | const password = passwordElement && passwordElement.value; 32 | accounts.authenticate.run(email, password); 33 | }} 34 | loading={accounts.authenticate.loading} 35 | > 36 | 44 | 52 | 53 | {accounts.authenticate.error && ( 54 | 55 | {t([`errors.${accounts.authenticate.error.code}`, 'errors.default'])} 56 | 57 | )} 58 | {accounts.authenticate.data && Object.keys(accounts.authenticate.data).length === 0 && ( 59 | No account was found with such email 60 | )} 61 | 64 | 65 | ); 66 | 67 | LoginForm.propTypes = {}; 68 | 69 | LoginForm.defaultProps = {}; 70 | 71 | export default LoginForm; 72 | -------------------------------------------------------------------------------- /app/sources/src/components/PaymentConfirmation/index.js: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react'; 2 | import { withNamespaces } from 'react-i18next'; 3 | 4 | import view from './view'; 5 | 6 | export default withNamespaces()(observer(view)); 7 | -------------------------------------------------------------------------------- /app/sources/src/components/PaymentDetails/index.js: -------------------------------------------------------------------------------- 1 | import { withNamespaces } from 'react-i18next'; 2 | 3 | import view from './view'; 4 | 5 | export default withNamespaces()(view); 6 | -------------------------------------------------------------------------------- /app/sources/src/components/PaymentsGroup/PaymentItem/index.js: -------------------------------------------------------------------------------- 1 | import { withNamespaces } from 'react-i18next'; 2 | import { withRouter } from 'react-router'; 3 | 4 | import view from './view'; 5 | 6 | export default withNamespaces()(withRouter(view)); 7 | -------------------------------------------------------------------------------- /app/sources/src/components/PaymentsGroup/PaymentItem/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | import { P, Span, Img } from 'components/common'; 13 | 14 | import goToDetailsIcon from 'assets/icons/chevron-right.svg'; 15 | 16 | // ----------------------------------------------------------------------------- 17 | // Code 18 | // ----------------------------------------------------------------------------- 19 | 20 | export * from 'components/common'; 21 | 22 | export const AmountMain = styled.span` 23 | font-size: 1.12em; 24 | color: ${({ direction }) => direction === 'incoming' && 'var(--colors__text_ok)'}; 25 | `; 26 | 27 | export const AmountAdditional = styled.span` 28 | font-size: 0.8em; 29 | color: var(--colors__text_bright); 30 | margin-top: 0.4em; 31 | `; 32 | 33 | export const Time = styled.span``; 34 | export const Description = styled.span` 35 | max-width: 15em; 36 | word-break: break-word; 37 | white-space: ${({ wrap }) => (wrap ? 'normal' : 'nowrap')}; 38 | margin-top: 0.6em; 39 | font-size: 0.8em; 40 | overflow: hidden; 41 | text-overflow: ellipsis; 42 | color: var(--colors__text_bright); 43 | ${({ wrap }) => 44 | wrap 45 | ? `display: -webkit-box; 46 | -webkit-line-clamp: 5; 47 | -webkit-box-orient: vertical;` 48 | : ''}; 49 | `; 50 | 51 | export const Info = styled.div` 52 | display: flex; 53 | flex-direction: column; 54 | `; 55 | 56 | export const Amount = styled.div` 57 | display: flex; 58 | flex-direction: column; 59 | align-items: flex-end; 60 | margin-left: auto; 61 | `; 62 | 63 | export const Root = styled.div` 64 | display: flex; 65 | align-items: flex-start; 66 | font-size: 0.8em; 67 | margin-left: 1.7em; 68 | padding-right: 3.5em; 69 | position: relative; 70 | &:not(:last-child) { 71 | border-bottom: 0.1em solid var(--colors__bg_dark); 72 | } 73 | & ${Info}, & ${Amount} { 74 | margin-top: 1.6em; 75 | margin-bottom: 1.6em; 76 | } 77 | &:after { 78 | position: absolute; 79 | right: 1.4em; 80 | opacity: 0.4; 81 | /* background: no-repeat url(${goToDetailsIcon}) center; */ 82 | background-color: var(--colors__bg_dark); 83 | height: 100%; 84 | width: 0.7em; 85 | background-size: contain; 86 | } 87 | &:hover:after { 88 | display: block; 89 | content: ''; 90 | } 91 | 92 | background-color: var(--colors__bg); 93 | `; 94 | 95 | export default Root; 96 | -------------------------------------------------------------------------------- /app/sources/src/components/PaymentsGroup/PaymentItem/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React, { Component } from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import formatDate from 'date-fns/format'; 13 | 14 | import NavLink from 'react-router-dom'; 15 | 16 | import logger from 'utils/logging'; 17 | 18 | import { Root, P, AmountAdditional, AmountMain, Description, Amount, Time, Info } from './styles'; 19 | 20 | const log = logger(); 21 | 22 | // ----------------------------------------------------------------------------- 23 | // Code 24 | // ----------------------------------------------------------------------------- 25 | 26 | export const PaymentItem = ({ 27 | className, 28 | history, 29 | puid, 30 | updatedAt, 31 | description, 32 | isDescriptionReadable, 33 | direction, 34 | mainDenominationString, 35 | additionalDenominationString, 36 | t, 37 | }) => { 38 | return ( 39 | { 42 | history.push(`/payments/${puid}`); 43 | }} 44 | > 45 | 46 | 47 | {description} 48 | 49 | 50 | {mainDenominationString} 51 | {additionalDenominationString} 52 | 53 | 54 | ); 55 | }; 56 | 57 | PaymentItem.propTypes = {}; 58 | 59 | PaymentItem.defaultProps = {}; 60 | 61 | export default PaymentItem; 62 | -------------------------------------------------------------------------------- /app/sources/src/components/PaymentsGroup/index.js: -------------------------------------------------------------------------------- 1 | import { withNamespaces } from 'react-i18next'; 2 | import { withRouter } from 'react-router'; 3 | 4 | import view from './view'; 5 | 6 | export default withNamespaces()(withRouter(view)); 7 | -------------------------------------------------------------------------------- /app/sources/src/components/PaymentsGroup/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React, { Component, useState } from 'react'; 11 | import PropTypes from 'prop-types'; 12 | 13 | import logger from 'utils/logging'; 14 | 15 | import { 16 | Root, 17 | Span, 18 | P, 19 | AmountAdditional, 20 | AmountMain, 21 | Vendor, 22 | Amount, 23 | Status, 24 | Button, 25 | Img, 26 | VendorIcon, 27 | GroupInfo, 28 | GroupedItems, 29 | PaymentItem, 30 | } from './styles'; 31 | 32 | const log = logger(); 33 | 34 | // ----------------------------------------------------------------------------- 35 | // Code 36 | // ----------------------------------------------------------------------------- 37 | 38 | export const PaymentsGroup = ({ 39 | className, 40 | history, 41 | payments, 42 | status, 43 | vendorName, 44 | vendorIcon, 45 | vendorColor, 46 | t, 47 | }) => { 48 | const [folded, toggleFold] = useState(true); 49 | const groupedAmountMain = payments.reduce((p, c) => p + c.denominations.main.totalRaw, 0); 50 | 51 | const groupedAmountAdditional = payments.reduce( 52 | (p, c) => p + c.denominations.additional.totalRaw, 53 | 0, 54 | ); 55 | const positiveTotal = groupedAmountMain >= 0 && groupedAmountAdditional > 0; 56 | return ( 57 | 58 | { 61 | toggleFold(!folded); 62 | }} 63 | > 64 | 65 | 66 | 67 | 68 |

69 | {vendorName} 70 | {payments[0].description || payments[0].receipt} 71 |

72 |
73 | 74 | 75 | {payments[0].denominations.main.stringify(groupedAmountMain)} 76 | 77 | 78 | {payments[0].denominations.additional.stringify(groupedAmountAdditional)} 79 | 80 | 81 | 82 |
83 | {folded ? null : ( 84 | 85 | {payments.map(payment => ( 86 | 96 | ))} 97 | 98 | )} 99 |
100 | ); 101 | }; 102 | 103 | PaymentsGroup.propTypes = {}; 104 | 105 | PaymentsGroup.defaultProps = {}; 106 | 107 | export default PaymentsGroup; 108 | -------------------------------------------------------------------------------- /app/sources/src/components/ReceivePayment/index.js: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react'; 2 | import { withNamespaces } from 'react-i18next'; 3 | import { withRouter } from 'react-router'; 4 | 5 | import view from './view'; 6 | 7 | export default withNamespaces()(withRouter(observer(view))); 8 | -------------------------------------------------------------------------------- /app/sources/src/components/RestoreForm/index.js: -------------------------------------------------------------------------------- 1 | import { observer, inject } from 'mobx-react'; 2 | import { withNamespaces } from 'react-i18next'; 3 | 4 | import view from './view'; 5 | 6 | export default withNamespaces()(inject('accounts')(observer(view))); 7 | -------------------------------------------------------------------------------- /app/sources/src/components/RestoreForm/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | import { withLoader, Form, Button, Message, A, P } from 'components/common'; 12 | 13 | // ----------------------------------------------------------------------------- 14 | // Code 15 | // ----------------------------------------------------------------------------- 16 | 17 | export * from 'components/common'; 18 | 19 | export const Root = withLoader(styled(Form)` 20 | & ${A} { 21 | cursor: pointer; 22 | font-weight: 500; 23 | color: var(--colors__bg_accent); 24 | } 25 | 26 | & ${Button} { 27 | height: 2.7em; 28 | } 29 | & ${Message} { 30 | margin: 1em 0; 31 | } 32 | & > ${P} { 33 | margin-left: 0.3em; 34 | font-size: 0.7em; 35 | font-weight: 500; 36 | color: var(--colors__text_dark); 37 | margin-top: 1em; 38 | margin-bottom: 0.4em; 39 | white-space: pre-line; 40 | } 41 | & input { 42 | font-size: 0.9em; 43 | } 44 | & label { 45 | left: 1.4em; 46 | } 47 | `); 48 | 49 | export default Root; 50 | -------------------------------------------------------------------------------- /app/sources/src/components/RestoreForm/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import { Trans } from 'react-i18next'; 13 | 14 | import logger from 'utils/logging'; 15 | 16 | import { Root, Input, Button, Message, A, P } from './styles'; 17 | 18 | const log = logger(); 19 | 20 | // ----------------------------------------------------------------------------- 21 | // Code 22 | // ----------------------------------------------------------------------------- 23 | 24 | export const RestoreForm = ({ accounts, className, history, t, exists }) => ( 25 | { 28 | e.preventDefault(); 29 | const emailElement = e.target.querySelector('#restoreEmail'); 30 | const email = emailElement && emailElement.value; 31 | accounts.sendRestoreLink.run({ body: { email } }); 32 | }} 33 | loading={accounts.sendRestoreLink.loading} 34 | > 35 | 43 | {accounts.sendRestoreLink.error && ( 44 | 45 | {t([`errors.${accounts.sendRestoreLink.error.code}`, 'errors.default'])} 46 | 47 | )} 48 | {accounts.sendRestoreLink.data && ( 49 | 50 | {t('account.restoreSent', { 51 | email: 52 | document.querySelector('#restoreEmail') && 53 | document.querySelector('#restoreEmail').value, 54 | })} 55 | 56 | )} 57 | 60 | 61 | ); 62 | 63 | RestoreForm.propTypes = {}; 64 | 65 | RestoreForm.defaultProps = {}; 66 | 67 | export default RestoreForm; 68 | -------------------------------------------------------------------------------- /app/sources/src/components/SelectReceiveAsset/index.js: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react'; 2 | import { withNamespaces } from 'react-i18next'; 3 | import { withRouter } from 'react-router'; 4 | 5 | import view from './view'; 6 | 7 | export default withNamespaces()(withRouter(observer(view))); 8 | -------------------------------------------------------------------------------- /app/sources/src/components/SelectReceiveAsset/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | import { withLoader } from 'components/common'; 13 | 14 | import { Img, Span, P } from 'components/common'; 15 | 16 | import goToDetailsIcon from 'assets/icons/chevron-right.svg'; 17 | 18 | // ----------------------------------------------------------------------------- 19 | // Code 20 | // ----------------------------------------------------------------------------- 21 | 22 | export * from 'components/common'; 23 | 24 | export const AssetItem = styled.div` 25 | cursor: pointer; 26 | position: relative; 27 | display: flex; 28 | align-items: center; 29 | margin-left: 2rem; 30 | margin-right: 2rem; 31 | width: calc(100vw - 4rem); 32 | background-color: var(--colors__bg_bright); 33 | border-radius: 0.2em; 34 | overflow: hidden; 35 | margin-bottom: 0.3rem; 36 | padding: 1em 1em; 37 | padding-right: 2em; 38 | margin-bottom: 0.5em; 39 | & ${Img} { 40 | height: 2.7em; 41 | width: 2.7em; 42 | opacity: 0.7; 43 | margin-right: 1.6rem; 44 | border-radius: 0.3em; 45 | } 46 | & ${P} { 47 | display: flex; 48 | flex-direction: column; 49 | } 50 | & ${Span} { 51 | word-break: break-word; 52 | } 53 | & ${Span}:first-child { 54 | font: var(--fonts__text_bold); 55 | } 56 | & ${Span}:last-child { 57 | padding-top: 0.5em; 58 | font-size: 0.8em; 59 | color: var(--colors__text_dark); 60 | } 61 | 62 | &:after { 63 | position: absolute; 64 | right: 0; 65 | top: 0; 66 | opacity: 0.4; 67 | /* background: no-repeat url(${goToDetailsIcon}) center; */ 68 | background-color: var(--colors__bg_dark); 69 | height: 100%; 70 | width: 0.6em; 71 | background-size: contain; 72 | } 73 | &:hover:after { 74 | display: block; 75 | content: ''; 76 | } 77 | `; 78 | 79 | export const Root = styled.div` 80 | background-color: var(--colors__bg); 81 | justify-content: center; 82 | display: flex; 83 | flex-direction: column; 84 | 85 | & ${AssetItem}:last-of-type { 86 | /* background: transparent; */ 87 | margin-top: 3em; 88 | } 89 | `; 90 | 91 | export default Root; 92 | -------------------------------------------------------------------------------- /app/sources/src/components/SelectReceiveAsset/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | 13 | import logger from 'utils/logging'; 14 | 15 | import { Root, AssetItem, P, Img, Span } from './styles'; 16 | 17 | import iconOnchain from 'assets/icons/assets/btc.svg'; 18 | import iconOffchain from 'assets/icons/assets/lightning.svg'; 19 | import iconAltcoins from 'assets/icons/assets/altcoins.svg'; 20 | 21 | const log = logger(); 22 | 23 | // ----------------------------------------------------------------------------- 24 | // Code 25 | // ----------------------------------------------------------------------------- 26 | 27 | export const SelectReceiveAsset = ({ className, history, t }) => { 28 | return ( 29 | 30 | { 32 | history.push( 33 | `/payments/receive/confirm?receive=${JSON.stringify({ 34 | type: 'lightning', 35 | asset: 'BTC', 36 | })}`, 37 | ); 38 | }} 39 | > 40 | 41 |

42 | Lightning Network Bitcoin 43 | Receive with lightning faster than other methods and takes less fees 44 |

45 |
46 | { 48 | history.push( 49 | `/payments/receive/confirm?receive=${JSON.stringify({ 50 | type: 'blockchain', 51 | asset: 'BTC', 52 | })}`, 53 | ); 54 | }} 55 | > 56 | 57 |

58 | On-chain Bitcoin 59 | 60 | Receive with on-chain / regular Bitcoin payment will take about ~10 minutes to confirm 61 | 62 |

63 |
64 | { 66 | window.open( 67 | 'https://zigzag.io?utm_source=integration&utm_medium=bitlumwallet&utm_campaign=receiveLink', 68 | '_blank', 69 | ); 70 | }} 71 | > 72 | 73 |

74 | Recieve with alt-coins 75 | 76 | Receive with alt-coin implies conversion of alt-coin to Bitcoin with fee using zigzag.io 77 | service 78 | 79 |

80 |
81 |
82 | ); 83 | }; 84 | 85 | SelectReceiveAsset.propTypes = {}; 86 | 87 | SelectReceiveAsset.defaultProps = {}; 88 | 89 | export default SelectReceiveAsset; 90 | -------------------------------------------------------------------------------- /app/sources/src/components/SettingsForm/index.js: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react'; 2 | import { withNamespaces } from 'react-i18next'; 3 | import { withRouter } from 'react-router'; 4 | 5 | import view from './view'; 6 | 7 | export default withNamespaces()(withRouter(observer(view))); 8 | -------------------------------------------------------------------------------- /app/sources/src/components/SettingsForm/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | import SelectRaw from 'react-select'; 12 | 13 | import { withLoader } from 'components/common'; 14 | 15 | import { Img, Span, P, Button } from 'components/common'; 16 | 17 | // ----------------------------------------------------------------------------- 18 | // Code 19 | // ----------------------------------------------------------------------------- 20 | 21 | export * from 'components/common'; 22 | 23 | export const Select = styled(SelectRaw)` 24 | min-width: 7em; 25 | `; 26 | 27 | export const SettingsItem = styled.div` 28 | position: relative; 29 | display: flex; 30 | align-items: center; 31 | margin-left: 2rem; 32 | width: calc(100vw - 4rem); 33 | background-color: var(--colors__bg_bright); 34 | border-radius: 0.2em; 35 | margin-bottom: 0.3rem; 36 | padding: 1em 1em; 37 | padding-right: 2em; 38 | margin-bottom: 0.5em; 39 | 40 | & > *:first-child { 41 | font: var(--fonts__text_bold); 42 | } 43 | & > *:last-child:not(:first-child) { 44 | margin-left: auto; 45 | cursor: pointer; 46 | } 47 | 48 | & ${Span} { 49 | word-break: break-word; 50 | font-size: 0.8em; 51 | } 52 | `; 53 | 54 | export const Root = styled.div` 55 | height: 100%; 56 | top: 0; 57 | width: 100%; 58 | height: 100%; 59 | background-color: var(--colors__bg); 60 | z-index: 10; 61 | justify-content: center; 62 | display: flex; 63 | flex-direction: column; 64 | `; 65 | 66 | export default Root; 67 | -------------------------------------------------------------------------------- /app/sources/src/components/SettingsForm/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | 13 | import logger from 'utils/logging'; 14 | 15 | import { Root, SettingsItem, P, Button, Span, Select } from './styles'; 16 | 17 | const log = logger(); 18 | 19 | // ----------------------------------------------------------------------------- 20 | // Code 21 | // ----------------------------------------------------------------------------- 22 | 23 | export const SettingsForm = ({ className, settings, accounts, denominations, t }) => { 24 | const denominationsAvailable = Object.keys(denominations.get.data.BTC) 25 | .map( 26 | denomination => 27 | !denomination.match(/(main|additional)/) && { 28 | value: denomination, 29 | label: denominations.get.data.BTC[denomination].sign, 30 | }, 31 | ) 32 | .filter(denomination => denomination); 33 | 34 | return ( 35 | 36 | 37 | Main currency 38 | denomination.value === settings.get.data.denominations_BTC_additional, 60 | )} 61 | onChange={data => { 62 | settings.set.run({ denominations_BTC_additional: data.value }); 63 | }} 64 | /> 65 | 66 | {settings.get.data.content_script_permissions !== 'granted' && ( 67 | { 69 | window.chrome.permissions.request( 70 | { 71 | permissions: ['tabs'], 72 | origins: [''], 73 | }, 74 | granted => { 75 | settings.set.run({ content_script_permissions: granted ? 'granted' : 'denied' }); 76 | }, 77 | ); 78 | }} 79 | > 80 | 81 | 82 | )} 83 | { 85 | accounts.authenticate.cleanup('all'); 86 | }} 87 | > 88 | 89 | 90 | 91 | ); 92 | }; 93 | 94 | SettingsForm.propTypes = {}; 95 | 96 | SettingsForm.defaultProps = {}; 97 | 98 | export default SettingsForm; 99 | -------------------------------------------------------------------------------- /app/sources/src/components/SignupForm/index.js: -------------------------------------------------------------------------------- 1 | import { observer, inject } from 'mobx-react'; 2 | import { withNamespaces } from 'react-i18next'; 3 | 4 | import view from './view'; 5 | 6 | export default withNamespaces()(inject('accounts')(observer(view))); 7 | -------------------------------------------------------------------------------- /app/sources/src/components/SignupForm/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | import { withLoader, Form, Button, Message, A, P } from 'components/common'; 12 | 13 | // ----------------------------------------------------------------------------- 14 | // Code 15 | // ----------------------------------------------------------------------------- 16 | 17 | export * from 'components/common'; 18 | 19 | export const Root = withLoader(styled(Form)` 20 | & ${A} { 21 | cursor: pointer; 22 | font-weight: 500; 23 | color: var(--colors__bg_accent); 24 | } 25 | 26 | & ${Button} { 27 | height: 2.7em; 28 | } 29 | & ${Message} { 30 | margin: 1em 0; 31 | } 32 | & > ${P} { 33 | margin-left: 0.3em; 34 | font-size: 0.7em; 35 | font-weight: 500; 36 | color: var(--colors__text_dark); 37 | margin-top: 1em; 38 | margin-bottom: 0.4em; 39 | white-space: pre-line; 40 | } 41 | & input { 42 | font-size: 0.9em; 43 | } 44 | & label { 45 | left: 1.4em; 46 | } 47 | `); 48 | 49 | export default Root; 50 | -------------------------------------------------------------------------------- /app/sources/src/components/SignupForm/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display name and button for name generation 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import { Trans } from 'react-i18next'; 13 | 14 | import logger from 'utils/logging'; 15 | 16 | import { Root, Input, Button, Message, A, P } from './styles'; 17 | 18 | const log = logger(); 19 | 20 | // ----------------------------------------------------------------------------- 21 | // Code 22 | // ----------------------------------------------------------------------------- 23 | 24 | export const SignupForm = ({ accounts, className, history, t, exists }) => ( 25 | { 28 | e.preventDefault(); 29 | const emailElement = e.target.querySelector('#signupEmail'); 30 | const email = emailElement && emailElement.value; 31 | const passwordElement = e.target.querySelector('#signupPassword'); 32 | const password = passwordElement && passwordElement.value; 33 | const referralElement = e.target.querySelector('#signupReferral'); 34 | const referral = referralElement && referralElement.value; 35 | accounts.signup.run(email, password, referral); 36 | }} 37 | loading={accounts.signup.loading} 38 | > 39 | 47 | 55 | {/*

Have you been invited by someone?

56 | */} 63 | 64 | {accounts.signup.error && ( 65 | 66 | 67 | {' '} 68 | { 70 | history.push('/login'); 71 | }} 72 | > 73 | {' '} 74 | 75 | 76 | 77 | )} 78 | 81 |
82 | ); 83 | 84 | SignupForm.propTypes = {}; 85 | 86 | SignupForm.defaultProps = {}; 87 | 88 | export default SignupForm; 89 | -------------------------------------------------------------------------------- /app/sources/src/components/common/BackButton.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Styled component for button 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import styled from 'styled-components'; 12 | 13 | import backIcon from 'assets/icons/arrow-left.svg'; 14 | 15 | // ----------------------------------------------------------------------------- 16 | // Code 17 | // ----------------------------------------------------------------------------- 18 | 19 | export const BackButton = styled(({ children, ...rest }) => ( 20 |
{ 22 | window.history.back(); 23 | }} 24 | {...rest} 25 | > 26 | {children} 27 |
28 | ))` 29 | font-size: 1em; 30 | cursor: pointer; 31 | background: no-repeat url(${backIcon}); 32 | background-size: contain; 33 | width: 1em; 34 | height: 1em; 35 | margin-left: 0.9em; 36 | margin-right: 1.4em; 37 | visibility: ${({ hidden }) => hidden && 'hidden'}; 38 | `; 39 | 40 | export default BackButton; 41 | -------------------------------------------------------------------------------- /app/sources/src/components/common/Button.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Styled component for button 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | // ----------------------------------------------------------------------------- 13 | // Code 14 | // ----------------------------------------------------------------------------- 15 | 16 | export const Button = styled.button` 17 | word-break: break-word; 18 | font-size: 1em; 19 | padding: ${({ link }) => (link ? '0' : '0.45em 1.3em')}; 20 | border-radius: 0.2em; 21 | color: ${({ primary }) => (primary ? 'var(--colors__text_accent)' : 'var(--colors__bg_accent)')}; 22 | background-color: ${({ primary }) => (primary ? 'var(--colors__bg_accent)' : null)}; 23 | border: ${({ link }) => (link ? 'none' : '0.1em solid var(--colors__bg_accent)')}; 24 | font-weight: 600; 25 | text-align: center; 26 | ${({ external, children }) => 27 | external && 28 | children && 29 | `:after { 30 | content: url('data:image/svg+xml;utf8,'); 31 | fill: red; 32 | margin: 0 0.3em; 33 | }`}; 34 | ${({ disabled }) => 35 | disabled && 36 | `pointer-events: none; 37 | opacity: 0.5; 38 | background-color: var(--colors__bg_dark); 39 | border-color: var(--colors__bg_dark);`} 40 | `; 41 | 42 | export default Button; 43 | -------------------------------------------------------------------------------- /app/sources/src/components/common/CopyButton.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Styled component for button 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import styled from 'styled-components'; 12 | 13 | import { Button as ButtonCommon } from './Button'; 14 | 15 | // ----------------------------------------------------------------------------- 16 | // Code 17 | // ----------------------------------------------------------------------------- 18 | 19 | export const CopyButton = styled( 20 | ({ copyData, copyText = 'Copy', copiedText = 'Copied!', children, inline, ...rest }) => ( 21 |
{ 23 | const copyContainer = document.createElement('input'); 24 | copyContainer.value = copyData; 25 | document.body.appendChild(copyContainer); 26 | copyContainer.select(); 27 | document.execCommand('Copy'); 28 | copyContainer.remove(); 29 | const button = e.currentTarget; 30 | button.setAttribute('data-afterText', copiedText); 31 | setTimeout(() => { 32 | button.setAttribute('data-afterText', copyText); 33 | }, 800); 34 | }} 35 | data-afterText={copyText} 36 | {...rest} 37 | > 38 | {children} 39 |
40 | ), 41 | )` 42 | position: relative; 43 | font-size: 1em; 44 | border-radius: 0.2em; 45 | font-weight: 600; 46 | text-align: center; 47 | display: flex; 48 | align-items: center; 49 | cursor: pointer; 50 | grid-template-columns: 1fr; 51 | &:after { 52 | content: attr(data-afterText); 53 | position: absolute; 54 | font-size: 0.8em; 55 | bottom: -1.5em; 56 | left: 50%; 57 | right: 50%; 58 | transform: translate(-50%); 59 | word-break: initial; 60 | width: max-content; 61 | transition: opacity 0.3s ease-in-out; 62 | } 63 | `; 64 | 65 | export default CopyButton; 66 | -------------------------------------------------------------------------------- /app/sources/src/components/common/Form.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Styled component for Form 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | // ----------------------------------------------------------------------------- 13 | // Code 14 | // ----------------------------------------------------------------------------- 15 | 16 | export const Form = styled.form` 17 | display: flex; 18 | flex-direction: column; 19 | `; 20 | 21 | export default Form; 22 | -------------------------------------------------------------------------------- /app/sources/src/components/common/Header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Styled component for Header 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | // ----------------------------------------------------------------------------- 13 | // Code 14 | // ----------------------------------------------------------------------------- 15 | 16 | export const Header = styled.header` 17 | font-weight: 700; 18 | display: flex; 19 | align-items: center; 20 | width: 100%; 21 | max-width: 100vw; 22 | `; 23 | 24 | export default Header; 25 | -------------------------------------------------------------------------------- /app/sources/src/components/common/Icon.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Styled component for Icon 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import styled from 'styled-components'; 12 | 13 | import { ReactComponent as Info } from 'assets/icons/information-circled.svg'; 14 | import { ReactComponent as Warn } from 'assets/icons/android-alert.svg'; 15 | import { ReactComponent as Error } from 'assets/icons/close-circled.svg'; 16 | 17 | // ----------------------------------------------------------------------------- 18 | // Code 19 | // ----------------------------------------------------------------------------- 20 | 21 | const icons = { 22 | info: , 23 | warn: , 24 | error: , 25 | }; 26 | 27 | const Container = styled.button` 28 | height: 1em; 29 | width: 1em; 30 | font-size: 1em; 31 | `; 32 | 33 | export const Icon = ({ 34 | i, 35 | className, 36 | ...rest 37 | } = {}) => ( 38 | 39 | {icons[i.toLowerCase()]} 40 | 41 | ); 42 | 43 | export default Icon; 44 | 45 | -------------------------------------------------------------------------------- /app/sources/src/components/common/Input.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Styled component for Input 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import styled from 'styled-components'; 12 | 13 | // ----------------------------------------------------------------------------- 14 | // Code 15 | // ----------------------------------------------------------------------------- 16 | 17 | const StyledInput = styled.input` 18 | width: 100%; 19 | padding: 1.15em; 20 | font-size: 1em; 21 | background-color: #fff; 22 | 23 | :not(:placeholder-shown) { 24 | padding-top: 1.7em; 25 | padding-bottom: 0.6em; 26 | } 27 | 28 | :invalid:not(:placeholder-shown) { 29 | background-color: var(--colors__bg_error); 30 | } 31 | `; 32 | 33 | const StyledLabel = styled.label` 34 | opacity: 0.4; 35 | position: absolute; 36 | top: 0.9em; 37 | left: 1.6em; 38 | font-size: 0.7em; 39 | font-weight: 600; 40 | 41 | ${StyledInput}:placeholder-shown + & { 42 | opacity: 0; 43 | } 44 | 45 | :before { 46 | content: attr(data-label-valid); 47 | } 48 | 49 | ${StyledInput}:invalid:not(:placeholder-shown) + & { 50 | color: var(--colors__text_error); 51 | } 52 | 53 | ${StyledInput}:invalid:not(:placeholder-shown) + &:before { 54 | content: attr(data-label-invalid); 55 | } 56 | `; 57 | 58 | const Container = styled.div` 59 | font-size: 1em; 60 | position: relative; 61 | border: 0.2rem solid var(--colors__bg); 62 | border-radius: 0.2em; 63 | overflow: hidden; 64 | `; 65 | 66 | export const Input = ({ 67 | className, 68 | id, 69 | labelValid, 70 | labelInvalid, 71 | ...rest 72 | } = {}) => ( 73 | 74 | 75 | 76 | 77 | ); 78 | 79 | export default Input; 80 | -------------------------------------------------------------------------------- /app/sources/src/components/common/Message.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Component to display childs with custom color and background 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | // ----------------------------------------------------------------------------- 13 | // Code 14 | // ----------------------------------------------------------------------------- 15 | 16 | export const Message = styled.div` 17 | word-break: break-word; 18 | white-space: pre-wrap; 19 | display: flex; 20 | flex-direction: column; 21 | width: 100%; 22 | justify-content: center; 23 | align-items: center; 24 | text-align: center; 25 | padding: 0.85em 1.3em; 26 | font-size: 0.8em; 27 | color: ${({ type }) => `var(--colors__text_${type})`}; 28 | background-color: ${({ type }) => `var(--colors__bg_${type})`}; 29 | `; 30 | 31 | export default Message; 32 | -------------------------------------------------------------------------------- /app/sources/src/components/common/Nav.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Styled component for Nav 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | // ----------------------------------------------------------------------------- 13 | // Code 14 | // ----------------------------------------------------------------------------- 15 | 16 | export const Nav = styled.div` 17 | display: flex; 18 | flex-direction: ${({ column }) => (column ? 'column' : 'row')}; 19 | 20 | & > * { 21 | padding: 0.5em 1em; 22 | cursor: pointer; 23 | } 24 | 25 | & > *{ 26 | text-decoration: none; 27 | } 28 | 29 | & > :hover, 30 | & > .active { 31 | color: var(--colors__text_accent); 32 | font-weight: 600; 33 | } 34 | `; 35 | 36 | export default Nav; 37 | -------------------------------------------------------------------------------- /app/sources/src/components/common/Select.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Styled component for Input 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import styled from 'styled-components'; 12 | 13 | // ----------------------------------------------------------------------------- 14 | // Code 15 | // ----------------------------------------------------------------------------- 16 | 17 | const StyledSelect = styled.select` 18 | width: 100%; 19 | padding: 1.15em; 20 | font-size: 1em; 21 | background-color: #fff; 22 | `; 23 | 24 | const Container = styled.div` 25 | font-size: 1em; 26 | position: relative; 27 | border: 0.2rem solid var(--colors__bg); 28 | border-radius: 0.2em; 29 | overflow: hidden; 30 | position: relative; 31 | 32 | &:after { 33 | content: ''; 34 | position: absolute; 35 | top: calc(50% - 0.125em); 36 | right: 1em; 37 | width: 0; 38 | height: 0; 39 | border-left: 0.5em solid transparent; 40 | border-right: 0.5em solid transparent; 41 | border-top: 0.5em solid var(--colors__bg_dark); 42 | clear: both; 43 | } 44 | `; 45 | 46 | export const Select = ({ className, ...rest } = {}) => ( 47 | 48 | 49 | 50 | ); 51 | 52 | export default Select; 53 | -------------------------------------------------------------------------------- /app/sources/src/components/common/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Various utils for easier styling and reducing boilerplate 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled, { css } from 'styled-components'; 11 | 12 | // ----------------------------------------------------------------------------- 13 | // Code 14 | // ----------------------------------------------------------------------------- 15 | 16 | export const withLoader = styledComponent => styled(styledComponent)` 17 | /* opacity: ${({ loading }) => (loading ? '0.3' : '1')}; */ 18 | filter: ${({ loading }) => loading && 'blur(2px)'}; 19 | pointer-events: ${({ loading }) => (loading ? 'none' : 'initial')}; 20 | :after { 21 | position:absolute; 22 | content: ''; 23 | z-index: 1000; 24 | background-color: rgba(255,255,255,0.8); 25 | width: 100%; 26 | height: 100%; 27 | top: 0; 28 | display: ${({ loading }) => (loading ? 'block' : 'none')}; 29 | } 30 | :before { 31 | z-index: 2000; 32 | content: ''; 33 | position: absolute; 34 | width: 2em; 35 | height: 2em; 36 | left: calc(50% - 1em); 37 | bottom: calc(50% - 1em); 38 | background-color: #333; 39 | border-radius: 100%; 40 | animation: sk-scaleout 1s infinite ease-in-out; 41 | display: ${({ loading }) => (loading ? 'block' : 'none')}; 42 | } 43 | 44 | @keyframes sk-scaleout { 45 | 0% { 46 | transform: scale(0); 47 | } 48 | 100% { 49 | transform: scale(1); 50 | opacity: 0; 51 | } 52 | } 53 | `; 54 | 55 | const mediaBreakpoints = { 56 | desktopXL: 1200, 57 | desktop: 992, 58 | tablet: 768, 59 | mobile: 576, 60 | }; 61 | 62 | // Iterate through the sizes and create a media template 63 | export const media = Object.keys(mediaBreakpoints).reduce((acc, label) => { 64 | acc[label] = (...args) => css` 65 | @media (max-width: ${mediaBreakpoints[label] / 16}em) { 66 | ${css(...args)}; 67 | } 68 | `; 69 | return acc; 70 | }, {}); 71 | -------------------------------------------------------------------------------- /app/sources/src/locales/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrypoint for all internationalization stuff 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import i18n from 'i18next'; 11 | 12 | import en from './en'; 13 | 14 | // ----------------------------------------------------------------------------- 15 | // Code 16 | // ----------------------------------------------------------------------------- 17 | 18 | i18n.init({ 19 | fallbackLng: 'en', 20 | debug: process.env.NODE_ENV === 'development', 21 | 22 | interpolation: { 23 | escapeValue: false, // not needed for react!! 24 | }, 25 | 26 | // react i18next special options (optional) 27 | react: { 28 | wait: false, 29 | bindI18n: 'languageChanged loaded', 30 | nsMode: 'default', 31 | }, 32 | resources: { 33 | en: { 34 | translation: en, 35 | }, 36 | }, 37 | }); 38 | 39 | export default i18n; 40 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Check/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Data fetching and final component export 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import { observer, inject } from 'mobx-react'; 12 | 13 | import logger from 'utils/logging'; 14 | 15 | import GA from 'utils/GA'; 16 | 17 | import view from './view'; 18 | 19 | const log = logger(); 20 | 21 | // ----------------------------------------------------------------------------- 22 | // Code 23 | // ----------------------------------------------------------------------------- 24 | 25 | class Wrapper extends React.Component { 26 | async componentDidMount() { 27 | const { wallets, info } = this.props; 28 | const query = window.location.hash.match(/\?(.*)/); 29 | 30 | info.get.run(); 31 | 32 | let wallet; 33 | if (query) { 34 | wallet = new URLSearchParams(query[0]).get('wallet'); 35 | try { 36 | wallet = JSON.parse(wallet); 37 | } catch (error) { 38 | log.error(error); 39 | wallet = undefined; 40 | } 41 | } 42 | if (wallet) { 43 | GA({ 44 | type: 'event', 45 | category: 'lnDomains', 46 | action: 'clickOnButton', 47 | label: wallet.origin || 'manual', 48 | }); 49 | wallets.getDetails.run(wallet.wuid, wallet.asset); 50 | } 51 | } 52 | 53 | componentWillUnmount() { 54 | const { wallets } = this.props; 55 | wallets.getDetails.cleanup('all'); 56 | clearInterval(this.polling); 57 | } 58 | 59 | render() { 60 | const query = window.location.hash.match(/\?(.*)/); 61 | let wallet; 62 | if (query) { 63 | wallet = new URLSearchParams(query[0]).get('wallet'); 64 | try { 65 | wallet = JSON.parse(wallet); 66 | } catch (error) { 67 | log.error(error); 68 | wallet = undefined; 69 | } 70 | } 71 | return React.createElement(observer(view), { ...this.props, wallet }); 72 | } 73 | } 74 | 75 | Wrapper.propTypes = {}; 76 | 77 | export default inject('payments', 'wallets', 'accounts', 'info')(observer(Wrapper)); 78 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Check/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Send scene styles 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | import CheckDestinationRaw from 'components/CheckDestination'; 13 | 14 | import { Header as HeaderRaw } from 'components/common'; 15 | 16 | // ----------------------------------------------------------------------------- 17 | // Code 18 | // ----------------------------------------------------------------------------- 19 | 20 | export * from 'components/common'; 21 | 22 | export const Header = styled(HeaderRaw)` 23 | background: var(--colors__bg); 24 | font: var(--fonts__header_thin); 25 | min-height: var(--sizing__header_heigh); 26 | border-bottom: 0.05em solid var(--colors__bg_dark); 27 | `; 28 | 29 | export const CheckDestination = styled(CheckDestinationRaw)` 30 | padding-top: 2em; 31 | `; 32 | 33 | export const Root = styled.div` 34 | height: 100%; 35 | display: flex; 36 | flex-direction: column; 37 | justify-content: center; 38 | `; 39 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Check/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Send scene 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | 12 | import logger from 'utils/logging'; 13 | 14 | import { Root, Header, BackButton, P, CheckDestination, Support } from './styles'; 15 | 16 | const log = logger(); 17 | 18 | // ----------------------------------------------------------------------------- 19 | // Code 20 | // ----------------------------------------------------------------------------- 21 | 22 | const Send = ({ wallets, wallet, accounts, info }) => { 23 | return ( 24 | 25 |
26 | 27 |

Pay

28 | 29 |
30 | 31 |
32 | ); 33 | }; 34 | 35 | export default Send; 36 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Confirm/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Data fetching and final component export 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import { observer, inject } from 'mobx-react'; 12 | 13 | import logger from 'utils/logging'; 14 | 15 | import GA from 'utils/GA'; 16 | 17 | import view from './view'; 18 | 19 | const log = logger(); 20 | 21 | // ----------------------------------------------------------------------------- 22 | // Code 23 | // ----------------------------------------------------------------------------- 24 | 25 | class Wrapper extends React.Component { 26 | async componentDidMount() { 27 | const { vendors, accounts, info } = this.props; 28 | const query = window.location.hash.match(/\?(.*)/); 29 | 30 | let payment; 31 | if (query) { 32 | payment = new URLSearchParams(query[0]).get('payment'); 33 | try { 34 | payment = JSON.parse(payment); 35 | } catch (error) { 36 | log.error(error); 37 | payment = undefined; 38 | } 39 | } 40 | if (!payment.origin) { 41 | try { 42 | const clipboardPayment = JSON.parse(localStorage.getItem('latestCopiedWuid')); 43 | if (clipboardPayment.wuid === payment.wuid) { 44 | payment.origin = clipboardPayment.origin; 45 | } 46 | } catch (e) { 47 | console.log(e); 48 | } 49 | GA({ 50 | type: 'event', 51 | category: 'payment', 52 | action: 'insertedManually', 53 | label: payment.origin || 'unknown', 54 | }); 55 | } else { 56 | GA({ 57 | type: 'event', 58 | category: 'payment', 59 | action: 'insertedAutomatically_button', 60 | label: payment.origin, 61 | }); 62 | } 63 | 64 | GA({ 65 | type: 'event', 66 | category: 'vuidDomainPair', 67 | action: `${payment.origin || 'unknown'}_${payment.vuid}`, 68 | }); 69 | 70 | vendors.get.run(payment.vuid, { origin: payment.origin }); 71 | accounts.get.run(); 72 | info.get.run(); 73 | 74 | this.polling = setInterval(() => { 75 | accounts.get.run(); 76 | info.get.run(); 77 | }, 5000); 78 | } 79 | 80 | componentWillUnmount() { 81 | const { vendors } = this.props; 82 | vendors.get.cleanup('all'); 83 | clearInterval(this.polling); 84 | } 85 | 86 | render() { 87 | const query = window.location.hash.match(/\?(.*)/); 88 | let payment; 89 | if (query) { 90 | payment = new URLSearchParams(query[0]).get('payment'); 91 | try { 92 | payment = JSON.parse(payment); 93 | } catch (error) { 94 | log.error(error); 95 | payment = undefined; 96 | } 97 | } 98 | if (!payment.origin) { 99 | try { 100 | const clipboardPayment = JSON.parse(localStorage.getItem('latestCopiedWuid')); 101 | if (clipboardPayment.wuid === payment.wuid) { 102 | payment.origin = clipboardPayment.origin; 103 | } 104 | } catch (e) { 105 | console.log(e); 106 | } 107 | } 108 | return React.createElement(observer(view), { ...this.props, payment }); 109 | } 110 | } 111 | 112 | Wrapper.propTypes = {}; 113 | 114 | export default inject('payments', 'vendors', 'accounts', 'info')(observer(Wrapper)); 115 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Confirm/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Send scene styles 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | import PaymentConfirmationRaw from 'components/PaymentConfirmation'; 13 | 14 | import { Header as HeaderRaw } from 'components/common'; 15 | 16 | // ----------------------------------------------------------------------------- 17 | // Code 18 | // ----------------------------------------------------------------------------- 19 | 20 | export * from 'components/common'; 21 | 22 | export const Header = styled(HeaderRaw)` 23 | background: var(--colors__bg); 24 | font: var(--fonts__header_thin); 25 | min-height: var(--sizing__header_heigh); 26 | border-bottom: 0.05em solid var(--colors__bg_dark); 27 | `; 28 | 29 | export const PaymentConfirmation = styled(PaymentConfirmationRaw)``; 30 | 31 | export const Root = styled.div` 32 | display: flex; 33 | flex-direction: column; 34 | justify-content: flex-start; 35 | height: 100%; 36 | background: var(--colors__bg_bright); 37 | `; 38 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Confirm/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Send scene 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | 12 | import logger from 'utils/logging'; 13 | 14 | import { Root, Header, BackButton, P, PaymentConfirmation, Support } from './styles'; 15 | 16 | const log = logger(); 17 | 18 | // ----------------------------------------------------------------------------- 19 | // Code 20 | // ----------------------------------------------------------------------------- 21 | 22 | const Confirm = ({ payments, vendors, payment, accounts, info }) => { 23 | return ( 24 | 25 |
26 | 27 |

Pay

28 | 29 |
30 | {payment ? ( 31 | 38 | ) : ( 39 |

No payment provided to confirm

40 | )} 41 |
42 | ); 43 | }; 44 | 45 | export default Confirm; 46 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Landing/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Data fetching and final component export 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import view from './view'; 11 | 12 | // ----------------------------------------------------------------------------- 13 | // Code 14 | // ----------------------------------------------------------------------------- 15 | 16 | export default view; 17 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Landing/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Landing scene styles 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | import SignupFormCommon from 'components/SignupForm'; 13 | 14 | import { Div, media, Span } from 'components/common'; 15 | 16 | import LogoFull from 'assets/img/logo/full.png'; 17 | import LogoTextOnly from 'assets/img/logo/textOnly.png'; 18 | 19 | // ----------------------------------------------------------------------------- 20 | // Code 21 | // ----------------------------------------------------------------------------- 22 | 23 | export * from 'components/common'; 24 | 25 | export const Logo = styled.img.attrs({ 26 | src: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==', 27 | })` 28 | position: relative; 29 | content: url(${LogoFull}); 30 | height: 7.5em; 31 | ${media.desktop` 32 | content: url(${LogoTextOnly}); 33 | `}; 34 | `; 35 | 36 | export const Uvp = styled.div` 37 | padding-left: 1em; 38 | grid-area: left; 39 | align-items: flex-end; 40 | font-family: 'Open Sans', sans-serif; 41 | text-align: right; 42 | `; 43 | 44 | export const UvpTitle = styled.div` 45 | margin-top: -0.5em; 46 | display: flex; 47 | flex-direction: column; 48 | & > ${Span} { 49 | word-break: initial; 50 | } 51 | font-size: 2.7em; 52 | `; 53 | 54 | export const UvpDesc = styled.div` 55 | display: flex; 56 | flex-direction: column; 57 | margin-top: 1em; 58 | font-size: 1em; 59 | font-weight: 500; 60 | word-break: initial; 61 | `; 62 | 63 | export const SignupForm = styled(SignupFormCommon)``; 64 | 65 | export const Disclaimer = styled.div` 66 | position: relative; 67 | font-size: 0.8em; 68 | display: flex; 69 | flex-direction: column; 70 | font-weight: 600; 71 | color: var(--colors__text_error); 72 | & ${Span} { 73 | margin-bottom: 0.3em; 74 | } 75 | /* :before { 76 | content: '*'; 77 | position: absolute; 78 | } */ 79 | `; 80 | 81 | export const Root = styled.div` 82 | background-color: var(--colors__bg); 83 | display: grid; 84 | grid-template-areas: 85 | 'left right' 86 | 'left right'; 87 | grid-template-rows: 1fr 1fr; 88 | grid-template-columns: 1fr 1fr; 89 | gap: 0 4em; 90 | height: 100%; 91 | 92 | & > ${Div}, & > ${Uvp} { 93 | display: flex; 94 | width: 100%; 95 | flex-direction: column; 96 | justify-content: center; 97 | } 98 | 99 | & > ${Div}:last-child { 100 | grid-area: right; 101 | align-items: flex-start; 102 | } 103 | 104 | & ${SignupForm} { 105 | width: 90%; 106 | max-width: 400px; 107 | min-width: 400px; 108 | } 109 | 110 | ${media.desktop` 111 | & > ${Uvp}, 112 | & > ${Div}:last-child { 113 | align-items: center; 114 | } 115 | grid-template-areas: "left left" 116 | "right right"; 117 | grid-template-rows: 1fr 1fr; 118 | grid-template-columns: 1fr 1fr; 119 | `}; 120 | `; 121 | 122 | export default Root; 123 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Landing/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Public landing scene 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | 12 | import { Root, Div, Uvp, SignupForm, Logo, Span, UvpDesc, UvpTitle, Disclaimer } from './styles'; 13 | 14 | // ----------------------------------------------------------------------------- 15 | // Code 16 | // ----------------------------------------------------------------------------- 17 | 18 | const Landing = () => ( 19 | 20 | 21 | 22 | 23 | Lightning Network 24 | web wallet 25 | 26 | 27 | Fast, easy, no node install and synchronisation, 28 | no need to lock funds in channels and rebalance 29 | 30 | 31 |
32 | 33 | 34 | This is beta product, do not put a lot of money in it yet ;) 35 | If you will be too #reckless you can loose your funds 36 | 37 |
38 |
39 | ); 40 | 41 | export default Landing; 42 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Login/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Data fetching and final component export 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import { withRouter } from 'react-router'; 11 | 12 | import view from './view'; 13 | 14 | // ----------------------------------------------------------------------------- 15 | // Code 16 | // ----------------------------------------------------------------------------- 17 | 18 | export default withRouter(view); 19 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Login/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Login scene styles 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | import LoginFormCommon from 'components/LoginForm'; 13 | 14 | import LogoFull from 'assets/img/logo/full.png'; 15 | 16 | import { A, Span } from 'components/common'; 17 | 18 | // ----------------------------------------------------------------------------- 19 | // Code 20 | // ----------------------------------------------------------------------------- 21 | 22 | export * from 'components/common'; 23 | 24 | export const Root = styled.div` 25 | background-color: var(--colors__bg); 26 | display: flex; 27 | flex-direction: column; 28 | justify-content: center; 29 | align-items: center; 30 | height: 100%; 31 | & ${A} ${Span}:last-child { 32 | font: var(--fonts__text_bold); 33 | } 34 | & > ${A} { 35 | margin-top: 3.5em; 36 | display: flex; 37 | flex-direction: column; 38 | justify-content: center; 39 | align-items: center; 40 | color: var(--colors__bg_accent); 41 | font-size: 0.8em; 42 | } 43 | & > ${Span} { 44 | margin-top: 1.5em; 45 | font-size: 0.8em; 46 | color: var(--colors__bg_accent); 47 | cursor: pointer; 48 | } 49 | `; 50 | 51 | export const Logo = styled.img.attrs({ 52 | src: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==', 53 | })` 54 | position: relative; 55 | content: url(${LogoFull}); 56 | height: 3.5em; 57 | margin-bottom: 2em; 58 | `; 59 | 60 | export const LoginForm = styled(LoginFormCommon)` 61 | width: 100%; 62 | width: calc(100% - 2em); 63 | margin: 0 1em; 64 | & > div:last-of-type input { 65 | margin-bottom: 1.5em; 66 | } 67 | `; 68 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Login/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Authentication scene 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | 12 | import { Root, LoginForm, Logo, A, Span, Button } from './styles'; 13 | 14 | // ----------------------------------------------------------------------------- 15 | // Code 16 | // ----------------------------------------------------------------------------- 17 | 18 | const Login = ({ history }) => ( 19 | 20 | 21 | 22 | {process.env.NODE_ENV === 'development' ? ( 23 | 36 | ) : null} 37 | { 39 | history.push(`/signup`); 40 | }} 41 | > 42 | Do not have an account yet? 43 | Register here! 44 | 45 | { 47 | history.push(`/restore`); 48 | }} 49 | > 50 | Forgot your password? 51 | 52 | Have any other problems? Contact us! 53 | 54 | ); 55 | 56 | export default Login; 57 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Payment/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Data fetching and final component export 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import { observer, inject, PropTypes } from 'mobx-react'; 12 | import { withNamespaces } from 'react-i18next'; 13 | import { withRouter } from 'react-router'; 14 | 15 | import view from './view'; 16 | 17 | // ----------------------------------------------------------------------------- 18 | // Code 19 | // ----------------------------------------------------------------------------- 20 | 21 | class Wrapper extends React.Component { 22 | componentDidMount() { 23 | const { 24 | payments, 25 | match: { 26 | params: { puid }, 27 | }, 28 | } = this.props; 29 | 30 | payments.getById.run({ puid }); 31 | } 32 | 33 | componentWillUnmount() { 34 | const { payments } = this.props; 35 | payments.getById.cleanup('all'); 36 | } 37 | 38 | render() { 39 | return React.createElement(observer(view), this.props); 40 | } 41 | } 42 | 43 | Wrapper.propTypes = {}; 44 | 45 | export default withNamespaces()(inject('payments')(withRouter(Wrapper))); 46 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Payment/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Payments scene styles 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | import { P, Header as HeaderRaw } from 'components/common'; 13 | 14 | import { ReactComponent as ErrorIconRaw } from 'assets/icons/x-circle.svg'; 15 | import { ReactComponent as EmptyIconRaw } from 'assets/icons/paper.svg'; 16 | 17 | import PaymentDetailsRaw from 'components/PaymentDetails'; 18 | 19 | // ----------------------------------------------------------------------------- 20 | // Code 21 | // ----------------------------------------------------------------------------- 22 | 23 | export * from 'components/common'; 24 | 25 | export const Header = styled(HeaderRaw)` 26 | min-height: var(--sizing__header_heigh); 27 | font: var(--fonts__header_thin); 28 | position: fixed; 29 | top: 0; 30 | z-index: 1; 31 | background-color: var(--colors__bg); 32 | `; 33 | 34 | export const PaymentDetails = styled(PaymentDetailsRaw)` 35 | margin-top: var(--sizing__header_heigh); 36 | `; 37 | 38 | export const EmptyIcon = styled(EmptyIconRaw)` 39 | margin-bottom: 3rem; 40 | height: 15rem; 41 | width: 15rem; 42 | opacity: 0.3; 43 | `; 44 | export const ErrorIcon = styled(ErrorIconRaw)` 45 | margin-bottom: 3rem; 46 | height: 15rem; 47 | width: 15rem; 48 | opacity: 0.3; 49 | `; 50 | 51 | export const EmptyWrapper = styled.div` 52 | display: flex; 53 | flex-direction: column; 54 | align-items: center; 55 | justify-content: center; 56 | opacity: 0.6; 57 | font-size: 2em; 58 | text-align: center; 59 | margin-top: 2em; 60 | margin-top: var(--sizing__header_heigh); 61 | & svg { 62 | stroke: #000; 63 | } 64 | `; 65 | 66 | export const Root = styled.div` 67 | display: flex; 68 | flex-direction: column; 69 | justify-content: center; 70 | `; 71 | 72 | export default Root; 73 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Payment/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Payments scene 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import formatDate from 'date-fns/format'; 12 | import isSameDay from 'date-fns/is_same_day'; 13 | import isToday from 'date-fns/is_today'; 14 | import isYesteray from 'date-fns/is_yesterday'; 15 | 16 | import { 17 | Root, 18 | EmptyIcon, 19 | ErrorIcon, 20 | EmptyWrapper, 21 | P, 22 | Header, 23 | Button, 24 | BalanceSummary, 25 | PaymentsGroup, 26 | Separator, 27 | BackButton, 28 | Support, 29 | PaymentDetails, 30 | } from './styles'; 31 | 32 | // ----------------------------------------------------------------------------- 33 | // Code 34 | // ----------------------------------------------------------------------------- 35 | 36 | const getSeparatorText = date => { 37 | let distance; 38 | distance = formatDate(new Date(date), 'Do MMMM'); 39 | if (isToday(date)) { 40 | distance = 'Today'; 41 | } 42 | if (isYesteray(date)) { 43 | distance = 'Yesterday'; 44 | } 45 | return distance; 46 | }; 47 | 48 | // eslint-disable-next-line 49 | const Payment = ({ history, payments, location: { search }, t }) => { 50 | const backButtonHidden = new URLSearchParams(search).get('nopopup') === 'true'; 51 | 52 | if (payments.getById.error || (!payments.getById.data && !payments.getById.loading)) { 53 | return ( 54 | 55 |
56 |
60 | 61 | 62 |

Unable to load details :(

63 |

Try again later

64 |
65 |
66 | ); 67 | } 68 | 69 | if (payments.getById.loading && !payments.getById.data) { 70 | return ( 71 | 72 |
73 |
77 | 78 | 79 |

Loading details

80 |
81 |
82 | ); 83 | } 84 | 85 | if (!payments.getById.data[0]) { 86 | return ( 87 | 88 |
89 |
93 | 94 | 95 |

Payment not found

96 |
97 |
98 | ); 99 | } 100 | 101 | return ( 102 | 103 |
104 |
108 | 109 |
110 | ); 111 | }; 112 | 113 | export default Payment; 114 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Payments/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Data fetching and final component export 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React, { useEffect } from 'react'; 11 | import { observer, inject, PropTypes } from 'mobx-react'; 12 | import { withNamespaces } from 'react-i18next'; 13 | 14 | import view from './view'; 15 | 16 | // ----------------------------------------------------------------------------- 17 | // Code 18 | // ----------------------------------------------------------------------------- 19 | 20 | const Wrapper = props => { 21 | const { payments, accounts, ui, info } = props; 22 | 23 | useEffect(() => { 24 | accounts.get.run(); 25 | payments.get.run(); 26 | ui.getLiveChat.run(); 27 | info.get.run(); 28 | info.getSkippedAnnouncements.run(); 29 | 30 | const polling = setInterval(() => { 31 | payments.get.run(); 32 | accounts.get.run(); 33 | info.get.run(); 34 | }, 3000); 35 | 36 | return () => { 37 | clearInterval(polling); 38 | }; 39 | }); 40 | 41 | return React.createElement(observer(view), props); 42 | }; 43 | 44 | Wrapper.propTypes = { 45 | // payments: PropTypes.observableObject.isRequired, 46 | }; 47 | 48 | export default withNamespaces()(inject('payments', 'accounts', 'settings', 'info', 'ui')(Wrapper)); 49 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Receive/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Data fetching and final component export 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React, { useEffect } from 'react'; 11 | import { observer, inject } from 'mobx-react'; 12 | 13 | import logger from 'utils/logging'; 14 | 15 | import view from './view'; 16 | 17 | const log = logger(); 18 | 19 | // ----------------------------------------------------------------------------- 20 | // Code 21 | // ----------------------------------------------------------------------------- 22 | 23 | const Wrapper = props => { 24 | const { payments, denominations } = props; 25 | 26 | useEffect(() => { 27 | const query = window.location.hash.match(/\?(.*)/); 28 | 29 | let receive; 30 | if (query) { 31 | receive = new URLSearchParams(query[0]).get('receive'); 32 | try { 33 | receive = JSON.parse(receive); 34 | } catch (error) { 35 | log.error(error); 36 | receive = undefined; 37 | } 38 | } 39 | if (receive.type.match('blockchain')) { 40 | payments.receive.run(receive.type, null, receive.asset); 41 | } 42 | 43 | denominations.get.run(); 44 | 45 | const polling = setInterval(() => { 46 | payments.get.run(); 47 | }, 3000); 48 | 49 | return () => { 50 | clearInterval(polling); 51 | payments.receive.cleanup('all'); 52 | }; 53 | }); 54 | 55 | const query = window.location.hash.match(/\?(.*)/); 56 | 57 | let receive; 58 | if (query) { 59 | receive = new URLSearchParams(query[0]).get('receive'); 60 | try { 61 | receive = JSON.parse(receive); 62 | } catch (error) { 63 | log.error(error); 64 | receive = undefined; 65 | } 66 | } 67 | 68 | return React.createElement(observer(view), { ...props, receive, nopopup: new URLSearchParams(query[0]).get('nopopup') }); 69 | }; 70 | 71 | Wrapper.propTypes = {}; 72 | 73 | export default inject('payments', 'denominations')(Wrapper); 74 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Receive/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Receive scene styles 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | import ReceivePaymentCommon from 'components/ReceivePayment'; 13 | 14 | import { Header as HeaderRaw } from 'components/common'; 15 | 16 | // ----------------------------------------------------------------------------- 17 | // Code 18 | // ----------------------------------------------------------------------------- 19 | 20 | export * from 'components/common'; 21 | 22 | export const ReceivePayment = styled(ReceivePaymentCommon)``; 23 | 24 | export const Header = styled(HeaderRaw)` 25 | background: var(--colors__bg); 26 | font: var(--fonts__header_thin); 27 | min-height: var(--sizing__header_heigh); 28 | border-bottom: 0.05em solid var(--colors__bg_dark); 29 | `; 30 | 31 | export const Root = styled.div` 32 | display: flex; 33 | height: 100%; 34 | flex-direction: column; 35 | justify-content: center; 36 | `; 37 | 38 | export default Root; 39 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Receive/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Receive scene 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | 12 | import { Root, Header, ReceivePayment, P, BackButton, Support } from './styles'; 13 | 14 | // ----------------------------------------------------------------------------- 15 | // Code 16 | // ----------------------------------------------------------------------------- 17 | 18 | const Receive = ({ payments, receive, denominations, nopopup }) => ( 19 | 20 |
21 |
25 | 31 |
32 | ); 33 | 34 | export default Receive; 35 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Restore/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Data fetching and final component export 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import { observer, inject, PropTypes } from 'mobx-react'; 12 | import { withNamespaces } from 'react-i18next'; 13 | import { withRouter } from 'react-router'; 14 | 15 | import view from './view'; 16 | 17 | // ----------------------------------------------------------------------------- 18 | // Code 19 | // ----------------------------------------------------------------------------- 20 | 21 | class Wrapper extends React.Component { 22 | componentWillUnmount() { 23 | const { accounts } = this.props; 24 | accounts.sendRestoreLink.cleanup('all'); 25 | } 26 | 27 | render() { 28 | return React.createElement(observer(view), this.props); 29 | } 30 | } 31 | 32 | Wrapper.propTypes = {}; 33 | 34 | export default withNamespaces()(inject('accounts')(withRouter(Wrapper))); 35 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Restore/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Auth scene styles 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | import RestoreFormCommon from 'components/RestoreForm'; 13 | 14 | import LogoFull from 'assets/img/logo/full.png'; 15 | 16 | import { A, Span, Header as HeaderRaw } from 'components/common'; 17 | 18 | // ----------------------------------------------------------------------------- 19 | // Code 20 | // ----------------------------------------------------------------------------- 21 | 22 | export * from 'components/common'; 23 | 24 | export const Header = styled(HeaderRaw)` 25 | background: var(--colors__bg); 26 | font: var(--fonts__header_thin); 27 | min-height: var(--sizing__header_heigh); 28 | border-bottom: 0.05em solid var(--colors__bg_dark); 29 | `; 30 | 31 | export const Root = styled.div` 32 | background-color: var(--colors__bg); 33 | display: flex; 34 | flex-direction: column; 35 | justify-content: center; 36 | align-items: center; 37 | height: 100%; 38 | & ${A} ${Span}:last-child { 39 | font: var(--fonts__text_bold); 40 | } 41 | & > ${A} { 42 | margin-top: 3.5em; 43 | display: flex; 44 | flex-direction: column; 45 | justify-content: center; 46 | align-items: center; 47 | color: var(--colors__bg_accent); 48 | font-size: 0.8em; 49 | } 50 | & > ${Span} { 51 | margin-top: 1.5em; 52 | font-size: 0.8em; 53 | color: var(--colors__bg_accent); 54 | cursor: pointer; 55 | margin-bottom: auto; 56 | } 57 | `; 58 | 59 | export const Logo = styled.img.attrs({ 60 | src: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==', 61 | })` 62 | position: relative; 63 | content: url(${LogoFull}); 64 | height: 3.5em; 65 | margin-bottom: 2em; 66 | `; 67 | 68 | export const RestoreForm = styled(RestoreFormCommon)` 69 | width: calc(100% - 2em); 70 | margin: 0 1em; 71 | margin-top: 1em; 72 | & > div:last-of-type input { 73 | margin-bottom: 1.5em; 74 | } 75 | `; 76 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Restore/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Authentication scene 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | 12 | import { Root, Logo, RestoreForm, A, Span, Header, P, BackButton } from './styles'; 13 | 14 | // ----------------------------------------------------------------------------- 15 | // Code 16 | // ----------------------------------------------------------------------------- 17 | 18 | const Restore = ({ history }) => ( 19 | 20 |
21 | 22 |

Reset password

23 |
24 | 25 | Have any problems? Contact us! 26 |
27 | ); 28 | 29 | export default Restore; 30 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Select/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Data fetching and final component export 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import { observer, inject } from 'mobx-react'; 12 | import { withNamespaces } from 'react-i18next'; 13 | 14 | import view from './view'; 15 | 16 | // ----------------------------------------------------------------------------- 17 | // Code 18 | // ----------------------------------------------------------------------------- 19 | 20 | class Wrapper extends React.Component { 21 | async componentDidMount() { 22 | const { payments, info } = this.props; 23 | info.get.run(); 24 | payments.receive.run('blockchain', null, 'BTC'); 25 | } 26 | 27 | componentWillUnmount() { 28 | const { payments } = this.props; 29 | payments.receive.cleanup('all'); 30 | } 31 | 32 | render() { 33 | return React.createElement(observer(view), { ...this.props }); 34 | } 35 | } 36 | 37 | Wrapper.propTypes = {}; 38 | 39 | export default withNamespaces()(inject('payments', 'info')(observer(Wrapper))); 40 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Select/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Receive scene styles 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | import SelectReceiveAssetRaw from 'components/SelectReceiveAsset'; 13 | 14 | import { 15 | Header as HeaderRaw, 16 | Message as MessageRaw, 17 | withLoader, 18 | Span, 19 | Tip, 20 | } from 'components/common'; 21 | 22 | // ----------------------------------------------------------------------------- 23 | // Code 24 | // ----------------------------------------------------------------------------- 25 | 26 | export * from 'components/common'; 27 | 28 | export const SelectReceiveAsset = styled(SelectReceiveAssetRaw)` 29 | height: 100%; 30 | ${({ disabled }) => 31 | disabled && 32 | ` 33 | opacity: 0.5; 34 | pointer-events: none; 35 | `} 36 | `; 37 | 38 | export const Header = styled(HeaderRaw)` 39 | background: var(--colors__bg); 40 | font: var(--fonts__header_thin); 41 | min-height: var(--sizing__header_heigh); 42 | border-bottom: 0.05em solid var(--colors__bg_dark); 43 | `; 44 | 45 | export const Maintenance = styled(MessageRaw)` 46 | position: relative; 47 | display: flex; 48 | flex-direction: column; 49 | justify-content: center; 50 | align-items: center; 51 | white-space: pre-wrap; 52 | text-align: center; 53 | margin-bottom: -3.5em; 54 | background: var(--colors__bg_warn); 55 | font-weight: 400; 56 | z-index: 11; 57 | & * { 58 | word-break: break-word; 59 | } 60 | & > ${Span} { 61 | font-weight: 600; 62 | font-size: 1.1em; 63 | padding: 0 !important; 64 | } 65 | & > ${Tip} { 66 | margin-left: 0.5em; 67 | } 68 | & > ${Tip}:before { 69 | background: none; 70 | border: 0.1em solid black; 71 | } 72 | `; 73 | 74 | export const Root = styled.div` 75 | display: flex; 76 | height: 100%; 77 | flex-direction: column; 78 | justify-content: center; 79 | `; 80 | 81 | export default withLoader(Root); 82 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Select/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Receive scene 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | 12 | import { Root, Header, SelectReceiveAsset, P, BackButton, Support, Message, Span, Maintenance } from './styles'; 13 | 14 | // ----------------------------------------------------------------------------- 15 | // Code 16 | // ----------------------------------------------------------------------------- 17 | 18 | const Select = ({ payments, info, t }) => ( 19 | 20 |
21 | 22 |

Receive

23 | 24 |
25 | {payments.receive.error && !(info.get.data && info.get.data.status && info.get.data.status.type === 'maintenance' && info.get.data.status.message) ? ( 26 | 27 | {t([`errors.${payments.receive.error.code}`, 'errors.default'], { 28 | ...payments.receive.error, 29 | })} 30 | 31 | ) : null} 32 | {info.get.data && info.get.data.status && info.get.data.status.type === 'maintenance' && info.get.data.status.message ? ( 33 | 34 | {info.get.data.status.message.split('\n')[0]} 35 | {info.get.data.status.message 36 | .split('\n') 37 | .slice(1) 38 | .join('\n')} 39 | 40 | ) : null} 41 | 44 |
45 | ); 46 | 47 | export default Select; 48 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Settings/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Data fetching and final component export 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import { observer, inject, PropTypes } from 'mobx-react'; 12 | import { withNamespaces } from 'react-i18next'; 13 | import { withRouter } from 'react-router'; 14 | 15 | import view from './view'; 16 | 17 | // ----------------------------------------------------------------------------- 18 | // Code 19 | // ----------------------------------------------------------------------------- 20 | 21 | class Wrapper extends React.Component { 22 | componentDidMount() { 23 | const { settings, denominations } = this.props; 24 | if (settings.get.data) { 25 | settings.get.run(); 26 | } 27 | if (denominations.get.data) { 28 | denominations.get.run(); 29 | } 30 | } 31 | 32 | render() { 33 | return React.createElement(observer(view), this.props); 34 | } 35 | } 36 | 37 | Wrapper.propTypes = {}; 38 | 39 | export default withNamespaces()(inject('settings', 'denominations', 'accounts')(withRouter(Wrapper))); 40 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Settings/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Payments scene styles 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | import { P, Header as HeaderRaw } from 'components/common'; 13 | 14 | import { ReactComponent as ErrorIconRaw } from 'assets/icons/x-circle.svg'; 15 | import { ReactComponent as EmptyIconRaw } from 'assets/icons/paper.svg'; 16 | 17 | import SettingsFormRaw from 'components/SettingsForm'; 18 | 19 | // ----------------------------------------------------------------------------- 20 | // Code 21 | // ----------------------------------------------------------------------------- 22 | 23 | export * from 'components/common'; 24 | 25 | export const Header = styled(HeaderRaw)` 26 | min-height: var(--sizing__header_heigh); 27 | font: var(--fonts__header_thin); 28 | position: fixed; 29 | top: 0; 30 | z-index: 1; 31 | background-color: var(--colors__bg); 32 | `; 33 | 34 | export const SettingsForm = styled(SettingsFormRaw)` 35 | margin-top: var(--sizing__header_heigh); 36 | `; 37 | 38 | export const EmptyIcon = styled(EmptyIconRaw)` 39 | margin-bottom: 3rem; 40 | height: 15rem; 41 | width: 15rem; 42 | opacity: 0.3; 43 | `; 44 | export const ErrorIcon = styled(ErrorIconRaw)` 45 | margin-bottom: 3rem; 46 | height: 15rem; 47 | width: 15rem; 48 | opacity: 0.3; 49 | `; 50 | 51 | export const EmptyWrapper = styled.div` 52 | display: flex; 53 | flex-direction: column; 54 | align-items: center; 55 | justify-content: center; 56 | opacity: 0.6; 57 | font-size: 2em; 58 | text-align: center; 59 | margin-top: 2em; 60 | margin-top: var(--sizing__header_heigh); 61 | & svg { 62 | stroke: #000; 63 | } 64 | `; 65 | 66 | export const Root = styled.div` 67 | display: flex; 68 | flex-direction: column; 69 | justify-content: center; 70 | `; 71 | 72 | export default Root; 73 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Settings/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Payments scene 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | import formatDate from 'date-fns/format'; 12 | import isSameDay from 'date-fns/is_same_day'; 13 | import isToday from 'date-fns/is_today'; 14 | import isYesteray from 'date-fns/is_yesterday'; 15 | 16 | import { 17 | Root, 18 | EmptyIcon, 19 | ErrorIcon, 20 | EmptyWrapper, 21 | P, 22 | Header, 23 | SettingsForm, 24 | BackButton, 25 | Support, 26 | } from './styles'; 27 | 28 | // ----------------------------------------------------------------------------- 29 | // Code 30 | // ----------------------------------------------------------------------------- 31 | 32 | // eslint-disable-next-line 33 | const SettingsScene = ({ history, settings, accounts, denominations, t }) => { 34 | if (settings.get.error || (!settings.get.data && !settings.get.loading)) { 35 | return ( 36 | 37 |
38 | 39 |

Settings

40 | 41 |
42 | 43 | 44 |

Unable to load settings :(

45 |

Try again later

46 |
47 |
48 | ); 49 | } 50 | 51 | if (settings.get.loading && !settings.get.data) { 52 | return ( 53 | 54 |
55 | 56 |

Settings

57 | 58 |
59 | 60 | 61 |

Loading settings

62 |
63 |
64 | ); 65 | } 66 | 67 | return ( 68 | 69 |
70 | 71 |

Settings

72 | 73 |
74 | 75 |
76 | ); 77 | }; 78 | 79 | export default SettingsScene; 80 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Signup/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Data fetching and final component export 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import { withRouter } from 'react-router'; 11 | 12 | import view from './view'; 13 | 14 | // ----------------------------------------------------------------------------- 15 | // Code 16 | // ----------------------------------------------------------------------------- 17 | 18 | export default withRouter(view); 19 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Signup/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Auth scene styles 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import styled from 'styled-components'; 11 | 12 | import SignupFormCommon from 'components/SignupForm'; 13 | 14 | import LogoFull from 'assets/img/logo/full.png'; 15 | 16 | import { A, Span, Header as HeaderRaw } from 'components/common'; 17 | 18 | // ----------------------------------------------------------------------------- 19 | // Code 20 | // ----------------------------------------------------------------------------- 21 | 22 | export * from 'components/common'; 23 | 24 | export const Header = styled(HeaderRaw)` 25 | background: var(--colors__bg); 26 | font: var(--fonts__header_thin); 27 | min-height: var(--sizing__header_heigh); 28 | border-bottom: 0.05em solid var(--colors__bg_dark); 29 | `; 30 | 31 | export const Root = styled.div` 32 | background-color: var(--colors__bg); 33 | display: flex; 34 | flex-direction: column; 35 | justify-content: center; 36 | align-items: center; 37 | height: 100%; 38 | & ${A} ${Span}:last-child { 39 | font: var(--fonts__text_bold); 40 | } 41 | & > ${A} { 42 | margin-top: 3.5em; 43 | display: flex; 44 | flex-direction: column; 45 | justify-content: center; 46 | align-items: center; 47 | color: var(--colors__bg_accent); 48 | font-size: 0.8em; 49 | } 50 | & > ${Span} { 51 | margin-top: 1.5em; 52 | font-size: 0.8em; 53 | color: var(--colors__bg_accent); 54 | cursor: pointer; 55 | margin-bottom: auto; 56 | } 57 | `; 58 | 59 | export const Logo = styled.img.attrs({ 60 | src: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==', 61 | })` 62 | position: relative; 63 | content: url(${LogoFull}); 64 | height: 3.5em; 65 | margin-bottom: 2em; 66 | `; 67 | 68 | export const SignupForm = styled(SignupFormCommon)` 69 | width: calc(100% - 2em); 70 | margin: 0 1em; 71 | margin-top: 1em; 72 | & > div:last-of-type input { 73 | margin-bottom: 1.5em; 74 | } 75 | `; 76 | -------------------------------------------------------------------------------- /app/sources/src/scenes/Signup/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Authentication scene 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import React from 'react'; 11 | 12 | import { Root, Logo, SignupForm, A, Span, Header, P, BackButton } from './styles'; 13 | 14 | // ----------------------------------------------------------------------------- 15 | // Code 16 | // ----------------------------------------------------------------------------- 17 | 18 | const Signup = ({ history }) => ( 19 | 20 |
21 | 22 |

Sign up

23 |
24 | 25 | { 27 | history.push(`/login`); 28 | }} 29 | > 30 | Already have account? 31 | Login instead 32 | 33 | Have any problems? Contact us! 34 |
35 | ); 36 | 37 | export default Signup; 38 | -------------------------------------------------------------------------------- /app/sources/src/setupProxy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Proxy setup for creat ereact app webpack dev server 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | const proxy = require('http-proxy-middleware'); 11 | 12 | // ----------------------------------------------------------------------------- 13 | // Code 14 | // ----------------------------------------------------------------------------- 15 | 16 | module.exports = app => { 17 | app.use( 18 | proxy('/api', { 19 | target: 'http://host.docker.internal:3004', 20 | pathRewrite: { 21 | '^/api': '', 22 | }, 23 | }), 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /app/sources/src/stores/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrypoint of all app stores 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import accounts from './accounts'; 11 | import payments from './payments'; 12 | import wallets from './wallets'; 13 | import vendors from './vendors'; 14 | import info from './info'; 15 | import settings from './settings'; 16 | import denominations from './denominations'; 17 | import ui from './ui'; 18 | import * as dataGeneric from './dataGeneric'; 19 | 20 | // ----------------------------------------------------------------------------- 21 | // Code 22 | // ----------------------------------------------------------------------------- 23 | 24 | const init = async () => { 25 | await settings.get.init(); 26 | await accounts.authenticate.run(); 27 | if (accounts.authenticate.error) { 28 | await accounts.authenticate.cleanup('all'); 29 | } 30 | }; 31 | 32 | export { accounts, payments, wallets, settings, vendors, info, denominations, ui, init }; 33 | 34 | export default { 35 | accounts, 36 | payments, 37 | wallets, 38 | settings, 39 | vendors, 40 | info, 41 | denominations, 42 | init, 43 | ui, 44 | ...dataGeneric, 45 | }; 46 | -------------------------------------------------------------------------------- /app/sources/src/stores/info.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrypoint of info store 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import logger from 'utils/logging'; 11 | import GA from 'utils/GA'; 12 | 13 | import { createDataFetcher } from './dataGeneric'; 14 | 15 | const log = logger(); 16 | 17 | // ----------------------------------------------------------------------------- 18 | // Code 19 | // ----------------------------------------------------------------------------- 20 | 21 | const info = { 22 | get: createDataFetcher({ 23 | name: 'InfoGet', 24 | fetchOptions: { 25 | url: '/info', 26 | localFirst: true, 27 | }, 28 | }), 29 | }; 30 | 31 | info.skipAnnouncement = createDataFetcher({ 32 | name: 'skipAnnouncement', 33 | fetchOptions: { 34 | method: 'POST', 35 | localName: 'skippedAnnouncements', 36 | localOnly: true, 37 | }, 38 | async run(anuid) { 39 | const alreadySkipped = info.getSkippedAnnouncements.data || []; 40 | await info.getEngagedAnnouncements.run(); 41 | GA({ 42 | type: 'event', 43 | category: 'announcement', 44 | action: 45 | info.getEngagedAnnouncements.data && info.getEngagedAnnouncements.data.includes(anuid) 46 | ? 'skippedAndEngaged' 47 | : 'skipped', 48 | label: anuid, 49 | }); 50 | return this.startFetching({ body: { data: [...alreadySkipped, anuid] } }); 51 | }, 52 | onData() { 53 | info.getSkippedAnnouncements.run(); 54 | }, 55 | }); 56 | 57 | info.getSkippedAnnouncements = createDataFetcher({ 58 | name: 'getSkippedAnnouncements', 59 | fetchOptions: { 60 | localName: 'skippedAnnouncements', 61 | localOnly: true, 62 | defaultValue: { data: [] }, 63 | }, 64 | }); 65 | 66 | info.engageInAnnouncement = createDataFetcher({ 67 | name: 'engageInAnnouncement', 68 | fetchOptions: { 69 | method: 'POST', 70 | localName: 'engagedAnnouncements', 71 | localOnly: true, 72 | }, 73 | async run(anuid) { 74 | const alreadyEngaged = info.getEngagedAnnouncements.data || []; 75 | GA({ 76 | type: 'event', 77 | category: 'announcement', 78 | action: 'engaged', 79 | label: anuid, 80 | }); 81 | return this.startFetching({ body: { data: [...alreadyEngaged, anuid] } }); 82 | }, 83 | onData() { 84 | info.getEngagedAnnouncements.run(); 85 | }, 86 | }); 87 | 88 | info.getEngagedAnnouncements = createDataFetcher({ 89 | name: 'getEngagedAnnouncements', 90 | fetchOptions: { 91 | localName: 'engagedAnnouncements', 92 | localOnly: true, 93 | defaultValue: { data: [] }, 94 | }, 95 | }); 96 | 97 | export default info; 98 | -------------------------------------------------------------------------------- /app/sources/src/stores/settings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrypoint of settings store 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import logger from 'utils/logging'; 11 | 12 | import { denominations } from 'stores'; 13 | 14 | import { createDataFetcher, round } from './dataGeneric'; 15 | 16 | const log = logger(); 17 | 18 | // ----------------------------------------------------------------------------- 19 | // Code 20 | // ----------------------------------------------------------------------------- 21 | 22 | const settings = { 23 | default: { 24 | content_script_permissions: undefined, 25 | denominations_BTC_main: 'USD', 26 | denominations_BTC_additional: 'SAT', 27 | }, 28 | }; 29 | 30 | settings.get = createDataFetcher({ 31 | name: 'SettingsGet', 32 | fetchOptions: { 33 | localName: 'settings', 34 | localOnly: true, 35 | defaultValue: { data: settings.default }, 36 | }, 37 | init() { 38 | return new Promise(resolve => { 39 | window.chrome.permissions.contains( 40 | { 41 | permissions: ['tabs'], 42 | origins: [''], 43 | }, 44 | async granted => { 45 | await settings.get.run(); 46 | if (!settings.get.data) { 47 | await settings.set.run(settings.default); 48 | } else if (!settings.get.data.content_script_permissions && granted) { 49 | await settings.set.run({ content_script_permissions: 'granted' }); 50 | } 51 | resolve(); 52 | }, 53 | ); 54 | }); 55 | }, 56 | parseData(localSettings) { 57 | return { 58 | ...settings.default, 59 | ...localSettings, 60 | }; 61 | }, 62 | onData() { 63 | denominations.get.run(); 64 | }, 65 | }); 66 | 67 | settings.set = createDataFetcher({ 68 | name: 'SettingsSet', 69 | fetchOptions: { 70 | method: 'POST', 71 | localName: 'settings', 72 | localOnly: true, 73 | }, 74 | async run(newSettings) { 75 | const currentSettings = settings.get.data || settings.default; 76 | return this.startFetching({ body: { data: { ...currentSettings, ...newSettings } } }); 77 | }, 78 | onData() { 79 | settings.get.run(); 80 | }, 81 | }); 82 | 83 | export default settings; 84 | -------------------------------------------------------------------------------- /app/sources/src/stores/ui.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrypoint of UI store 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import logger from 'utils/logging'; 11 | 12 | import { createDataFetcher } from './dataGeneric'; 13 | 14 | const log = logger(); 15 | 16 | // ----------------------------------------------------------------------------- 17 | // Code 18 | // ----------------------------------------------------------------------------- 19 | 20 | const ui = { 21 | getLiveChat: createDataFetcher({ 22 | name: 'GetLiveChat', 23 | data: { unread: 0 }, 24 | async run() { 25 | return this.data || { unread: 0 }; 26 | }, 27 | }), 28 | }; 29 | 30 | ui.setLiveChat = createDataFetcher({ 31 | name: 'SetLiveChat', 32 | async run(data) { 33 | return ui.getLiveChat.onDataDefault(data, undefined, {}); 34 | }, 35 | }); 36 | 37 | export default ui; 38 | -------------------------------------------------------------------------------- /app/sources/src/stores/wallets.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrypoint of all wallets store 3 | * 4 | */ 5 | 6 | // ----------------------------------------------------------------------------- 7 | // Dependencies 8 | // ----------------------------------------------------------------------------- 9 | 10 | import logger from 'utils/logging'; 11 | 12 | import { accounts } from 'stores'; 13 | 14 | import { createDataFetcher } from './dataGeneric'; 15 | 16 | const log = logger(); 17 | 18 | // ----------------------------------------------------------------------------- 19 | // Code 20 | // ----------------------------------------------------------------------------- 21 | 22 | const wallets = { 23 | getDetails: createDataFetcher({ 24 | name: 'WalletsGetDetails', 25 | async fetchOptions({ wuid, asset } = {}) { 26 | return { 27 | url: `/wallets/details?asset=${asset}&wuid=${wuid}`, 28 | wuid, 29 | headers: { 30 | Authorization: `Bearer ${accounts.authenticate.data && accounts.authenticate.data.token}`, 31 | }, 32 | }; 33 | }, 34 | parseData(data) { 35 | return { 36 | ...data, 37 | memo: data.memo && data.memo.replace(/&/g, '%26'), 38 | description: data.description && data.description.replace(/&/g, '%26'), 39 | }; 40 | }, 41 | async run(wuid, asset) { 42 | return this.startFetching({ 43 | asset, 44 | wuid, 45 | headers: { 46 | Authorization: `Bearer ${accounts.authenticate.data && accounts.authenticate.data.token}`, 47 | }, 48 | }); 49 | }, 50 | }), 51 | }; 52 | 53 | export default wallets; 54 | -------------------------------------------------------------------------------- /app/sources/src/utils/HA.js: -------------------------------------------------------------------------------- 1 | 2 | const trackingId = 3 | process.env.NODE_ENV !== 'production' 4 | ? '3256711276' 5 | : '664479579'; 6 | 7 | (function HAinitializer() { 8 | window.heap = window.heap || []; 9 | window.heap.load = function(e, t) { 10 | window.heap.appid = e; 11 | window.heap.config = t = t || {}; 12 | var r = t.forceSSL || 'https:' === document.location.protocol; 13 | const a = document.createElement('script'); 14 | a.type = 'text/javascript'; 15 | a.async = !0; 16 | a.src = (r ? 'https:' : 'https:') + '//cdn.heapanalytics.com/js/heap-' + e + '.js'; 17 | var n = document.getElementsByTagName('script')[0]; 18 | n.parentNode.insertBefore(a, n); 19 | for ( 20 | var o = function(e) { 21 | return function() { 22 | window.heap.push([e].concat(Array.prototype.slice.call(arguments, 0))); 23 | }; 24 | }, 25 | p = [ 26 | 'addEventProperties', 27 | 'addUserProperties', 28 | 'clearEventProperties', 29 | 'identify', 30 | 'resetIdentity', 31 | 'removeEventProperty', 32 | 'setEventProperties', 33 | 'track', 34 | 'unsetEventProperty', 35 | ], 36 | c = 0; 37 | c < p.length; 38 | c++ 39 | ) 40 | window.heap[p[c]] = o(p[c]); 41 | }; 42 | window.heap.load(trackingId); 43 | })(); 44 | export default ''; 45 | -------------------------------------------------------------------------------- /app/sources/src/utils/cryptonetChecker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Toolkit for checking current net (simnet/testnet/mainnet) 3 | * where app operates 4 | * 5 | */ 6 | 7 | // ----------------------------------------------------------------------------- 8 | // Dependencies 9 | // ----------------------------------------------------------------------------- 10 | 11 | // ----------------------------------------------------------------------------- 12 | // Code 13 | // ----------------------------------------------------------------------------- 14 | 15 | const getCurrentNet = () => 16 | (window.location.href.includes('simnet') 17 | ? 'simnet' 18 | : window.location.href.includes('testnet') ? 'testnet' : 'mainnet'); 19 | 20 | export default getCurrentNet; 21 | -------------------------------------------------------------------------------- /docker-compose-build.yml: -------------------------------------------------------------------------------- 1 | # Here changes of docker-compose for build prod app 2 | version: '3.4' 3 | 4 | services: 5 | ui: 6 | build: 7 | context: ./app 8 | target: builder 9 | volumes: 10 | - ./build:/opt/app/build-prod 11 | environment: 12 | - NODE_ENV=production 13 | -------------------------------------------------------------------------------- /docker-compose-dev.yml: -------------------------------------------------------------------------------- 1 | # Here changes of docker-compose for development 2 | version: '3.4' 3 | 4 | services: 5 | ui: 6 | build: 7 | context: ./app 8 | target: development 9 | volumes: 10 | - ./build-watch:/opt/app/dist 11 | - ./app/sources/src:/opt/app/src 12 | - ./app/sources/public:/opt/app/public 13 | ports: 14 | - "3005:3000" 15 | environment: 16 | - NODE_ENV=development 17 | # Allows you to use any host like lvh.me and others 18 | - DANGEROUSLY_DISABLE_HOST_CHECK=true 19 | # Enables live reload inside VM or Docker 20 | - CHOKIDAR_USEPOLLING=true -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | # ---------------------------------------------------------------------------- 3 | # Setup default logging for all services 4 | # ---------------------------------------------------------------------------- 5 | x-logging: 6 | &default-logging 7 | driver: json-file 8 | options: 9 | max-size: '10m' 10 | max-file: '10' 11 | 12 | services: 13 | # ---------------------------------------------------------------------------- 14 | # Reverse proxy and SSL provider for all services 15 | # ---------------------------------------------------------------------------- 16 | traefik: 17 | build: 18 | context: ./ 19 | dockerfile: Dockerfile-traefik 20 | ports: 21 | - "80:80" 22 | - "443:443" 23 | - "10.135.15.180:8080:8080" 24 | restart: on-failure:3 25 | logging: *default-logging 26 | volumes: 27 | - /var/run/docker.sock:/var/run/docker.sock 28 | - ssl:/etc/traefik/acme 29 | 30 | # ---------------------------------------------------------------------------- 31 | # Static file server with UI inside 32 | # ---------------------------------------------------------------------------- 33 | ui: 34 | build: ./app 35 | restart: on-failure:3 36 | logging: *default-logging 37 | expose: 38 | - "80" 39 | environment: 40 | - INTERCOM_TOKEN=replace 41 | - NODE_ENV=production 42 | labels: 43 | - "traefik.enable=true" 44 | - "traefik.backend=ui" 45 | - "traefik.frontend.priority=10" 46 | - "traefik.port=80" 47 | - "traefik.frontend.rule=Host:testnet.bitlum.io,mainnet.bitlum.io" 48 | 49 | volumes: 50 | ssl: 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bitlum-ui", 3 | "version": "0.0.1", 4 | "description": "Bitlum wallet frontend services", 5 | "author": "Denis Khvostov", 6 | "private": true, 7 | "//": [ 8 | "Comments section:", 9 | "root: is base docker-compose command with base Dockerfiles", 10 | "deploy: command that must be used for deployment to server through compose", 11 | "up: command to run app on development machine" 12 | ], 13 | "scripts": { 14 | "root": "docker-compose --project-name %npm_package_name% -f ./docker-compose.yml", 15 | "deploy": "npm run root -- up --build -d", 16 | "build": "npm run root -- -f ./docker-compose-build.yml up --build", 17 | "up": "npm run root -- -f ./docker-compose-dev.yml up --build" 18 | } 19 | } -------------------------------------------------------------------------------- /traefik.toml: -------------------------------------------------------------------------------- 1 | debug = false 2 | 3 | logLevel = "INFO" 4 | defaultEntryPoints = ["https","http"] 5 | 6 | [entryPoints] 7 | [entryPoints.http] 8 | address = ":80" 9 | [entryPoints.http.redirect] 10 | entryPoint = "https" 11 | [entryPoints.https] 12 | address = ":443" 13 | [entryPoints.https.tls] 14 | 15 | [api] 16 | 17 | [docker] 18 | watch = true 19 | exposedByDefault = false 20 | 21 | [acme] 22 | email = "talionarwork@gmail.com" 23 | storage = "/etc/traefik/acme/acme.json" 24 | OnHostRule=true 25 | acmeLogging = true 26 | entryPoint = "https" 27 | [acme.httpChallenge] 28 | entryPoint = "http" 29 | 30 | [file] 31 | [backends.testnet_api] 32 | [backends.testnet_api.servers.server] 33 | url = "https://testnet.api.bitlum.io" 34 | 35 | [backends.mainnet_api] 36 | [backends.mainnet_api.servers.server] 37 | url = "https://api.bitlum.io" 38 | 39 | [frontends.testnet_api_passthrough] 40 | backend = "testnet_api" 41 | priority = 11 42 | [frontends.testnet_api_passthrough.routes.main] 43 | rule = "Host:testnet.bitlum.io;PathPrefixStrip:/api/" 44 | 45 | [frontends.mainnet_api_passthrough] 46 | backend = "mainnet_api" 47 | priority = 11 48 | [frontends.mainnet_api_passthrough.routes.main] 49 | rule = "Host:mainnet.bitlum.io;PathPrefixStrip:/api/" --------------------------------------------------------------------------------