diff --git a/.editorconfig b/.editorconfig index 6aaa7cf6d9..47a1c671e6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,4 +5,4 @@ indent_size = 4 end_of_line = lf indent_style = tab insert_final_newline = true -trim_trailing_whitespace = true \ No newline at end of file +trim_trailing_whitespace = true diff --git a/.eslintignore b/.eslintignore index a141f0e729..faff5f92ac 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,7 +1,7 @@ -src/pwa/* public/ +src/tailwind.config.js src/resources/* +src/i18n dist/ build/ coverage/ -src/i18n diff --git a/.eslintrc.js b/.eslintrc.js index 2707ddf90b..42c9af83d6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -11,6 +11,7 @@ module.exports = { env: { browser: true, node: true, + es6: true, }, plugins: ["@typescript-eslint", "prettier", "testing-library"], extends: [ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..d6dd75272f --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) Ark Ecosystem + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/package.json b/package.json index d7e8b189f1..fce78a34a6 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,22 @@ "url": "https://github.com/ArkEcosystem/desktop-wallet/issues" }, "homepage": "./", - "main": "public/electron.js", + "main": "src/electron/index.js", + "scripts": { + "dev": "concurrently \"BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\"", + "build": "react-app-rewired build", + "format": "yarn lint && yarn prettier", + "lint": "eslint '*/**/*.{js,ts,tsx}' --quiet --fix", + "prettier": "prettier --write \"./*.{ts,js,json,md,vue}\" \"./**/*.{ts,js,json,md,vue}\"", + "storybook": "start-storybook -p 5000", + "start": "react-app-rewired start", + "test": "react-app-rewired test", + "test:coverage": "react-app-rewired test --coverage --watchAll=false", + "eject": "react-app-rewired eject", + "build:mac": "yarn build && electron-builder --mac --publish never", + "build:linux": "yarn build && electron-builder --linux --publish never", + "build:win": "yarn build && electron-builder --win --x64 --ia32 --publish never" + }, "dependencies": { "@testing-library/jest-dom": "^5.9.0", "@testing-library/react": "^9.3.2", @@ -53,43 +68,6 @@ "twin.macro": "^1.3.0", "typescript": "~3.7.2" }, - "scripts": { - "dev": "concurrently \"BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\"", - "build": "react-app-rewired build", - "format": "yarn lint && yarn prettier", - "lint": "eslint '*/**/*.{js,ts,tsx}' --quiet --fix", - "prettier": "prettier --write \"./*.{ts,js,json,md,vue}\" \"./**/*.{ts,js,json,md,vue}\"", - "storybook": "start-storybook -p 5000", - "start": "react-app-rewired start", - "test": "react-app-rewired test", - "test:coverage": "react-app-rewired test --coverage --watchAll=false", - "eject": "react-app-rewired eject", - "build:mac": "yarn build && electron-builder --mac --publish never", - "build:linux": "yarn build && electron-builder --linux --publish never", - "build:win": "yarn build && electron-builder --win --x64 --ia32 --publish never" - }, - "babelMacros": { - "twin": { - "preset": "styled-components", - "config": "src/tailwind.config.js", - "autoCssProp": true - } - }, - "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" - ] - }, "devDependencies": { "@storybook/addon-actions": "^5.3.19", "@storybook/addon-knobs": "^5.3.19", @@ -125,6 +103,28 @@ "tailwindcss": "^1.4.6", "wait-on": "^5.0.0" }, + "babelMacros": { + "twin": { + "preset": "styled-components", + "config": "src/tailwind.config.js", + "autoCssProp": true + } + }, + "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" + ] + }, "husky": { "hooks": { "pre-commit": "lint-staged" diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index bcd5dfd67c..0000000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/public/index.html b/public/index.html index 3e1257b0b3..c4ad843514 100644 --- a/public/index.html +++ b/public/index.html @@ -2,12 +2,7 @@ - - - - - ARK Desktop Wallet diff --git a/public/manifest.json b/public/manifest.json deleted file mode 100644 index 7c866422a9..0000000000 --- a/public/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "short_name": "React App", - "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/public/robots.txt b/public/robots.txt deleted file mode 100644 index e9e57dc4d4..0000000000 --- a/public/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * -Disallow: diff --git a/src/electron/index.js b/src/electron/index.js new file mode 100644 index 0000000000..15f7bb6fad --- /dev/null +++ b/src/electron/index.js @@ -0,0 +1,124 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +const electron = require("electron"); +const path = require("path"); +const isDev = require("electron-is-dev"); +const winState = require("electron-window-state"); +const assignMenu = require("./menu"); + +const { BrowserWindow, app, screen, ipcMain } = electron; + +const windows = {}; +let mainWindow; +let windowState = null; +let deeplinkingUrl = null; + +const winURL = isDev ? "http://localhost:3000" : `file://${path.join(__dirname, "../../build/index.html")}`; + +const installExtensions = async () => { + const installer = require("electron-devtools-installer"); + const forceDownload = !!process.env.UPGRADE_EXTENSIONS; + const extensions = ["REACT_DEVELOPER_TOOLS"]; + + return Promise.all(extensions.map((name) => installer.default(installer[name], forceDownload))).catch(console.log); +}; + +function broadcastURL(url) { + if (!url || typeof url !== "string") { + return; + } + + if (mainWindow && mainWindow.webContents) { + mainWindow.webContents.send("process-url", url); + deeplinkingUrl = null; + } +} + +ipcMain.on("disable-iframe-protection", function (_event, urls) { + const filter = { urls }; + windows.main.webContents.session.webRequest.onHeadersReceived(filter, (details, done) => { + const headers = details.responseHeaders; + + const xFrameOrigin = Object.keys(headers).find((header) => header.toString().match(/^x-frame-options$/i)); + if (xFrameOrigin) { + delete headers[xFrameOrigin]; + } + + done({ + cancel: false, + responseHeaders: headers, + statusLine: details.statusLine, + }); + }); +}); + +function createWindow() { + const { width, height } = screen.getPrimaryDisplay().workAreaSize; + + windowState = winState({ + defaultWidth: width, + defaultHeight: height, + fullScreen: false, + }); + + mainWindow = new BrowserWindow({ + width: windowState.width, + height: windowState.height, + x: windowState.x, + y: windowState.y, + backgroundColor: "#f7fafb", + center: true, + show: true, + webPreferences: { + nodeIntegration: true, + webviewTag: true, + }, + }); + + mainWindow.isMain = true; + + windowState.manage(mainWindow); + mainWindow.loadURL(winURL); + mainWindow.setBackgroundColor("#f7fafb"); + + mainWindow.on("close", () => (mainWindow = null)); + mainWindow.on("closed", () => (mainWindow = null)); + + mainWindow.webContents.on("did-finish-load", () => { + const version = app.getVersion(); + const windowTitle = `ARK Desktop Wallet ${version}`; + mainWindow.setTitle(windowTitle); + + broadcastURL(deeplinkingUrl); + }); + + if (isDev) { + installExtensions() + .then(() => mainWindow.webContents.openDevTools()) + .catch((error) => console.error(error)); + } +} + +assignMenu({ createWindow }); + +app.on("ready", createWindow); + +app.on("window-all-closed", () => { + if (process.platform !== "darwin") { + app.quit(); + } +}); + +app.on("activate", () => { + if (mainWindow === null) { + createWindow(); + } +}); + +app.on("open-url", (event, url) => { + // Protocol handler for osx + event.preventDefault(); + deeplinkingUrl = url; + broadcastURL(deeplinkingUrl); +}); + +app.setAsDefaultProtocolClient("ark", process.execPath, ["--"]); diff --git a/src/electron/menu.js b/src/electron/menu.js new file mode 100644 index 0000000000..18e626b0b7 --- /dev/null +++ b/src/electron/menu.js @@ -0,0 +1,136 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +const { Menu, shell } = require("electron"); +const aboutWindow = require("about-window").default; +const path = require("path"); +const packageJson = require("../../package.json"); + +const isProduction = process.env.NODE_ENV === "production"; + +const latestReleaseUrl = () => { + const [, project] = packageJson.repository.url.match(/github.com\/(.*)\.git$/); + return `https://github.com/${project}/releases/latest`; +}; + +module.exports = function () { + const about = { + label: "About", + click: () => + aboutWindow({ + adjust_window_size: true, + icon_path: isProduction + ? path.resolve(__dirname, "./static/128x128.png") + : path.resolve(__dirname, "../resources/assets/icons/128x128.png"), + copyright: [ + `

Distributed under ${packageJson.license} license

`, + '

Flag icons made by Freepik from flaticon.com are licensed by CC 3.0 BY

', + ], + package_json_dir: path.resolve(__dirname, "../../"), + css_path: isProduction ? path.resolve(__dirname, "styles.css") : null, + use_inner_html: true, + }), + }; + + const template = [ + { + label: "File", + submenu: [{ role: "quit" }], + }, + { + label: "Edit", + submenu: [ + { role: "undo" }, + { role: "redo" }, + { type: "separator" }, + { role: "cut" }, + { role: "copy" }, + { role: "paste" }, + { role: "pasteandmatchstyle" }, + { role: "delete" }, + { role: "selectall" }, + ], + }, + { + label: "View", + submenu: [ + { role: "reload" }, + { role: "forceReload" }, + { role: "toggledevtools" }, + { type: "separator" }, + { role: "resetzoom" }, + { role: "zoomin" }, + { role: "zoomout" }, + { type: "separator" }, + { role: "togglefullscreen" }, + ], + }, + { + role: "window", + submenu: [{ role: "minimize" }, { role: "close" }], + }, + { + role: "help", + submenu: [ + { + label: "Learn More", + click() { + shell.openExternal("https://ark.io"); + }, + }, + { + label: `Version ${packageJson.version}`, + click() { + shell.openExternal(latestReleaseUrl()); + }, + }, + ], + }, + ]; + + if (process.platform === "darwin") { + // File menu + template[0] = { + role: "appMenu", + label: packageJson.build.productName, + submenu: [ + about, + { type: "separator" }, + { type: "separator" }, + { role: "services", submenu: [] }, + { type: "separator" }, + { + label: "Hide", + role: "hide", + }, + { role: "hideothers" }, + { role: "unhide" }, + { type: "separator" }, + { + label: "Quit", + role: "quit", + }, + ], + }; + + // Edit menu + template[1].submenu.push( + { type: "separator" }, + { + label: "Speech", + submenu: [{ role: "startspeaking" }, { role: "stopspeaking" }], + }, + ); + + // Window menu + template[3].submenu = [ + { role: "close" }, + { role: "minimize" }, + { role: "zoom" }, + { type: "separator" }, + { role: "front" }, + ]; + } else { + template[4].submenu.unshift(about, { type: "separator" }); + } + + Menu.setApplicationMenu(Menu.buildFromTemplate(template)); +}; diff --git a/src/tailwind.config.js b/src/tailwind.config.js index 56d46ccd11..5c2fb2eda5 100644 --- a/src/tailwind.config.js +++ b/src/tailwind.config.js @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable no-undef */ const defaultConfig = require("tailwindcss/defaultConfig"); const tailwindUI = require("@tailwindcss/ui");