diff --git a/web/cashtab/.gitignore b/web/cashtab/.gitignore index 8bbeb8790..99dcb0296 100644 --- a/web/cashtab/.gitignore +++ b/web/cashtab/.gitignore @@ -1,30 +1,29 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js # testing /coverage # production /build -/dist -/extension # misc .DS_Store .env.local .env.development.local .env.test.local .env.production.local npm-debug.log* yarn-debug.log* yarn-error.log* # Chrome Extension testing *.crx *.zip -*.pem \ No newline at end of file +*.pem +dist/ \ No newline at end of file diff --git a/web/cashtab/README.md b/web/cashtab/README.md index 6dd553f93..fe6a02533 100644 --- a/web/cashtab/README.md +++ b/web/cashtab/README.md @@ -1,77 +1,86 @@ # CashTab ## Bitcoin Cash Web Wallet ### Features - Send & Receive BCH - Import existing wallets ## Development CashTab relies on some modules that retain legacy dependencies. NPM version 7 or later no longer supports automatic resolution of these peer dependencies. To successfully install modules such as `qrcode.react`, with NPM > 7, run `npm install` with the flag `--legacy-peer-deps` ``` npm install --legacy-peer-deps npm start ``` Runs the app in the development mode.
Open [http://localhost:3000](http://localhost:3000) to view it in the browser. The page will reload if you make edits.
You will also see any lint errors in the console. ## Testing ### 'npm test' ### 'npm run test:coverage' ## Production In the project directory, run: ### `npm run build` Builds the app for production to the `build` folder.
It correctly bundles React in production mode and optimizes the build for the best performance. The build is minified and the filenames include the hashes.
Your app is ready to be deployed! See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. +## Browser Extension + +1. `npm run extension` +2. Open Chrome or Brave +3. Navigate to `chrome://extensions/` (or `brave://extensions/`) +4. Enable Developer Mode +5. Click "Load unpacked" +6. Select the `extension/dist` folder that was created with `npm run extension` + ## Docker deployment ``` npm install --legacy-peer-deps docker-compose build docker-compose up ``` ## Redundant APIs CashTab accepts multiple instances of `bch-api` as its backend. Input your desired API URLs separated commas into the `REACT_APP_BCHA_APIS` variable. For example, to run CashTab with three redundant APIs, use: ``` REACT_APP_BCHA_APIS=https://rest.kingbch.com/v3/,https://wallet-service-prod.bitframe.org/v3/,https://free-main.fullstack.cash/v3/ ``` You can also run CashTab with a single API, e.g. ``` REACT_APP_BCHA_APIS=https://rest.kingbch.com/v3/ ``` CashTab will start with the first API in your list. If it receives an error from that API, it will try the next one. Navigate to `localhost:8080` to see the app. ## CashTab Roadmap The following features are under active development: - Transaction history - Simple Ledger Postage Protocol Support - CashTab browser extension diff --git a/web/cashtab/extension/README.md b/web/cashtab/extension/README.md new file mode 100644 index 000000000..3f357de96 --- /dev/null +++ b/web/cashtab/extension/README.md @@ -0,0 +1,9 @@ +# CashTab extension + +Some minor but important code changes are required to build CashTab as a browser extension. + +1. Add option to open extension in tab +2. Unique format of manifest.json with sha256 hash of any external scripts +3. CSS rules for extension pop-up sizing + +Source files unique to the browser extension are kept in the `extension/` directory. diff --git a/web/cashtab/extension/public/manifest.json b/web/cashtab/extension/public/manifest.json new file mode 100644 index 000000000..6de20bd5d --- /dev/null +++ b/web/cashtab/extension/public/manifest.json @@ -0,0 +1,20 @@ +{ + "manifest_version": 2, + + "name": "CashTab", + "description": "A browser-integrated BCHA wallet from Bitcoin ABC", + "version": "0.0.1", + + "browser_action": { + "default_popup": "index.html", + "default_title": "CashTab" + }, + "icons": { + "16": "bch16.png", + "48": "bch48.png", + "128": "bch128.png", + "192": "bch192.png", + "512": "bch512.png" + }, + "content_security_policy": "script-src 'self' https://unpkg.com/minimal-slp-wallet-web-joey 'sha256-03cee7e881f6cdd32ce620d5787d7e6a0eaef8acc55557bded61dd6ad82ff0e6'; object-src 'self'" +} diff --git a/web/cashtab/extension/src/assets/popout.svg b/web/cashtab/extension/src/assets/popout.svg new file mode 100644 index 000000000..dd9fa471e --- /dev/null +++ b/web/cashtab/extension/src/assets/popout.svg @@ -0,0 +1,21 @@ + + + + popout + Created with Sketch. + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/cashtab/extension/src/components/App.css b/web/cashtab/extension/src/components/App.css new file mode 100644 index 000000000..c41be5ad4 --- /dev/null +++ b/web/cashtab/extension/src/components/App.css @@ -0,0 +1,411 @@ +@import '~antd/dist/antd.less'; +@import '~@fortawesome/fontawesome-free/css/all.css'; +@import url('https://fonts.googleapis.com/css?family=Khula&display=swap&.css'); + +@font-face { + font-family: 'Roboto Mono'; + src: local('Roboto Mono'), + url(../assets/fonts/RobotoMono-Regular.ttf) format('truetype'); + font-weight: normal; +} + +aside::-webkit-scrollbar { + width: 0.3em; +} +aside::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px #13171f; +} +aside::-webkit-scrollbar-thumb { + background-color: darkgrey; + outline: 1px solid slategrey; +} + +/* Hide up and down arros on input type="number" */ +/* Chrome, Safari, Edge, Opera */ +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +/* Hide up and down arros on input type="number" */ +/* Firefox */ +input[type='number'] { + -moz-appearance: textfield; +} + +html, +body { + min-width: 320px; + min-height: 600px; + max-width: 100%; + overflow-x: hidden; +} + +.ant-modal-wrap.ant-modal-centered::-webkit-scrollbar { + display: none; +} + +.App { + text-align: center; + font-family: 'Gilroy', sans-serif; + background-color: #fbfbfd; +} +.App-logo { + width: 100%; + display: block; +} + +.logo img { + width: 100%; + min-width: 193px; + display: block; + padding-left: 24px; + padding-right: 34px; + padding-top: 24px; + max-width: 200px; +} +.ant-list-item-meta .ant-list-item-meta-content { + display: flex; +} + +#react-qrcode-logo { + border-radius: 8px; +} +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #f59332; +} +.ant-menu-item-group-title { + padding-left: 30px; + font-size: 20px !important; + font-weight: 500 !important; +} + +.ant-menu-item > span { + font-size: 14px !important; + font-weight: 500 !important; +} + +.ant-card-actions > li > span:hover, +.ant-btn:hover, +.ant-btn:focus { + color: #f59332; + transition: color 0.3s; + background-color: white; +} + +.ant-card-actions > li { + color: #3e3f42; +} +.anticon { + color: #3e3f42; +} +.ant-list-item-meta-description, +.ant-list-item-meta-title { + color: #3e3f42; +} + +.ant-list-item-meta-description > :first-child { + right: 20px !important; + position: absolute; +} + +.ant-modal-body .ant-list-item-meta { + height: 85px; + width: 85px; + padding-left: 10px; + padding-top: 10px; + padding-bottom: 20px; + overflow: visible !important; +} + +/* .ant-radio-group-solid .ant-radio-button-wrapper { + margin-top: 0px; +} + +.ant-radio-group-solid .ant-radio-button-wrapper-checked { + border: none !important; + box-shadow: none !important; +} */ +.identicon { + border-radius: 50%; + width: 200px; + height: 200px; + margin-top: -75px; + margin-left: -75px; + margin-bottom: 20px; + box-shadow: 1px 1px 2px 1px #444; +} +.ant-list-item-meta { + width: 40px; + height: 40px; +} + +/* .ant-radio-group-solid .ant-radio-button-wrapper-checked { + background: #ff8d00 !important; +} + +.ant-radio-group.ant-radio-group-solid.ant-radio-group-small { + font-size: 14px !important; + font-weight: 600 !important; + vertical-align: middle; + border-radius: 100px; + overflow: auto; + background: rgba(255, 255, 255, 0.5) !important; + margin-top: 14px; + margin-bottom: 10px; + cursor: pointer; +} */ + +input.ant-input, +.ant-select-selection { + background-color: #fff !important; + box-shadow: none !important; + border: 1px solid #eaedf3 !important; + border-radius: 4px; + font-weight: bold; + color: rgb(62, 63, 66); + opacity: 1; + padding: 11px 5px; + height: 50px; +} + +.ant-select-selection:hover { + border: 1px solid #eaedf3; +} + +.ant-select-selection-selected-value { + color: rgb(62, 63, 66); +} + +.ant-select-dropdown-menu-item { + color: #444; + background-color: #fff; +} + +.ant-select-dropdown-menu-item-active, +.ant-select-dropdown-menu-item:hover { + color: #fff; + background-color: #ff8d00 !important; +} + +.ant-checkbox-inner { + border: 1px solid #eaedf3 !important; + background: white; +} + +.ant-checkbox-inner::after { + border-color: white !important; +} + +.ant-card-bordered { + border: 1px solid rgb(234, 237, 243); + border-radius: 8px; +} + +.ant-card-actions { + border-top: 1px solid rgb(234, 237, 243); + border-bottom: 1px solid rgb(234, 237, 243); + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + box-shadow: 0px 5px 8px rgba(0, 0, 0, 0.35); +} + +.ant-input-group-addon { + background-color: #f4f4f4 !important; + border: 1px solid rgb(234, 237, 243); + color: #3e3f42 !important; + + * { + color: #3e3f42 !important; + } +} + +.ant-menu-item.ant-menu-item-selected > * { + color: #fff !important; +} + +.ant-menu-item.ant-menu-item-selected { + border: 0; + overflow: hidden; + text-align: left; + padding-left: 28px; + background-color: rgba(255, 255, 255, 0.2) !important; +} + +.ant-btn { + border-radius: 8px; + background-color: #fff; + color: rgb(62, 63, 66); + font-weight: bold; +} + +.ant-card-actions > li:not(:last-child) { + border-right: 0; +} +.ant-list-item-meta-avatar > img { + margin-left: -12px; + transform: translate(0, -6px); +} + +.ant-list-item-meta-avatar > svg { + margin-right: -70px; +} + +/* Removing these for ABC SLP warning +.ant-alert-warning { + background-color: #20242d; + border: 1px solid #17171f; + border-radius: 0; +} + +.ant-alert-message { + color: #fff; +} +*/ + +.ant-layout-sider-dark { + background: linear-gradient(0deg, #040c3c, #212c6e); +} + +.ant-menu-dark { + background: none; +} + +.ant-layout-sider-zero-width-trigger.ant-layout-sider-zero-width-trigger-left + .anticon.anticon-bars { + color: #fff; + transform: scale(1.3); +} + +.ant-layout-sider-zero-width-trigger.ant-layout-sider-zero-width-trigger-left { + background: #3e3f42; + border-radius: 0 8px 8px 0; +} + +.ant-btn-group .ant-btn-primary:first-child:not(:last-child) { + border-right-color: transparent !important; +} + +.ant-btn-group .ant-btn-primary:last-child:not(:first-child), +.ant-btn-group .ant-btn-primary + .ant-btn-primary { + border-left-color: #20242d !important; +} + +.audit { + a, + a:active { + color: #46464a; + } + + a:hover { + color: #111117; + } +} + +.dividends { + a, + a:active { + color: #111117; + } + + a:hover { + color: #46464a; + } +} + +.ant-popover-inner-content { + color: white; +} + +.ant-modal-body .ant-card { + max-width: 100%; +} + +.ant-upload.ant-upload-drag { + border: 1px solid #eaedf3; + border-radius: 8px; + background: #d3d3d3; +} + +.ant-upload-list-item:hover .ant-upload-list-item-info { + background-color: #ffffff; +} + +/* .ant-radio-button-wrapper { + border: none; +} + +.ant-radio-button-wrapper-checked { + border-radius: none !important; +} */ + +/* .ant-radio-button-wrapper:first-child, .ant-radio-button-wrapper:last-child { + border-radius: 0 0 0 0; +} */ + +.ant-radio-group { + width: 100%; + margin-top: 10px; +} + +.ant-radio-button-wrapper { + background: rgba(255, 255, 255, 0.2); + width: 104px; + border: none; + text-align: center; + color: #fff; +} + +.ant-radio-button-wrapper:hover { + color: #fff; + background: rgba(255, 255, 255, 0.3); +} + +.ant-radio-group-small .ant-radio-button-wrapper { + height: 35px; + line-height: 35px; +} + +.ant-radio-button-wrapper-checked { + background: #ff8d00 !important; + border: none !important; +} + +.ant-radio-button-wrapper:first-child { + border-radius: 100px 0 0 100px; +} + +.ant-radio-button-wrapper:last-child { + border-radius: 0 100px 100px 0; +} + +::selection { + background-color: #ff8d00; +} + +@media (max-width: 768px) { + .ant-notification { + width: 100%; + top: 20px !important; + max-width: unset; + margin-right: 0; + } +} + +@media (max-width: 350px) { + .ant-select-selection-selected-value { + font-size: 10px; + } +} diff --git a/web/cashtab/extension/src/components/App.js b/web/cashtab/extension/src/components/App.js new file mode 100644 index 000000000..b86d2e024 --- /dev/null +++ b/web/cashtab/extension/src/components/App.js @@ -0,0 +1,278 @@ +import React from 'react'; +import 'antd/dist/antd.less'; +import '../index.css'; +import styled from 'styled-components'; +import { Layout, Tabs, Icon } from 'antd'; +import Wallet from './Wallet/Wallet'; +import Send from './Send/Send'; +import SendToken from './Send/SendToken'; +import Configure from './Configure/Configure'; +import NotFound from './NotFound'; +import CashTab from '../assets/cashtab.png'; +import PopOut from '../assets/popout.svg'; +import './App.css'; +import { WalletContext } from '../utils/context'; +import { + Route, + Redirect, + Switch, + useLocation, + useHistory, +} from 'react-router-dom'; + +const { Footer } = Layout; +const { TabPane } = Tabs; + +const OpenInTabBtn = styled.button` + background: none; + border: none; +`; +const ExtTabImg = styled.img` + max-width: 20px; +`; + +const StyledTabsMenu = styled.div` + .ant-layout-footer { + position: absolute; + bottom: 0; + width: 100%; + padding: 0; + background-color: #fff; + left: 0; + border-radius: 20px; + border-top: 1px solid #e2e2e2; + @media (max-width: 768px) { + position: fixed; + } + } + .ant-tabs-nav .ant-tabs-tab { + padding: 30px 0 20px 0; + } + .ant-tabs-bar.ant-tabs-bottom-bar { + margin-top: 0; + border-top: none; + } + .ant-tabs-tab { + span { + font-size: 12px; + display: grid; + font-weight: bold; + } + .anticon { + color: rgb(148, 148, 148); + font-size: 24px; + margin-left: 8px; + margin-bottom: 3px; + } + } + .ant-tabs-tab:hover { + color: #ff8d00 !important; + .anticon { + color: #ff8d00; + } + } + .ant-tabs-tab-active.ant-tabs-tab { + color: #ff8d00; + .anticon { + color: #ff8d00; + } + } + .ant-tabs-tab-active.ant-tabs-tab { + color: #ff8d00; + .anticon { + color: #ff8d00; + } + } + .ant-tabs-tab-active:active { + color: #ff8d00 !important; + } + .ant-tabs-ink-bar { + display: none !important; + } + .ant-tabs-nav { + margin: -3.5px 0 0 0; + } +`; + +export const WalletBody = styled.div` + display: flex; + align-items: center; + justify-content: center; + width: 100%; + min-height: 100vh; + background: linear-gradient(270deg, #040c3c, #212c6e); +`; + +export const WalletCtn = styled.div` + position: relative; + width: 500px; + background-color: #fff; + min-height: 100vh; + padding-top: 30px; + padding: 10px 30px 100px 30px; + background: #fff; + -webkit-box-shadow: 0px 0px 24px 1px rgba(0, 0, 0, 1); + -moz-box-shadow: 0px 0px 24px 1px rgba(0, 0, 0, 1); + box-shadow: 0px 0px 24px 1px rgba(0, 0, 0, 1); + @media (max-width: 768px) { + width: 100%; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } +`; + +export const HeaderCtn = styled.div` + display: flex; + align-items: center; + justify-content: center; + width: 100%; + padding: 20px 0 30px; + margin-bottom: 20px; + justify-content: space-between; + border-bottom: 1px solid #e2e2e2; + + a { + color: #848484; + + :hover { + color: #ff8d00; + } + } + + @media (max-width: 768px) { + a { + font-size: 12px; + } + padding: 10px 0 20px; + } +`; + +export const CashTabLogo = styled.img` + width: 120px; + @media (max-width: 768px) { + width: 110px; + } +`; +export const AbcLogo = styled.img` + width: 150px; + @media (max-width: 768px) { + width: 120px; + } +`; + +const App = () => { + const ContextValue = React.useContext(WalletContext); + const { wallet } = ContextValue; + const location = useLocation(); + const history = useHistory(); + const selectedKey = + location && location.pathname ? location.pathname.substr(1) : ''; + const openInTab = () => { + window.open(`index.html#/${selectedKey}`); + }; + + return ( +
+ + + + + openInTab()} + > + + + + + + + + + + + ( + + )} + /> + + + + + + + + {wallet ? ( + + + + ) : null} + + +
+ ); +}; + +export default App; diff --git a/web/cashtab/package.json b/web/cashtab/package.json index c73f243e2..56a3169b8 100644 --- a/web/cashtab/package.json +++ b/web/cashtab/package.json @@ -1,187 +1,188 @@ { "name": "cashtab", "version": "0.1.8", "private": true, "homepage": "https://cashtabapp.com/", "dependencies": { "@ant-design/dark-theme": "^1.0.3", "@ant-design/icons": "^4.2.2", "@babel/core": "7.6.0", "@fortawesome/fontawesome-free": "^5.15.1", "@psf/bch-js": "^3.9.1", "@svgr/webpack": "4.3.2", "@typescript-eslint/eslint-plugin": "^2.2.0", "@typescript-eslint/parser": "^2.2.0", "@zxing/library": "0.8.0", "antd": "^3.23.4", "babel-eslint": "10.0.3", "babel-jest": "^26.6.3", "babel-loader": "8.0.6", "babel-plugin-named-asset-import": "^0.3.4", "babel-preset-react-app": "^9.0.2", "big.js": "^5.2.2", "bignumber.js": "^9.0.0", "browser-image-compression": "^1.0.8", "camelcase": "^5.2.0", "case-sensitive-paths-webpack-plugin": "2.2.0", "core-util-is": "^1.0.2", "crypto-js": "^3.1.9-1", "css-loader": "2.1.1", "dotenv": "6.2.0", "dotenv-expand": "5.1.0", "eslint": "^6.1.0", "eslint-config-react-app": "^5.0.2", "eslint-loader": "3.0.0", "eslint-plugin-flowtype": "3.13.0", "eslint-plugin-import": "2.18.2", "eslint-plugin-jsx-a11y": "6.2.3", "eslint-plugin-react": "7.14.3", "eslint-plugin-react-hooks": "^1.6.1", "ethereum-blockies-base64": "^1.0.2", "file-loader": "3.0.1", "fs-extra": "7.0.1", "gh-pages": "^2.1.1", "html-webpack-plugin": "4.0.0-beta.5", "identity-obj-proxy": "3.0.0", "is-wsl": "^1.1.0", "jdenticon": "^2.2.0", "jest": "^24.9.0", "jest-environment-jsdom-fourteen": "0.1.0", "jest-resolve": "24.9.0", "jest-watch-typeahead": "0.4.0", "lint-staged": "^9.4.2", "localforage": "^1.9.0", "lodash": "^4.17.20", "mini-css-extract-plugin": "0.8.0", "optimize-css-assets-webpack-plugin": "5.0.3", "pnp-webpack-plugin": "1.5.0", "postcss-flexbugs-fixes": "4.1.0", "postcss-loader": "3.0.0", "postcss-normalize": "7.0.1", "postcss-preset-env": "6.7.0", "postcss-safe-parser": "4.0.1", "prettier": "^1.18.2", "qrcode.react": "^1.0.0", "react": "^16.10.0", "react-app-polyfill": "^1.0.3", "react-copy-to-clipboard": "^5.0.1", "react-dev-utils": "^9.0.4", "react-device-detect": "^1.14.0", "react-dom": "^16.10.0", "react-ga": "^3.2.1", "react-image": "^4.0.3", "react-jdenticon": "0.0.8", "react-router": "^5.1.1", "react-router-dom": "^5.1.1", "react-swipeable": "^5.5.0", "resolve": "1.12.0", "resolve-url-loader": "3.1.0", "sass-loader": "^10.0.4", "semver": "6.3.0", "style-loader": "1.0.0", "styled-components": "^4.4.0", "terser-webpack-plugin": "1.4.1", "ts-pnp": "1.1.4", "url-loader": "2.1.0", "webpack": "4.40.2", "webpack-dev-server": "^3.11.0", "webpack-manifest-plugin": "2.0.4" }, "lint-staged": { "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [ "prettier --write", "git add" ] }, "scripts": { "start": "node scripts/start.js", "build": "node scripts/build.js", + "extension": "./scripts/extension.sh", "predeploy": "npm run build", "test": "node scripts/test.js", "test:coverage": "yarn test --coverage --watchAll=false", "debug": "node --inspect-brk=9229 scripts/start.js" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "jest": { "roots": [ "/src" ], "collectCoverageFrom": [ "src/**/*.{js,jsx,ts,tsx}", "!src/**/*.d.ts" ], "coverageThreshold": { "global": { "branches": 15, "functions": 15, "lines": 15, "statements": 15 } }, "setupFiles": [ "react-app-polyfill/jsdom" ], "setupFilesAfterEnv": [], "testMatch": [ "/src/**/__tests__/**/*.{js,jsx,ts,tsx}", "/src/**/*.{spec,test}.{js,jsx,ts,tsx}" ], "testEnvironment": "jest-environment-jsdom-fourteen", "transform": { "^.+\\.(js|jsx|ts|tsx)$": "/node_modules/babel-jest", "^.+\\.css$": "/config/jest/cssTransform.js", "^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "/config/jest/fileTransform.js" }, "transformIgnorePatterns": [ "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$", "^.+\\.module\\.(css|sass|scss)$" ], "modulePaths": [], "moduleNameMapper": { "^react-native$": "react-native-web", "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy" }, "moduleFileExtensions": [ "web.js", "js", "web.ts", "ts", "web.tsx", "tsx", "json", "web.jsx", "jsx", "node" ], "watchPlugins": [ "jest-watch-typeahead/filename", "jest-watch-typeahead/testname" ] }, "babel": { "presets": [ "react-app" ] }, "devDependencies": { "@testing-library/react": "^11.1.0", "less": "^3.10.3", "less-loader": "^5.0.0", "react-test-renderer": "^17.0.0", "workbox-webpack-plugin": "^4.3.1" } } diff --git a/web/cashtab/scripts/extension.sh b/web/cashtab/scripts/extension.sh new file mode 100755 index 000000000..4bb6e4205 --- /dev/null +++ b/web/cashtab/scripts/extension.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +export LC_ALL=C +set -euo pipefail + +# Build CashTab as a Chrome/Brave browser extension + +# Create a working directory for stashing non-extension files +WORKDIR=$(mktemp -d) +echo Using workdir ${WORKDIR} + +# Delete workdir on script finish +function cleanup { + echo Deleting workdir ${WORKDIR} + rm -rf "${WORKDIR}" +} +trap cleanup EXIT + +# Stash web files that require extension changes in workdir +mv public/manifest.json ${WORKDIR} +mv src/components/App.js ${WORKDIR} +mv src/components/App.css ${WORKDIR} + +# Move extension src files into place for npm build +cp extension/src/assets/popout.svg src/assets/ +cp extension/public/manifest.json public/ +cp extension/src/components/App.js src/components/ +cp extension/src/components/App.css src/components/ + +# Delete the last extension build +if [ -d "extension/dist/" ]; then rm -Rf extension/dist/; fi + +# Build the extension +mkdir extension/dist/ +echo 'Building Extension...' + +# Required for extension build rules +export INLINE_RUNTIME_CHUNK=false +export GENERATE_SOURCEMAP=false + +npm run build + +# Copy extension build files to extension/ folder +cp -r build/* extension/dist + +# Delete extension build from build/ folder (reserved for web app builds) +rm -Rf build + +# Replace original web files +rm src/assets/popout.svg +rm public/manifest.json +rm src/components/App.js +rm src/components/App.css + +# Note, src/assets/popout.svg does not need to be replaced, not used by web app +mv ${WORKDIR}/manifest.json public/ +mv ${WORKDIR}/App.js src/components/ +mv ${WORKDIR}/App.css src/components/ + +echo 'Extension built and web files replaced!' \ No newline at end of file