diff --git a/.editorconfig b/.editorconfig index 013c8c5..37fee84 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,6 +8,3 @@ end_of_line = lf insert_final_newline = true charset = utf-8 trim_trailing_whitespace = true - -[*.css,*.scss] -indent_size = 4 diff --git a/.stylelintrc b/.stylelintrc index 38d2566..7f7097c 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -3,10 +3,5 @@ "stylelint-config-standard", "stylelint-config-css-modules", "./node_modules/prettier-stylelint/config.js" - ], - "rules": { - "indentation": 4, - "declaration-empty-line-before": null, - "number-leading-zero": "never" - } + ] } diff --git a/package.json b/package.json index 1d145c5..0ab3db0 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,9 @@ "package": "electron-builder build -mwl -p never && open ./dist", "dist": "rm -rf {dist,build}/ && npm run build && npm run package", "release": "release-it --non-interactive", - "changelog": "auto-changelog -p" + "changelog": "auto-changelog -p", + "format": "prettier --write 'src/**/*.{js,jsx}' && npm run format:css", + "format:css": "prettier-stylelint --write --quiet 'src/**/*.{css,scss}'" }, "repository": "https://github.com/kremalicious/blowfish.git", "homepage": "https://github.com/kremalicious/blowfish", @@ -32,7 +34,6 @@ "@babel/plugin-proposal-class-properties": "^7.5.5", "@babel/preset-env": "^7.6.0", "@babel/preset-react": "^7.0.0", - "@babel/runtime": "^7.6.0", "@reach/router": "^1.2.1", "@svgr/webpack": "^4.3.2", "auto-changelog": "^1.16.1", @@ -51,14 +52,12 @@ "file-loader": "^4.2.0", "html-webpack-plugin": "^3.2.0", "mini-css-extract-plugin": "^0.8.0", - "node-sass": "^4.12.0", "prettier": "^1.18.2", "prettier-stylelint": "^0.4.2", "react": "^16.9.0", "react-dom": "^16.9.0", "react-pose": "^4.0.8", "release-it": "^12.3.6", - "sass-loader": "^8.0.0", "style-loader": "^1.0.0", "stylelint": "^10.1.0", "stylelint-config-css-modules": "^1.4.0", @@ -67,7 +66,7 @@ "webpack-cli": "^3.3.8", "webpack-dev-server": "^3.8.0" }, - "browserslist": "electron >= 5.0", + "browserslist": "electron >= 6.0", "build": { "appId": "com.kremalicious.blowfish", "files": [ diff --git a/src/renderer/App.css b/src/renderer/App.css deleted file mode 100644 index ea9ad65..0000000 --- a/src/renderer/App.css +++ /dev/null @@ -1,96 +0,0 @@ -*, -*::before, -*::after { - box-sizing: border-box; -} - -html, -body { - margin: 0; - padding: 0; - width: 100%; - height: 100%; - background: #fcfcfc !important; - overflow: hidden; -} - -html.dark, -.dark body { - background: #141414 !important; -} - -html { - font-size: 13px; -} - -html.fullscreen { - font-size: 24px; -} - -#root { - height: 100%; - position: relative; - font-size: 1rem; - line-height: 1.3; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Ubuntu, - Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', - 'Segoe UI Symbol'; - font-feature-settings: 'kern' 1, 'liga' 1, 'calt' 1, 'pnum' 1, 'tnum' 0, - 'onum' 0, 'lnum' 0, 'dlig' 1; - color: #303030; - transform: translate3d(0, 0, 0); - -webkit-app-region: drag; - -webkit-user-select: none; -} - -.dark #root { - color: #e2e2e2; -} - -h1, -h2, -h3, -h4, -h5 { - font-weight: 700; -} - -a, -button { - text-decoration: none; - cursor: default; -} - -a h1 { - color: unset; -} - -.app { - padding: 10% 7% 5% 7%; - cursor: default; - transition: .15s ease-out; - width: 100%; - height: 100%; - overflow-y: auto; -} - -.darwin .app { - padding-top: calc(35px + 10%); -} - -.fullscreen.darwin .app { - transform: translate3d(0, -36px, 0); -} - -.box { - width: 100%; - padding: 5%; - background: #fff; - border-radius: 5px; - border: .1rem solid #e2e2e2; -} - -.dark .box { - background: #222; - border-color: #303030; -} diff --git a/src/renderer/App.jsx b/src/renderer/App.jsx index b04c4e6..6454683 100644 --- a/src/renderer/App.jsx +++ b/src/renderer/App.jsx @@ -10,10 +10,10 @@ import { import { webFrame, ipcRenderer } from 'electron' import posed, { PoseGroup } from 'react-pose' import Titlebar from './components/Titlebar' +import { defaultAnimation } from './components/Animations' import Home from './screens/Home' import Preferences from './screens/Preferences' -import './App.css' -import { defaultAnimation } from './components/Animations' +import styles from './App.module.css' // // Disable zooming @@ -55,7 +55,7 @@ export default class App extends PureComponent { return ( <> {process.platform === 'darwin' && } -
+
diff --git a/src/renderer/App.module.css b/src/renderer/App.module.css new file mode 100644 index 0000000..fee0a99 --- /dev/null +++ b/src/renderer/App.module.css @@ -0,0 +1,16 @@ +.app { + padding: 10% 7% 5% 7%; + cursor: default; + transition: 0.15s ease-out; + width: 100%; + height: 100%; + overflow-y: auto; +} + +:global(.darwin) .app { + padding-top: calc(35px + 10%); +} + +:global(.fullscreen.darwin) .app { + transform: translate3d(0, -36px, 0); +} diff --git a/src/renderer/components/Account.jsx b/src/renderer/components/Account.jsx deleted file mode 100644 index b4751b4..0000000 --- a/src/renderer/components/Account.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { PureComponent } from 'react' -import PropTypes from 'prop-types' -import { openUrl } from '../../utils' -import Balance from './Balance' - -export default class Account extends PureComponent { - static propTypes = { - account: PropTypes.shape({ - address: PropTypes.string.isRequired, - balance: PropTypes.object.isRequired - }) - } - - render() { - const { account } = this.props - const { balance, address } = account - - return ( - - ) - } -} diff --git a/src/renderer/components/Balance.jsx b/src/renderer/components/Balance.jsx index ee0feac..0335a40 100644 --- a/src/renderer/components/Balance.jsx +++ b/src/renderer/components/Balance.jsx @@ -1,30 +1,37 @@ -import React, { PureComponent } from 'react' +import React, { useContext } from 'react' import PropTypes from 'prop-types' import posed, { PoseGroup } from 'react-pose' import { AppContext } from '../store/createContext' import { cryptoFormatter } from '../../utils' -import styles from './Balance.module.scss' import { fadeIn } from './Animations' +import Label from './Label' +import styles from './Balance.module.css' const Animation = posed.h1(fadeIn) -export default class Balance extends PureComponent { - static contextType = AppContext +const Balance = ({ balance, label, labelOnClick, large }) => { + const { currency } = useContext(AppContext) - static propTypes = { - balance: PropTypes.object.isRequired - } - - render() { - const { currency } = this.context - const { balance } = this.props - - return ( + return ( +
- + {cryptoFormatter(balance[currency], currency)} - ) - } + {label && } +
+ ) } + +Balance.propTypes = { + balance: PropTypes.object.isRequired, + label: PropTypes.string, + labelOnClick: PropTypes.func, + large: PropTypes.bool +} + +export default Balance diff --git a/src/renderer/components/Balance.module.css b/src/renderer/components/Balance.module.css new file mode 100644 index 0000000..5099139 --- /dev/null +++ b/src/renderer/components/Balance.module.css @@ -0,0 +1,33 @@ +.balance { + text-align: center; + width: 100%; +} + +.number { + margin: 0; + -webkit-app-region: no-drag; + -webkit-user-select: text; + font-size: 1rem; + display: inline-block; + padding: 0 0.3rem; + border-radius: 4px; +} + +.numberLarge { + composes: number; + font-size: 2.5rem; +} + +.updated { + animation: updated 0.5s ease-out; +} + +@keyframes updated { + 0% { + background: rgba(255, 255, 255, 0.2); + } + + 100% { + background: rgba(255, 255, 255, 0); + } +} diff --git a/src/renderer/components/Balance.module.scss b/src/renderer/components/Balance.module.scss deleted file mode 100644 index 02fcd70..0000000 --- a/src/renderer/components/Balance.module.scss +++ /dev/null @@ -1,27 +0,0 @@ -.number { - margin: 0; - -webkit-app-region: no-drag; - -webkit-user-select: text; - font-size: 1rem; - display: inline-block; - padding: 0 .3rem; - border-radius: 4px; - - :global(.number-unit--main) & { - font-size: 2.5rem; - } -} - -.updated { - animation: updated .5s ease-out; -} - -@keyframes updated { - 0% { - background: rgba(255, 255, 255, .2); - } - - 100% { - background: rgba(255, 255, 255, 0); - } -} diff --git a/src/renderer/components/Box.module.css b/src/renderer/components/Box.module.css new file mode 100644 index 0000000..b436e46 --- /dev/null +++ b/src/renderer/components/Box.module.css @@ -0,0 +1,12 @@ +.box { + width: 100%; + padding: 5%; + background: #fff; + border-radius: 5px; + border: 0.1rem solid #e2e2e2; +} + +:global(.dark) .box { + background: #222; + border-color: #303030; +} diff --git a/src/renderer/components/Divider.jsx b/src/renderer/components/Divider.jsx new file mode 100644 index 0000000..a9c1e8c --- /dev/null +++ b/src/renderer/components/Divider.jsx @@ -0,0 +1,6 @@ +import React from 'react' +import styles from './Divider.module.css' + +const Divider = () =>
+ +export default Divider diff --git a/src/renderer/components/Divider.module.css b/src/renderer/components/Divider.module.css new file mode 100644 index 0000000..213d994 --- /dev/null +++ b/src/renderer/components/Divider.module.css @@ -0,0 +1,11 @@ +.divider { + width: 100%; + height: 1px; + background: #e2e2e2; + margin-top: 5%; + border: 0; +} + +:global(.dark) .divider { + background: #303030; +} diff --git a/src/renderer/components/Label.jsx b/src/renderer/components/Label.jsx new file mode 100644 index 0000000..dc68f70 --- /dev/null +++ b/src/renderer/components/Label.jsx @@ -0,0 +1,16 @@ +import React from 'react' +import PropTypes from 'prop-types' +import styles from './Label.module.css' + +const Label = ({ children, labelOnClick, ...props }) => ( + + {children} + +) + +Label.propTypes = { + labelOnClick: PropTypes.func, + children: PropTypes.any.isRequired +} + +export default Label diff --git a/src/renderer/components/Label.module.css b/src/renderer/components/Label.module.css new file mode 100644 index 0000000..ab262ca --- /dev/null +++ b/src/renderer/components/Label.module.css @@ -0,0 +1,7 @@ +.label { + color: #8b98a9; + font-size: 0.85rem; + display: block; + white-space: nowrap; + margin-top: 0.5rem; +} diff --git a/src/renderer/components/Spinner.jsx b/src/renderer/components/Spinner.jsx index 14e53de..5e8310a 100644 --- a/src/renderer/components/Spinner.jsx +++ b/src/renderer/components/Spinner.jsx @@ -1,5 +1,5 @@ import React from 'react' -import styles from './Spinner.module.scss' +import styles from './Spinner.module.css' const Spinner = () =>
diff --git a/src/renderer/components/Spinner.module.css b/src/renderer/components/Spinner.module.css new file mode 100644 index 0000000..ed36c44 --- /dev/null +++ b/src/renderer/components/Spinner.module.css @@ -0,0 +1,28 @@ +.spinner { + position: relative; + text-align: center; + margin: auto; + width: 100%; +} + +.spinner::before { + content: ''; + box-sizing: border-box; + position: absolute; + top: 0; + left: 50%; + width: 20px; + height: 20px; + margin-top: -20px; + margin-left: -10px; + border-radius: 50%; + border: 2px solid #7b1173; + border-top-color: #e000cf; + animation: spinner 0.6s linear infinite; +} + +@keyframes spinner { + to { + transform: rotate(360deg); + } +} diff --git a/src/renderer/components/Spinner.module.scss b/src/renderer/components/Spinner.module.scss deleted file mode 100644 index b01c1a0..0000000 --- a/src/renderer/components/Spinner.module.scss +++ /dev/null @@ -1,28 +0,0 @@ -.spinner { - position: relative; - text-align: center; - margin: auto; - width: 100%; - - &::before { - content: ''; - box-sizing: border-box; - position: absolute; - top: 0; - left: 50%; - width: 20px; - height: 20px; - margin-top: -20px; - margin-left: -10px; - border-radius: 50%; - border: 2px solid #7b1173; - border-top-color: #e000cf; - animation: spinner .6s linear infinite; - } -} - -@keyframes spinner { - to { - transform: rotate(360deg); - } -} diff --git a/src/renderer/components/Ticker.css b/src/renderer/components/Ticker.css deleted file mode 100644 index 67b2bc3..0000000 --- a/src/renderer/components/Ticker.css +++ /dev/null @@ -1,56 +0,0 @@ -.ticker { - margin-top: 3rem; - width: 100%; -} - -.ticker .number-unit { - margin-top: 0; -} - -.ticker button { - background: none; - border: 1px solid #e2e2e2; - box-shadow: none; - margin: 0 auto; - outline: 0; - font-size: .75rem; - border-radius: .3rem; - padding: .3rem .4rem; - display: block; - width: 100%; - max-width: 12rem; - transition: border .2s ease-out; - color: #41474e; -} - -.ticker button:disabled { - pointer-events: none; -} - -.dark .ticker button { - border-color: #303030; - color: #8b98a9; -} - -.label--price { - font-size: .95rem; -} - -.change { - font-size: .65rem; - display: inline-block; - margin-left: .25rem; -} - -.change--positive { - color: forestgreen; -} - -.change--negative { - color: crimson; -} - -.active .change--positive, -.active .change--negative { - color: #fff !important; -} diff --git a/src/renderer/components/Ticker.jsx b/src/renderer/components/Ticker.jsx deleted file mode 100644 index 8183b29..0000000 --- a/src/renderer/components/Ticker.jsx +++ /dev/null @@ -1,75 +0,0 @@ -import React, { PureComponent } from 'react' -import PropTypes from 'prop-types' -import posed, { PoseGroup } from 'react-pose' -import { AppContext } from '../store/createContext' -import { cryptoFormatter } from '../../utils' -import './Ticker.css' -import { fadeIn } from './Animations' - -const Item = posed.div(fadeIn) - -const Change = ({ currency }) => ( - - {({ priceChanges }) => { - const isNegative = JSON.stringify(priceChanges[currency]).startsWith('-') - let classes = isNegative ? 'change--negative' : 'change--positive' - - return ( - - {!isNegative && '+'} - {Number(priceChanges[currency]).toFixed(1)}% - - ) - }} - -) - -Change.propTypes = { - currency: PropTypes.string.isRequired -} - -export default class Ticker extends PureComponent { - static contextType = AppContext - - items = () => { - const { - prices, - needsConfig, - currency, - toggleCurrencies, - accentColor - } = this.context - - const activeStyle = { - backgroundColor: accentColor, - color: '#fff', - borderColor: accentColor - } - - // convert Map to array first, cause for...of or forEach returns - // undefined, so it cannot be mapped to a collection of elements - return [...prices.entries()].map(([key, value]) => ( - - - - )) - } - - render() { - return ( -
-
- {this.items()} -
-
- ) - } -} diff --git a/src/renderer/components/Titlebar.jsx b/src/renderer/components/Titlebar.jsx index fc1daa0..d0d1acf 100644 --- a/src/renderer/components/Titlebar.jsx +++ b/src/renderer/components/Titlebar.jsx @@ -1,6 +1,6 @@ import React from 'react' import pkg from '../../../package.json' -import styles from './Titlebar.module.scss' +import styles from './Titlebar.module.css' const Titlebar = () => (
diff --git a/src/renderer/components/Titlebar.module.css b/src/renderer/components/Titlebar.module.css new file mode 100644 index 0000000..e20ecb9 --- /dev/null +++ b/src/renderer/components/Titlebar.module.css @@ -0,0 +1,48 @@ +.titlebar { + position: fixed; + width: 100%; + height: 35px; + line-height: 35px; + text-align: center; + user-select: none; + background: linear-gradient(to top, #ccc 0%, #d6d6d6 1px, #ebebeb 100%); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5); + transition: opacity 0.15s ease-out; +} + +:global(.dark) .titlebar { + background: linear-gradient(to top, #141416 0%, #38383c 1px, #3f3f44 100%); + box-shadow: none; +} + +:global(.fullscreen) .titlebar { + opacity: 0; +} + +:global(.blur) .titlebar { + background: #f6f6f6; +} + +:global(.blur.dark) .titlebar { + background: linear-gradient(to top, #141416 0%, #2d2a32 1px, #2d2a32 100%); +} + +.titlebarTitle { + line-height: 35px; + height: 35px; + font-weight: 400; + font-size: 13px; + color: #555; +} + +:global(.dark) .titlebarTitle { + color: #b6b3ba; +} + +:global(.blur) .titlebarTitle { + color: #b6b6b6; +} + +:global(.blur.dark) .titlebarTitle { + color: #67666e; +} diff --git a/src/renderer/components/Titlebar.module.scss b/src/renderer/components/Titlebar.module.scss deleted file mode 100644 index d55a871..0000000 --- a/src/renderer/components/Titlebar.module.scss +++ /dev/null @@ -1,58 +0,0 @@ -.titlebar { - position: fixed; - width: 100%; - height: 35px; - line-height: 35px; - text-align: center; - user-select: none; - background: linear-gradient(to top, #ccc 0%, #d6d6d6 1px, #ebebeb 100%); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5); - transition: opacity .15s ease-out; - - :global(.dark) & { - background: linear-gradient( - to top, - #141416 0%, - #38383c 1px, - #3f3f44 100% - ); - box-shadow: none; - } - - :global(.fullscreen) & { - opacity: 0; - } - - :global(.blur) & { - background: #f6f6f6; - } - - :global(.blur.dark) & { - background: linear-gradient( - to top, - #141416 0%, - #2d2a32 1px, - #2d2a32 100% - ); - } -} - -.titlebarTitle { - line-height: 35px; - height: 35px; - font-weight: 400; - font-size: 13px; - color: #555; - - :global(.dark) & { - color: #b6b3ba; - } - - :global(.blur) & { - color: #b6b6b6; - } - - :global(.blur.dark) & { - color: #67666e; - } -} diff --git a/src/renderer/components/Total.jsx b/src/renderer/components/Total.jsx deleted file mode 100644 index 4812f28..0000000 --- a/src/renderer/components/Total.jsx +++ /dev/null @@ -1,44 +0,0 @@ -import React, { PureComponent } from 'react' -import { AppContext } from '../store/createContext' -import Balance from './Balance' -import { conversions } from '../../config' - -const calculateTotalBalance = (accounts, currency) => { - const balanceTotalArray = [] - - for (const account of accounts) { - balanceTotalArray.push(account.balance[currency]) - } - - // Convert array to numbers and add numbers together - const balanceTotal = balanceTotalArray.reduce( - (a, b) => Number(a) + Number(b), - 0 - ) - - return balanceTotal -} - -export default class Total extends PureComponent { - static contextType = AppContext - - render() { - const conversionsBalance = Object.assign( - ...conversions.map(key => ({ - [key]: calculateTotalBalance(this.context.accounts, key) - })) - ) - - const balanceNew = { - ocean: calculateTotalBalance(this.context.accounts, 'ocean'), - ...conversionsBalance - } - - return ( -
- - Total Balance -
- ) - } -} diff --git a/src/renderer/components/Welcome.jsx b/src/renderer/components/Welcome.jsx index 0f8e937..648b3c1 100644 --- a/src/renderer/components/Welcome.jsx +++ b/src/renderer/components/Welcome.jsx @@ -1,20 +1,20 @@ -import React from 'react' +import React, { useContext } from 'react' import { Link } from '@reach/router' -import IconRocket from '../images/rocket.svg' -import styles from './Welcome.module.scss' import { AppContext } from '../store/createContext' +import IconRocket from '../images/rocket.svg' +import styles from './Welcome.module.css' -const Welcome = () => ( -
- - - {context => ( - - Add your first address to get started. - - )} - -
-) +const Welcome = () => { + const { accentColor } = useContext(AppContext) + + return ( +
+ + + Add your first address to get started. + +
+ ) +} export default Welcome diff --git a/src/renderer/components/Welcome.module.css b/src/renderer/components/Welcome.module.css new file mode 100644 index 0000000..1ccf81e --- /dev/null +++ b/src/renderer/components/Welcome.module.css @@ -0,0 +1,18 @@ +.welcome { + width: 100%; + height: 100%; + text-align: center; + margin-top: 2rem; +} + +.welcome svg { + display: block; + width: 5rem; + height: 5rem; + margin: 0 auto 2rem auto; + fill: #e2e2e2; +} + +:global(.dark) .welcome svg { + fill: #41474e; +} diff --git a/src/renderer/components/Welcome.module.scss b/src/renderer/components/Welcome.module.scss deleted file mode 100644 index c389e27..0000000 --- a/src/renderer/components/Welcome.module.scss +++ /dev/null @@ -1,18 +0,0 @@ -.welcome { - width: 100%; - height: 100%; - text-align: center; - margin-top: 2rem; - - svg { - display: block; - width: 5rem; - height: 5rem; - margin: 0 auto 2rem auto; - fill: #e2e2e2; - } - - :global(.dark) & svg { - fill: #41474e; - } -} diff --git a/src/renderer/index.css b/src/renderer/index.css new file mode 100644 index 0000000..ac4fad4 --- /dev/null +++ b/src/renderer/index.css @@ -0,0 +1,66 @@ +*, +*::before, +*::after { + box-sizing: border-box; +} + +html, +body { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + background: #fcfcfc !important; + overflow: hidden; +} + +html.dark, +.dark body { + background: #141414 !important; +} + +html { + font-size: 13px; +} + +html.fullscreen { + font-size: 24px; +} + +#root { + height: 100%; + position: relative; + font-size: 1rem; + line-height: 1.3; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Ubuntu, + Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', + 'Segoe UI Symbol'; + font-feature-settings: 'kern' 1, 'liga' 1, 'calt' 1, 'pnum' 1, 'tnum' 0, + 'onum' 0, 'lnum' 0, 'dlig' 1; + color: #303030; + transform: translate3d(0, 0, 0); + -webkit-app-region: drag; + -webkit-user-select: none; +} + +.dark #root { + color: #e2e2e2; +} + +h1, +h2, +h3, +h4, +h5 { + font-weight: 700; +} + +a, +button { + text-decoration: none; + cursor: default; +} + +a h1 { + color: unset; +} diff --git a/src/renderer/index.js b/src/renderer/index.js index 631d13d..e606b4d 100644 --- a/src/renderer/index.js +++ b/src/renderer/index.js @@ -3,6 +3,7 @@ import { render } from 'react-dom' import AppProvider from './store/AppProvider' import App from './App' import pkg from '../../package.json' +import './index.css' document.body.style.backgroundColor = '#141414' diff --git a/src/renderer/screens/Home.css b/src/renderer/screens/Home.css deleted file mode 100644 index d603c96..0000000 --- a/src/renderer/screens/Home.css +++ /dev/null @@ -1,60 +0,0 @@ -.main { - min-height: 222px; - display: flex; - align-items: center; - flex-wrap: wrap; - position: relative; -} - -.preferences-link { - position: absolute; - right: 5%; - top: 1.5rem; -} - -.preferences-link svg { - fill: #8b98a9; - transition: .2s ease-out; - width: 1.25rem; - height: 1.25rem; -} - -.dark .preferences-link svg { - fill: #8b98a9; - opacity: .5; -} - -.number-unit-wrap { - display: grid; - grid-gap: .5rem; - grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); - justify-items: start; - width: 100%; -} - -.number-unit { - text-align: center; - width: 100%; -} - -.label { - color: #8b98a9; - font-size: .85rem; - display: block; - white-space: nowrap; - margin-top: .5rem; -} - -.number-unit-wrap--accounts { - min-height: 55px; - padding-top: 2rem; -} - -.number-unit--main { - padding-bottom: 5%; - border-bottom: 1px solid #e2e2e2; -} - -.dark .number-unit--main { - border-bottom-color: #303030; -} diff --git a/src/renderer/screens/Home.jsx b/src/renderer/screens/Home.jsx deleted file mode 100644 index 5627ba9..0000000 --- a/src/renderer/screens/Home.jsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, { PureComponent } from 'react' -import { Link } from '@reach/router' -import { AppContext } from '../store/createContext' -import Welcome from '../components/Welcome' -import Total from '../components/Total' -import Account from '../components/Account' -import Ticker from '../components/Ticker' -import Spinner from '../components/Spinner' -import IconCog from '../images/cog.svg' -import './Home.css' - -export default class Home extends PureComponent { - static contextType = AppContext - - render() { - const { isLoading, accounts, needsConfig } = this.context - - return ( - <> -
- - - - - {needsConfig ? ( - - ) : isLoading ? ( - - ) : ( - <> - - -
- {accounts.map((account, i) => ( - - ))} -
- - )} -
- - - - ) - } -} diff --git a/src/renderer/screens/Home/Accounts.jsx b/src/renderer/screens/Home/Accounts.jsx new file mode 100644 index 0000000..d0ebc14 --- /dev/null +++ b/src/renderer/screens/Home/Accounts.jsx @@ -0,0 +1,26 @@ +import React, { useContext } from 'react' +import { openUrl } from '../../../utils' +import Balance from '../../components/Balance' +import { AppContext } from '../../store/createContext' +import styles from './Accounts.module.css' + +const Accounts = () => { + const { accounts } = useContext(AppContext) + + return ( +
+ {accounts.map((account, i) => ( + + openUrl(`https://etherscan.io/address/${account.address}#tokentxns`) + } + /> + ))} +
+ ) +} + +export default Accounts diff --git a/src/renderer/screens/Home/Accounts.module.css b/src/renderer/screens/Home/Accounts.module.css new file mode 100644 index 0000000..295138a --- /dev/null +++ b/src/renderer/screens/Home/Accounts.module.css @@ -0,0 +1,5 @@ +.accounts { + composes: balanceWrap from './index.module.css'; + min-height: 55px; + padding-top: 2rem; +} diff --git a/src/renderer/screens/Home/Ticker.jsx b/src/renderer/screens/Home/Ticker.jsx new file mode 100644 index 0000000..1e69168 --- /dev/null +++ b/src/renderer/screens/Home/Ticker.jsx @@ -0,0 +1,71 @@ +import React, { useContext } from 'react' +import PropTypes from 'prop-types' +import posed, { PoseGroup } from 'react-pose' +import { AppContext } from '../../store/createContext' +import { cryptoFormatter } from '../../../utils' +import stylesIndex from './index.module.css' +import styles from './Ticker.module.css' +import { fadeIn } from '../../components/Animations' + +const Item = posed.div(fadeIn) + +const Change = ({ currency }) => { + const { priceChanges } = useContext(AppContext) + const isNegative = JSON.stringify(priceChanges[currency]).startsWith('-') + let classes = isNegative ? styles.negative : styles.positive + + return ( + + {!isNegative && '+'} + {Number(priceChanges[currency]).toFixed(1)}% + + ) +} + +Change.propTypes = { + currency: PropTypes.string.isRequired +} + +const Items = () => { + const { + prices, + needsConfig, + currency, + toggleCurrencies, + accentColor + } = useContext(AppContext) + + const activeStyle = { + backgroundColor: accentColor, + color: '#fff', + borderColor: accentColor + } + + // convert Map to array first, cause for...of or forEach returns + // undefined, so it cannot be mapped to a collection of elements + return [...prices.entries()].map(([key, value]) => ( + + + + )) +} + +const Ticker = props => ( +
+
+ + + +
+
+) + +export default Ticker diff --git a/src/renderer/screens/Home/Ticker.module.css b/src/renderer/screens/Home/Ticker.module.css new file mode 100644 index 0000000..3772080 --- /dev/null +++ b/src/renderer/screens/Home/Ticker.module.css @@ -0,0 +1,76 @@ +.ticker { + margin-top: 3rem; + width: 100%; +} + +.ticker button { + background: none; + border: 1px solid #e2e2e2; + box-shadow: none; + margin: 0 auto; + outline: 0; + font-size: 0.75rem; + border-radius: 0.3rem; + padding: 0.3rem 0.4rem; + display: block; + width: 100%; + max-width: 12rem; + transition: border 0.2s ease-out; + color: #41474e; +} + +.ticker button:disabled { + pointer-events: none; +} + +:global(.dark) .ticker button { + border-color: #303030; + color: #8b98a9; +} + +.label--price { + font-size: 0.95rem; +} + +.change { + font-size: 0.65rem; + display: inline-block; + margin-left: 0.25rem; +} + +.positive { + composes: change; + color: forestgreen; +} + +.negative { + composes: change; + color: crimson; +} + +:global(.active) .positive, +:global(.active) .negative { + color: #fff !important; +} + +.number { + text-align: center; + width: 100%; +} + +.label { + color: #8b98a9; + font-size: 0.85rem; + display: block; + white-space: nowrap; + margin-top: 0.5rem; +} + +.labelActive { + composes: label; +} + +.labelActive .positive, +.labelActive .negative { + color: #fff !important; +} diff --git a/src/renderer/screens/Home/Total.jsx b/src/renderer/screens/Home/Total.jsx new file mode 100644 index 0000000..85b24e1 --- /dev/null +++ b/src/renderer/screens/Home/Total.jsx @@ -0,0 +1,39 @@ +import React, { useContext } from 'react' +import { AppContext } from '../../store/createContext' +import Balance from '../../components/Balance' +import { conversions } from '../../../config' + +const calculateTotalBalance = (accounts, currency) => { + const balanceTotalArray = [] + + for (const account of accounts) { + balanceTotalArray.push(account.balance[currency]) + } + + // Convert array to numbers and add numbers together + const balanceTotal = balanceTotalArray.reduce( + (a, b) => Number(a) + Number(b), + 0 + ) + + return balanceTotal +} + +const Total = () => { + const { accounts } = useContext(AppContext) + + const conversionsBalance = Object.assign( + ...conversions.map(key => ({ + [key]: calculateTotalBalance(accounts, key) + })) + ) + + const balanceNew = { + ocean: calculateTotalBalance(accounts, 'ocean'), + ...conversionsBalance + } + + return +} + +export default Total diff --git a/src/renderer/screens/Home/index.jsx b/src/renderer/screens/Home/index.jsx new file mode 100644 index 0000000..5bd4259 --- /dev/null +++ b/src/renderer/screens/Home/index.jsx @@ -0,0 +1,41 @@ +import React, { useContext } from 'react' +import { Link } from '@reach/router' +import { AppContext } from '../../store/createContext' +import Welcome from '../../components/Welcome' +import Spinner from '../../components/Spinner' +import Divider from '../../components/Divider' +import Total from './Total' +import Ticker from './Ticker' +import Accounts from './Accounts' +import IconCog from '../../images/cog.svg' +import styles from './index.module.css' + +const Home = () => { + const { isLoading, needsConfig } = useContext(AppContext) + + return ( + <> +
+ + + + + {needsConfig ? ( + + ) : isLoading ? ( + + ) : ( + <> + + + + + )} +
+ + + + ) +} + +export default Home diff --git a/src/renderer/screens/Home/index.module.css b/src/renderer/screens/Home/index.module.css new file mode 100644 index 0000000..1c6a3ee --- /dev/null +++ b/src/renderer/screens/Home/index.module.css @@ -0,0 +1,34 @@ +.main { + composes: box from '../../components/Box.module.css'; + min-height: 222px; + display: flex; + align-items: center; + flex-wrap: wrap; + position: relative; +} + +.preferences { + position: absolute; + right: 5%; + top: 1.5rem; +} + +.preferences svg { + fill: #8b98a9; + transition: 0.2s ease-out; + width: 1.25rem; + height: 1.25rem; +} + +:global(.dark) .preferences svg { + fill: #8b98a9; + opacity: 0.5; +} + +.balanceWrap { + display: grid; + grid-gap: 0.5rem; + grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); + justify-items: start; + width: 100%; +} diff --git a/src/renderer/screens/Preferences/Accounts/New.jsx b/src/renderer/screens/Preferences/Accounts/New.jsx new file mode 100644 index 0000000..c72a0a8 --- /dev/null +++ b/src/renderer/screens/Preferences/Accounts/New.jsx @@ -0,0 +1,32 @@ +import React from 'react' +import PropTypes from 'prop-types' +import styles from './New.module.css' + +const New = ({ input, handleInputChange, handleSave, accentColor }) => ( +
  • + handleInputChange(e)} + className={styles.input} + /> + + +
  • +) + +New.propTypes = { + input: PropTypes.string.isRequired, + handleInputChange: PropTypes.func.isRequired, + handleSave: PropTypes.func.isRequired, + accentColor: PropTypes.string.isRequired +} + +export default New diff --git a/src/renderer/screens/Preferences/Accounts/New.module.css b/src/renderer/screens/Preferences/Accounts/New.module.css new file mode 100644 index 0000000..b91589a --- /dev/null +++ b/src/renderer/screens/Preferences/Accounts/New.module.css @@ -0,0 +1,18 @@ +.input { + font-size: 1rem; + outline: 0; + background: none; + border: 0; + width: 80%; + color: #303030; + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +:global(.dark) .input { + color: #fff; +} + +.button { + composes: button from './index.module.css'; +} diff --git a/src/renderer/screens/Preferences/Accounts/Saved.jsx b/src/renderer/screens/Preferences/Accounts/Saved.jsx new file mode 100644 index 0000000..774e949 --- /dev/null +++ b/src/renderer/screens/Preferences/Accounts/Saved.jsx @@ -0,0 +1,40 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { toDataUrl } from 'ethereum-blockies' +import posed, { PoseGroup } from 'react-pose' +import { fadeIn } from '../../../components/Animations' +import styles from './Saved.module.css' + +const Item = posed.li(fadeIn) + +const Saved = ({ accounts, handleDelete }) => ( + + {accounts.map(account => { + const identicon = account && toDataUrl(account) + + return ( + +
    + Blockies + {account} +
    + + +
    + ) + })} +
    +) + +Saved.propTypes = { + accounts: PropTypes.array.isRequired, + handleDelete: PropTypes.func.isRequired +} + +export default Saved diff --git a/src/renderer/screens/Preferences/Accounts/Saved.module.css b/src/renderer/screens/Preferences/Accounts/Saved.module.css new file mode 100644 index 0000000..234853f --- /dev/null +++ b/src/renderer/screens/Preferences/Accounts/Saved.module.css @@ -0,0 +1,15 @@ +.identicon { + width: 1.5rem !important; + height: 1.5rem !important; + border-radius: 50%; + margin-right: 0.75rem; +} + +.delete { + composes: button from './index.module.css'; + position: relative; + font-size: 2rem; + top: -0.2rem; + color: #41474e; + transition: color 0.5s ease-out; +} diff --git a/src/renderer/screens/Preferences/Accounts.jsx b/src/renderer/screens/Preferences/Accounts/index.jsx similarity index 50% rename from src/renderer/screens/Preferences/Accounts.jsx rename to src/renderer/screens/Preferences/Accounts/index.jsx index 15eb4de..de556ec 100644 --- a/src/renderer/screens/Preferences/Accounts.jsx +++ b/src/renderer/screens/Preferences/Accounts/index.jsx @@ -1,70 +1,10 @@ import React, { PureComponent } from 'react' -import PropTypes from 'prop-types' -import { toDataUrl } from 'ethereum-blockies' -import posed, { PoseGroup } from 'react-pose' import Store from 'electron-store' import ethereum_address from 'ethereum-address' -import { AppContext } from '../../store/createContext' -import { fadeIn } from '../../components/Animations' - -const Item = posed.li(fadeIn) - -const AccountsList = ({ accounts, handleDelete }) => ( - - {accounts.map(account => { - const identicon = account && toDataUrl(account) - - return ( - -
    - Blockies - {account} -
    - - -
    - ) - })} -
    -) - -AccountsList.propTypes = { - accounts: PropTypes.array.isRequired, - handleDelete: PropTypes.func.isRequired -} - -const AccountNew = ({ input, handleInputChange, handleSave, accentColor }) => ( -
  • - handleInputChange(e)} - className="preference__input" - /> - - -
  • -) - -AccountNew.propTypes = { - input: PropTypes.string.isRequired, - handleInputChange: PropTypes.func.isRequired, - handleSave: PropTypes.func.isRequired, - accentColor: PropTypes.string.isRequired -} +import { AppContext } from '../../../store/createContext' +import Saved from './Saved' +import New from './New' +import styles from './index.module.css' export default class Accounts extends PureComponent { static contextType = AppContext @@ -99,7 +39,9 @@ export default class Accounts extends PureComponent { this.setState({ error: 'Address already added. Try another one.' }) return } else if (!isAddress) { - this.setState({ error: 'Not an Ethereum address. Try another one.' }) + this.setState({ + error: 'Not an Ethereum address. Try another one.' + }) return } else { const joined = [...accounts, input] @@ -131,22 +73,22 @@ export default class Accounts extends PureComponent { const { accounts, input, error } = this.state return ( -
    -

    Accounts

    -

    +

    +

    Accounts

    +

    Add Ethereum account addresses holding Ocean Tokens.

    -
      - +
        + -
      - {error !== '' &&
      {error}
      } + {error !== '' &&
      {error}
      }
    ) } diff --git a/src/renderer/screens/Preferences/Accounts/index.module.css b/src/renderer/screens/Preferences/Accounts/index.module.css new file mode 100644 index 0000000..eb1403e --- /dev/null +++ b/src/renderer/screens/Preferences/Accounts/index.module.css @@ -0,0 +1,62 @@ +.preference { + composes: box from '../../../components/Box.module.css'; + -webkit-app-region: none; + -webkit-user-select: text; +} + +.title, +.help { + display: inline-block; + margin-top: 0; + margin-bottom: 0.5rem; +} + +.title { + font-size: 1.2rem; +} + +.help { + color: #8b98a9; + margin-left: 0.5rem; +} + +.error { + font-size: 0.9rem; +} + +.list { + padding: 0; + border-top: 1px solid #e2e2e2; +} + +:global(.dark) .list { + border-top-color: #303030; +} + +.list li, +.list li > div { + display: flex; + align-items: center; +} + +.list li { + list-style: none; + justify-content: space-between; + border-bottom: 1px solid #e2e2e2; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +:global(.dark) .list li { + border-bottom-color: #303030; +} + +.button { + background: none; + border: 0; + box-shadow: none; + margin: 0; + padding: 0; + outline: 0; + font-size: 1rem; +} diff --git a/src/renderer/screens/Preferences/index.css b/src/renderer/screens/Preferences/index.css deleted file mode 100644 index 2eade08..0000000 --- a/src/renderer/screens/Preferences/index.css +++ /dev/null @@ -1,114 +0,0 @@ -.preferences { - text-align: left; - width: 100%; - position: relative; - padding-bottom: 4rem; -} - -.preferences__title { - width: 100%; - font-size: 2.2rem; - margin-top: -1rem; - margin-bottom: 2rem; -} - -.preferences__close { - text-decoration: none; - font-size: 2rem; - position: absolute; - top: 0; - right: 0; - color: #8b98a9; -} - -.preference__list { - padding: 0; - border-top: 1px solid #e2e2e2; -} - -.dark .preference__list { - border-top-color: #303030; -} - -.preference__list li, -.preference__list li > div { - display: flex; - align-items: center; -} - -.preference__list li { - list-style: none; - justify-content: space-between; - border-bottom: 1px solid #e2e2e2; - padding-top: .25rem; - padding-bottom: .25rem; -} - -.dark .preference__list li { - border-bottom-color: #303030; -} - -.preferences button { - background: none; - border: 0; - box-shadow: none; - margin: 0; - padding: 0; - outline: 0; - font-size: 1rem; -} - -button.delete { - position: relative; - font-size: 2rem; - top: -.2rem; - color: #41474e; - transition: color .5s ease-out; -} - -.preference { - -webkit-app-region: none; - -webkit-user-select: text; -} - -.preference__title, -.preference__help { - display: inline-block; - margin-top: 0; - margin-bottom: .5rem; -} - -.preference__title { - font-size: 1.2rem; -} - -.preference__help { - color: #8b98a9; - margin-left: .5rem; -} - -.preference .identicon { - width: 1.5rem !important; - height: 1.5rem !important; - border-radius: 50%; - margin-right: .75rem; -} - -.preference__input { - font-size: 1rem; - outline: 0; - background: none; - border: 0; - width: 80%; - color: #303030; - margin-top: .75rem; - margin-bottom: .75rem; -} - -.dark .preference__input { - color: #fff; -} - -.preference__error { - font-size: .9rem; -} diff --git a/src/renderer/screens/Preferences/index.jsx b/src/renderer/screens/Preferences/index.jsx index 14da3c6..86a389c 100644 --- a/src/renderer/screens/Preferences/index.jsx +++ b/src/renderer/screens/Preferences/index.jsx @@ -1,20 +1,16 @@ -import React, { PureComponent } from 'react' +import React from 'react' import { Link } from '@reach/router' - -import './index.css' - import Accounts from './Accounts' +import styles from './index.module.css' -export default class Preferences extends PureComponent { - render() { - return ( -
    -

    Preferences

    {' '} - - × - - -
    - ) - } -} +const Preferences = () => ( +
    +

    Preferences

    {' '} + + × + + +
    +) + +export default Preferences diff --git a/src/renderer/screens/Preferences/index.module.css b/src/renderer/screens/Preferences/index.module.css new file mode 100644 index 0000000..c2aa751 --- /dev/null +++ b/src/renderer/screens/Preferences/index.module.css @@ -0,0 +1,21 @@ +.preferences { + text-align: left; + width: 100%; + position: relative; +} + +.title { + width: 100%; + font-size: 2.2rem; + margin-top: -1rem; + margin-bottom: 2rem; +} + +.close { + text-decoration: none; + font-size: 2rem; + position: absolute; + top: 0; + right: 0; + color: #8b98a9; +} diff --git a/webpack.common.config.js b/webpack.common.config.js index 2137cb2..69bc6cc 100644 --- a/webpack.common.config.js +++ b/webpack.common.config.js @@ -24,11 +24,12 @@ module.exports = { }, { test: /\.css$/, + exclude: /\.module\.css$/, use: ['style-loader', 'css-loader'], include: defaultInclude }, { - test: /\.module\.(scss|sass)$/, + test: /\.module\.css$/, include: defaultInclude, loader: [ isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, @@ -41,26 +42,6 @@ module.exports = { localsConvention: 'camelCase', sourceMap: isDevelopment } - }, - { - loader: 'sass-loader', - options: { - sourceMap: isDevelopment - } - } - ] - }, - { - test: /\.(scss|sass)$/, - exclude: /\.module.(scss|sass)$/, - loader: [ - isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, - 'css-loader', - { - loader: 'sass-loader', - options: { - sourceMap: isDevelopment - } } ] }, @@ -77,7 +58,7 @@ module.exports = { ] }, resolve: { - extensions: ['*', '.js', '.jsx', '.scss'] + extensions: ['*', '.js', '.jsx', '.css'] }, target: 'electron-renderer', plugins: [