diff --git a/.dockerignore b/.dockerignore index aa14b3ab..2fa3f648 100644 --- a/.dockerignore +++ b/.dockerignore @@ -21,7 +21,6 @@ node_modules release/app/dist release/build -.erb/dll .idea npm-debug.log.* diff --git a/.erb/configs/.eslintrc b/.erb/configs/.eslintrc deleted file mode 100644 index 3cdc0fd9..00000000 --- a/.erb/configs/.eslintrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "rules": { - "global-require": "off", - "import/no-dynamic-require": "off", - "import/no-extraneous-dependencies": "off", - "react/jsx-props-no-spreading": "off" - } -} diff --git a/.erb/configs/webpack.config.base.ts b/.erb/configs/webpack.config.base.ts deleted file mode 100644 index dc2c8ea0..00000000 --- a/.erb/configs/webpack.config.base.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Base webpack config used across other specific configs - */ - -import webpack from 'webpack'; -import webpackPaths from './webpack.paths'; -import { dependencies as externals } from '../../release/app/package.json'; - -const configuration: webpack.Configuration = { - externals: [...Object.keys(externals || {})], - - stats: 'errors-only', - - module: { - rules: [ - { - test: /\.[jt]sx?$/, - exclude: /node_modules/, - use: { - loader: 'ts-loader', - options: { - // Remove this line to enable type checking in webpack builds - transpileOnly: true, - }, - }, - }, - ], - }, - - output: { - path: webpackPaths.srcPath, - // https://github.com/webpack/webpack/issues/1114 - library: { - type: 'commonjs2', - }, - }, - - /** - * Determine the array of extensions that should be used to resolve modules. - */ - resolve: { - extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'], - modules: [webpackPaths.srcPath, 'node_modules'], - fallback: - { - 'stream': require.resolve('stream-browserify'), - 'crypto': require.resolve('crypto-browserify') - } - }, - - plugins: [ - new webpack.EnvironmentPlugin({ - NODE_ENV: 'production', - }), - ], -}; - -export default configuration; diff --git a/.erb/configs/webpack.config.eslint.ts b/.erb/configs/webpack.config.eslint.ts deleted file mode 100644 index 35a631b7..00000000 --- a/.erb/configs/webpack.config.eslint.ts +++ /dev/null @@ -1,3 +0,0 @@ -/* eslint import/no-unresolved: off, import/no-self-import: off */ - -module.exports = require('./webpack.config.renderer.dev').default; diff --git a/.erb/configs/webpack.config.main.prod.ts b/.erb/configs/webpack.config.main.prod.ts deleted file mode 100644 index 5a4514a4..00000000 --- a/.erb/configs/webpack.config.main.prod.ts +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Webpack config for production electron main process - */ - -import path from 'path'; -import webpack from 'webpack'; -import { merge } from 'webpack-merge'; -import TerserPlugin from 'terser-webpack-plugin'; -import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; -import baseConfig from './webpack.config.base'; -import webpackPaths from './webpack.paths'; -import checkNodeEnv from '../scripts/check-node-env'; -import deleteSourceMaps from '../scripts/delete-source-maps'; - -checkNodeEnv('production'); -deleteSourceMaps(); - -const devtoolsConfig = - process.env.DEBUG_PROD === 'true' - ? { - devtool: 'source-map', - } - : {}; - -const configuration: webpack.Configuration = { - ...devtoolsConfig, - - mode: 'production', - - target: 'electron-main', - - entry: { - main: path.join(webpackPaths.srcMainPath, 'main.ts'), - preload: path.join(webpackPaths.srcMainPath, 'preload.js'), - }, - - output: { - path: webpackPaths.distMainPath, - filename: '[name].js', - }, - - optimization: { - minimizer: [ - new TerserPlugin({ - parallel: true, - }), - ], - }, - - plugins: [ - new BundleAnalyzerPlugin({ - analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled', - }), - - /** - * Create global constants which can be configured at compile time. - * - * Useful for allowing different behaviour between development builds and - * release builds - * - * NODE_ENV should be production so that modules do not perform certain - * development checks - */ - new webpack.EnvironmentPlugin({ - NODE_ENV: 'production', - DEBUG_PROD: false, - START_MINIMIZED: false, - }), - ], - - /** - * Disables webpack processing of __dirname and __filename. - * If you run the bundle in node.js it falls back to these values of node.js. - * https://github.com/webpack/webpack/issues/2010 - */ - node: { - __dirname: false, - __filename: false, - }, -}; - -export default merge(baseConfig, configuration); diff --git a/.erb/configs/webpack.config.renderer.dev.dll.ts b/.erb/configs/webpack.config.renderer.dev.dll.ts deleted file mode 100644 index 614b90f0..00000000 --- a/.erb/configs/webpack.config.renderer.dev.dll.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Builds the DLL for development electron renderer process - */ - -import webpack from 'webpack'; -import path from 'path'; -import { merge } from 'webpack-merge'; -import baseConfig from './webpack.config.base'; -import webpackPaths from './webpack.paths'; -import { dependencies } from '../../package.json'; -import checkNodeEnv from '../scripts/check-node-env'; - -checkNodeEnv('development'); - -const dist = webpackPaths.dllPath; - -const configuration: webpack.Configuration = { - context: webpackPaths.rootPath, - - devtool: 'eval', - - mode: 'development', - - target: 'electron-renderer', - - externals: ['fsevents', 'crypto-browserify'], - - /** - * Use `module` from `webpack.config.renderer.dev.js` - */ - module: require('./webpack.config.renderer.dev').default.module, - - entry: { - renderer: Object.keys(dependencies || {}), - }, - - output: { - path: dist, - filename: '[name].dev.dll.js', - library: { - name: 'renderer', - type: 'var', - }, - }, - - plugins: [ - new webpack.DllPlugin({ - path: path.join(dist, '[name].json'), - name: '[name]', - }), - - /** - * Create global constants which can be configured at compile time. - * - * Useful for allowing different behaviour between development builds and - * release builds - * - * NODE_ENV should be production so that modules do not perform certain - * development checks - */ - new webpack.EnvironmentPlugin({ - NODE_ENV: 'development', - }), - - new webpack.LoaderOptionsPlugin({ - debug: true, - options: { - context: webpackPaths.srcPath, - output: { - path: webpackPaths.dllPath, - }, - }, - }), - ], -}; - -export default merge(baseConfig, configuration); diff --git a/.erb/configs/webpack.config.renderer.dev.ts b/.erb/configs/webpack.config.renderer.dev.ts deleted file mode 100644 index cc9b0eba..00000000 --- a/.erb/configs/webpack.config.renderer.dev.ts +++ /dev/null @@ -1,180 +0,0 @@ -import path from 'path'; -import fs from 'fs'; -import webpack from 'webpack'; -import HtmlWebpackPlugin from 'html-webpack-plugin'; -import chalk from 'chalk'; -import { merge } from 'webpack-merge'; -import { spawn, execSync } from 'child_process'; -import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'; -import baseConfig from './webpack.config.base'; -import webpackPaths from './webpack.paths'; -import checkNodeEnv from '../scripts/check-node-env'; - -// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's -// at the dev webpack config is not accidentally run in a production environment -if (process.env.NODE_ENV === 'production') { - checkNodeEnv('development'); -} - -const port = process.env.PORT || 1212; -const manifest = path.resolve(webpackPaths.dllPath, 'renderer.json'); -// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -const requiredByDLLConfig = module.parent!.filename.includes( - 'webpack.config.renderer.dev.dll' -); - -/** - * Warn if the DLL is not built - */ -if ( - !requiredByDLLConfig && - !(fs.existsSync(webpackPaths.dllPath) && fs.existsSync(manifest)) -) { - console.log( - chalk.black.bgYellow.bold( - 'The DLL files are missing. Sit back while we build them for you with "npm run build-dll"' - ) - ); - execSync('npm run postinstall'); -} - -const configuration: webpack.Configuration = { - devtool: 'inline-source-map', - - mode: 'development', - - target: ['web', 'electron-renderer'], - - entry: [ - `webpack-dev-server/client?http://localhost:${port}/dist`, - 'webpack/hot/only-dev-server', - path.join(webpackPaths.srcRendererPath, 'index.tsx'), - ], - - output: { - path: webpackPaths.distRendererPath, - publicPath: '/', - filename: 'renderer.dev.js', - library: { - type: 'umd', - }, - }, - - module: { - rules: [ - { - test: /\.s?css$/, - use: [ - 'style-loader', - { - loader: 'css-loader', - options: { - modules: true, - sourceMap: true, - importLoaders: 1, - }, - }, - 'sass-loader', - ], - include: /\.module\.s?(c|a)ss$/, - }, - { - test: /\.s?css$/, - use: ['style-loader', 'css-loader', 'sass-loader'], - exclude: /\.module\.s?(c|a)ss$/, - }, - // Fonts - { - test: /\.(woff|woff2|eot|ttf|otf)$/i, - type: 'asset/resource', - }, - // Images - { - test: /\.(png|svg|jpg|jpeg|gif)$/i, - type: 'asset/resource', - }, - ], - }, - plugins: [ - ...(requiredByDLLConfig - ? [] - : [ - new webpack.DllReferencePlugin({ - context: webpackPaths.dllPath, - manifest: require(manifest), - sourceType: 'var', - }), - ]), - - new webpack.NoEmitOnErrorsPlugin(), - - /** - * Create global constants which can be configured at compile time. - * - * Useful for allowing different behaviour between development builds and - * release builds - * - * NODE_ENV should be production so that modules do not perform certain - * development checks - * - * By default, use 'development' as NODE_ENV. This can be overriden with - * 'staging', for example, by changing the ENV variables in the npm scripts - */ - new webpack.EnvironmentPlugin({ - NODE_ENV: 'development', - }), - - new webpack.LoaderOptionsPlugin({ - debug: true, - }), - - new ReactRefreshWebpackPlugin(), - - new HtmlWebpackPlugin({ - filename: path.join('index.html'), - template: path.join(webpackPaths.srcRendererPath, 'index.ejs'), - minify: { - collapseWhitespace: true, - removeAttributeQuotes: true, - removeComments: true, - }, - isBrowser: false, - env: process.env.NODE_ENV, - isDevelopment: process.env.NODE_ENV !== 'production', - nodeModules: webpackPaths.appNodeModulesPath, - }), - ], - - node: { - __dirname: false, - __filename: false, - }, - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - devServer: { - port, - compress: true, - hot: true, - headers: { 'Access-Control-Allow-Origin': '*' }, - static: { - publicPath: '/', - }, - historyApiFallback: { - verbose: true, - }, - onBeforeSetupMiddleware() { - console.log('Starting Main Process...'); - spawn('npm', ['run', 'start:main'], { - shell: true, - env: process.env, - stdio: 'inherit', - }) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - .on('close', (code: number) => process.exit(code!)) - .on('error', (spawnError) => console.error(spawnError)); - }, - }, -}; - -export default merge(baseConfig, configuration); diff --git a/.erb/configs/webpack.config.renderer.prod.ts b/.erb/configs/webpack.config.renderer.prod.ts deleted file mode 100644 index 5f381ce4..00000000 --- a/.erb/configs/webpack.config.renderer.prod.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Build config for electron renderer process - */ - -import path from 'path'; -import webpack from 'webpack'; -import HtmlWebpackPlugin from 'html-webpack-plugin'; -import MiniCssExtractPlugin from 'mini-css-extract-plugin'; -import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; -import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; -import { merge } from 'webpack-merge'; -import TerserPlugin from 'terser-webpack-plugin'; -import baseConfig from './webpack.config.base'; -import webpackPaths from './webpack.paths'; -import checkNodeEnv from '../scripts/check-node-env'; -import deleteSourceMaps from '../scripts/delete-source-maps'; - -checkNodeEnv('production'); -deleteSourceMaps(); - -const devtoolsConfig = - process.env.DEBUG_PROD === 'true' - ? { - devtool: 'source-map', - } - : {}; - -const configuration: webpack.Configuration = { - ...devtoolsConfig, - - mode: 'production', - - target: ['web', 'electron-renderer'], - - entry: [path.join(webpackPaths.srcRendererPath, 'index.tsx')], - - output: { - path: webpackPaths.distRendererPath, - publicPath: './', - filename: 'renderer.js', - library: { - type: 'umd', - }, - }, - - module: { - rules: [ - { - test: /\.s?(a|c)ss$/, - use: [ - MiniCssExtractPlugin.loader, - { - loader: 'css-loader', - options: { - modules: true, - sourceMap: true, - importLoaders: 1, - }, - }, - 'sass-loader', - ], - include: /\.module\.s?(c|a)ss$/, - }, - { - test: /\.s?(a|c)ss$/, - use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'], - exclude: /\.module\.s?(c|a)ss$/, - }, - // Fonts - { - test: /\.(woff|woff2|eot|ttf|otf)$/i, - type: 'asset/resource', - }, - // Images - { - test: /\.(png|svg|jpg|jpeg|gif)$/i, - type: 'asset/resource', - }, - ], - }, - - optimization: { - minimize: true, - minimizer: [ - new TerserPlugin({ - parallel: true, - }), - new CssMinimizerPlugin(), - ], - }, - - plugins: [ - /** - * Create global constants which can be configured at compile time. - * - * Useful for allowing different behaviour between development builds and - * release builds - * - * NODE_ENV should be production so that modules do not perform certain - * development checks - */ - new webpack.EnvironmentPlugin({ - NODE_ENV: 'production', - DEBUG_PROD: false, - }), - - new MiniCssExtractPlugin({ - filename: 'style.css', - }), - - new BundleAnalyzerPlugin({ - analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled', - }), - - new HtmlWebpackPlugin({ - filename: 'index.html', - template: path.join(webpackPaths.srcRendererPath, 'index.ejs'), - minify: { - collapseWhitespace: true, - removeAttributeQuotes: true, - removeComments: true, - }, - isBrowser: false, - isDevelopment: process.env.NODE_ENV !== 'production', - }), - ], -}; - -export default merge(baseConfig, configuration); diff --git a/.erb/configs/webpack.paths.ts b/.erb/configs/webpack.paths.ts deleted file mode 100644 index e5ba5734..00000000 --- a/.erb/configs/webpack.paths.ts +++ /dev/null @@ -1,38 +0,0 @@ -const path = require('path'); - -const rootPath = path.join(__dirname, '../..'); - -const dllPath = path.join(__dirname, '../dll'); - -const srcPath = path.join(rootPath, 'src'); -const srcMainPath = path.join(srcPath, 'main'); -const srcRendererPath = path.join(srcPath, 'renderer'); - -const releasePath = path.join(rootPath, 'release'); -const appPath = path.join(releasePath, 'app'); -const appPackagePath = path.join(appPath, 'package.json'); -const appNodeModulesPath = path.join(appPath, 'node_modules'); -const srcNodeModulesPath = path.join(srcPath, 'node_modules'); - -const distPath = path.join(appPath, 'dist'); -const distMainPath = path.join(distPath, 'main'); -const distRendererPath = path.join(distPath, 'renderer'); - -const buildPath = path.join(releasePath, 'build'); - -export default { - rootPath, - dllPath, - srcPath, - srcMainPath, - srcRendererPath, - releasePath, - appPath, - appPackagePath, - appNodeModulesPath, - srcNodeModulesPath, - distPath, - distMainPath, - distRendererPath, - buildPath, -}; diff --git a/.erb/img/erb-banner.png b/.erb/img/erb-banner.png deleted file mode 100644 index 1d922465..00000000 Binary files a/.erb/img/erb-banner.png and /dev/null differ diff --git a/.erb/img/erb-banner.svg b/.erb/img/erb-banner.svg deleted file mode 100644 index f7ce6707..00000000 --- a/.erb/img/erb-banner.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.erb/img/erb-logo.png b/.erb/img/erb-logo.png deleted file mode 100644 index 97a5661a..00000000 Binary files a/.erb/img/erb-logo.png and /dev/null differ diff --git a/.erb/mocks/fileMock.js b/.erb/mocks/fileMock.js deleted file mode 100644 index 602eb23e..00000000 --- a/.erb/mocks/fileMock.js +++ /dev/null @@ -1 +0,0 @@ -export default 'test-file-stub'; diff --git a/.erb/scripts/.eslintrc b/.erb/scripts/.eslintrc deleted file mode 100644 index 30610f85..00000000 --- a/.erb/scripts/.eslintrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "rules": { - "global-require": "off", - "import/no-dynamic-require": "off", - "import/no-extraneous-dependencies": "off", - "react/jsx-props-no-spreading": "off" - } -} \ No newline at end of file diff --git a/.erb/scripts/check-build-exists.ts b/.erb/scripts/check-build-exists.ts deleted file mode 100644 index ccb0ba8a..00000000 --- a/.erb/scripts/check-build-exists.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Check if the renderer and main bundles are built -import path from 'path'; -import chalk from 'chalk'; -import fs from 'fs'; -import webpackPaths from '../configs/webpack.paths'; - -const mainPath = path.join(webpackPaths.distMainPath, 'main.js'); -const rendererPath = path.join(webpackPaths.distRendererPath, 'renderer.js'); - -if (!fs.existsSync(mainPath)) { - throw new Error( - chalk.whiteBright.bgRed.bold( - 'The main process is not built yet. Build it by running "npm run build:main"' - ) - ); -} - -if (!fs.existsSync(rendererPath)) { - throw new Error( - chalk.whiteBright.bgRed.bold( - 'The renderer process is not built yet. Build it by running "npm run build:renderer"' - ) - ); -} diff --git a/.erb/scripts/check-native-dep.js b/.erb/scripts/check-native-dep.js deleted file mode 100644 index f5a23268..00000000 --- a/.erb/scripts/check-native-dep.js +++ /dev/null @@ -1,54 +0,0 @@ -import fs from 'fs'; -import chalk from 'chalk'; -import { execSync } from 'child_process'; -import { dependencies } from '../../package.json'; - -if (dependencies) { - const dependenciesKeys = Object.keys(dependencies); - const nativeDeps = fs - .readdirSync('node_modules') - .filter((folder) => fs.existsSync(`node_modules/${folder}/binding.gyp`)); - if (nativeDeps.length === 0) { - process.exit(0); - } - try { - // Find the reason for why the dependency is installed. If it is installed - // because of a devDependency then that is okay. Warn when it is installed - // because of a dependency - const { dependencies: dependenciesObject } = JSON.parse( - execSync(`npm ls ${nativeDeps.join(' ')} --json`).toString() - ); - const rootDependencies = Object.keys(dependenciesObject); - const filteredRootDependencies = rootDependencies.filter((rootDependency) => - dependenciesKeys.includes(rootDependency) - ); - if (filteredRootDependencies.length > 0) { - const plural = filteredRootDependencies.length > 1; - console.log(` - ${chalk.whiteBright.bgYellow.bold( - 'Webpack does not work with native dependencies.' - )} -${chalk.bold(filteredRootDependencies.join(', '))} ${ - plural ? 'are native dependencies' : 'is a native dependency' - } and should be installed inside of the "./release/app" folder. - First, uninstall the packages from "./package.json": -${chalk.whiteBright.bgGreen.bold('npm uninstall your-package')} - ${chalk.bold( - 'Then, instead of installing the package to the root "./package.json":' - )} -${chalk.whiteBright.bgRed.bold('npm install your-package')} - ${chalk.bold('Install the package to "./release/app/package.json"')} -${chalk.whiteBright.bgGreen.bold( - 'cd ./release/app && npm install your-package' -)} - Read more about native dependencies at: -${chalk.bold( - 'https://electron-react-boilerplate.js.org/docs/adding-dependencies/#module-structure' -)} - `); - process.exit(1); - } - } catch (e) { - console.log('Native dependencies could not be checked'); - } -} diff --git a/.erb/scripts/check-node-env.js b/.erb/scripts/check-node-env.js deleted file mode 100644 index 0e74b115..00000000 --- a/.erb/scripts/check-node-env.js +++ /dev/null @@ -1,16 +0,0 @@ -import chalk from 'chalk'; - -export default function checkNodeEnv(expectedEnv) { - if (!expectedEnv) { - throw new Error('"expectedEnv" not set'); - } - - if (process.env.NODE_ENV !== expectedEnv) { - console.log( - chalk.whiteBright.bgRed.bold( - `"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config` - ) - ); - process.exit(2); - } -} diff --git a/.erb/scripts/check-port-in-use.js b/.erb/scripts/check-port-in-use.js deleted file mode 100644 index 3ade730a..00000000 --- a/.erb/scripts/check-port-in-use.js +++ /dev/null @@ -1,16 +0,0 @@ -import chalk from 'chalk'; -import detectPort from 'detect-port'; - -const port = process.env.PORT || '1212'; - -detectPort(port, (err, availablePort) => { - if (port !== String(availablePort)) { - throw new Error( - chalk.whiteBright.bgRed.bold( - `Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 npm start` - ) - ); - } else { - process.exit(0); - } -}); diff --git a/.erb/scripts/clean.js b/.erb/scripts/clean.js deleted file mode 100644 index b03b0baf..00000000 --- a/.erb/scripts/clean.js +++ /dev/null @@ -1,17 +0,0 @@ -import rimraf from 'rimraf'; -import process from 'process'; -import webpackPaths from '../configs/webpack.paths'; - -const args = process.argv.slice(2); -const commandMap = { - dist: webpackPaths.distPath, - release: webpackPaths.releasePath, - dll: webpackPaths.dllPath, -}; - -args.forEach((x) => { - const pathToRemove = commandMap[x]; - if (pathToRemove !== undefined) { - rimraf.sync(pathToRemove); - } -}); diff --git a/.erb/scripts/delete-source-maps.js b/.erb/scripts/delete-source-maps.js deleted file mode 100644 index 3d051eab..00000000 --- a/.erb/scripts/delete-source-maps.js +++ /dev/null @@ -1,8 +0,0 @@ -import path from 'path'; -import rimraf from 'rimraf'; -import webpackPaths from '../configs/webpack.paths'; - -export default function deleteSourceMaps() { - rimraf.sync(path.join(webpackPaths.distMainPath, '*.js.map')); - rimraf.sync(path.join(webpackPaths.distRendererPath, '*.js.map')); -} diff --git a/.erb/scripts/electron-rebuild.js b/.erb/scripts/electron-rebuild.js deleted file mode 100644 index c7518ce4..00000000 --- a/.erb/scripts/electron-rebuild.js +++ /dev/null @@ -1,20 +0,0 @@ -import { execSync } from 'child_process'; -import fs from 'fs'; -import { dependencies } from '../../release/app/package.json'; -import webpackPaths from '../configs/webpack.paths'; - -if ( - Object.keys(dependencies || {}).length > 0 && - fs.existsSync(webpackPaths.appNodeModulesPath) -) { - const electronRebuildCmd = - '../../node_modules/.bin/electron-rebuild --parallel --force --types prod,dev,optional --module-dir .'; - const cmd = - process.platform === 'win32' - ? electronRebuildCmd.replace(/\//g, '\\') - : electronRebuildCmd; - execSync(cmd, { - cwd: webpackPaths.appPath, - stdio: 'inherit', - }); -} diff --git a/.erb/scripts/link-modules.ts b/.erb/scripts/link-modules.ts deleted file mode 100644 index 6cc31e66..00000000 --- a/.erb/scripts/link-modules.ts +++ /dev/null @@ -1,9 +0,0 @@ -import fs from 'fs'; -import webpackPaths from '../configs/webpack.paths'; - -const { srcNodeModulesPath } = webpackPaths; -const { appNodeModulesPath } = webpackPaths; - -if (!fs.existsSync(srcNodeModulesPath) && fs.existsSync(appNodeModulesPath)) { - fs.symlinkSync(appNodeModulesPath, srcNodeModulesPath, 'junction'); -} diff --git a/.erb/scripts/notarize.js b/.erb/scripts/notarize.js deleted file mode 100644 index 4fd3bb8f..00000000 --- a/.erb/scripts/notarize.js +++ /dev/null @@ -1,30 +0,0 @@ -const { notarize } = require('electron-notarize'); -const { build } = require('../../package.json'); - -exports.default = async function notarizeMacos(context) { - const { electronPlatformName, appOutDir } = context; - if (electronPlatformName !== 'darwin') { - return; - } - - if (process.env.CI !== 'true') { - console.warn('Skipping notarizing step. Packaging is not running in CI'); - return; - } - - if (!('APPLE_ID' in process.env && 'APPLE_ID_PASS' in process.env)) { - console.warn( - 'Skipping notarizing step. APPLE_ID and APPLE_ID_PASS env variables must be set' - ); - return; - } - - const appName = context.packager.appInfo.productFilename; - - await notarize({ - appBundleId: build.appId, - appPath: `${appOutDir}/${appName}.app`, - appleId: process.env.APPLE_ID, - appleIdPassword: process.env.APPLE_ID_PASS, - }); -}; diff --git a/.eslintignore b/.eslintignore index da134307..0eecf9c9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,7 +1,12 @@ +src/renderer/auto-imports.d.ts + # Logs logs *.log +# built package +release + # Runtime data pids *.pid @@ -15,12 +20,12 @@ coverage # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules + # OSX .DS_Store release/app/dist release/build -.erb/dll .idea npm-debug.log.* diff --git a/.eslintrc.json b/.eslintrc.json index dad290da..b7b16c03 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -7,7 +7,11 @@ "jsx-a11y/no-noninteractive-element-interactions": "off", "jsx-a11y/click-events-have-key-events": "off", "jsx-a11y/no-static-element-interactions": "off", - "global-require": "off" + "global-require": "off", + "jest/no-standalone-expect": "off", + "react/jsx-no-undef": "off", + "react/prop-types": "off", + "import/extensions": "off" }, "extends": ["erb"], "parser": "@typescript-eslint/parser", diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fb76580e..8d6caaf4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,7 +23,7 @@ jobs: - name: Install dependencies run: | - npm install --legacy-peer-deps && (cd release/app && npm install) + npm install && (cd release/app && npm install) - name: Publish releases env: @@ -36,5 +36,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | npm run postinstall - npm run build - npm exec electron-builder -- --publish always --win --mac --linux + npm run package diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 029b0203..b91fa550 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,12 +22,11 @@ jobs: - name: Install intial npm deps run: | npm install -g npm@latest - npm install --legacy-peer-deps || exit 0 + npm install || exit 0 - name: Install native modules run: | - cd ./release/app - DEBUG=electron-rebuild npm install --legacy-peer-deps + DEBUG=electron-rebuild npm install - name: Run tests env: @@ -35,4 +34,3 @@ jobs: run: | npm run package npm run lint - npm exec tsc diff --git a/.gitignore b/.gitignore index da134307..9b03d988 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Files built during compilation +release + # Logs logs *.log @@ -20,7 +23,6 @@ node_modules release/app/dist release/build -.erb/dll .idea npm-debug.log.* diff --git a/.vscode/settings.json b/.vscode/settings.json index e51fb3d4..15362144 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,15 +3,13 @@ "editor.formatOnSave": true, "eslint.alwaysShowStatus": true, "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": true, + "source.organizeImports": false }, "[jsonc]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescriptreact]": { - "editor.defaultFormatter": "vscode.typescript-language-features" - }, - "[typescript]": { - "editor.defaultFormatter": "vscode.typescript-language-features" + "editor.defaultFormatter": "esbenp.prettier-vscode" } } diff --git a/Dockerfile b/Dockerfile index b3525756..3b99866a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,7 @@ WORKDIR /app/ COPY . . RUN rm -rf node_modules src/node_modules release/app/node_modules RUN source /root/.profile && source $HOME/.nvm/nvm.sh \ - && npm install --legacy-peer-deps + && npm install # more DEBUG build info ENV DEBUG=electron-rebuild @@ -35,4 +35,4 @@ RUN source /root/.profile && source $HOME/.nvm/nvm.sh \ WORKDIR /app/ #COPY . . RUN source /root/.profile && source $HOME/.nvm/nvm.sh \ - && npm run package \ No newline at end of file + && npm run package diff --git a/README.md b/README.md index 2a4bdc09..63f9fad8 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,7 @@ If you already have Node on your system (we recommend version 17), you can install the Node deps like so: ``` -$ npm install --legacy-peer-deps && \ - (cd ./release/app/ && npm install --legacy-peer-deps) && \ - npm install --legacy-peer-deps +$ npm install ``` In order to use Anchor functionality, the `anchor` CLI must be @@ -76,9 +74,7 @@ without anchor tooling for now - Node (latest version): `nvm install 16.15.0` - as Administrator `nvm use 16.15.0` - Yarn: `corepack enable` -- `npm install --legacy-peer-deps` -- `cd ./release/app/` -- `npm install --legacy-peer-deps` +- `npm install` - Docker Desktop, or a working configured Docker setup @@ -87,11 +83,16 @@ without anchor tooling for now to run: ``` -$ npm run start +$ npm run dev ``` Now you're working with Workbench! +## Development + +The project is currently in a migratory phase from Bootstrap to Tailwind. Do not write new code using Bootstrap layouting. Instead, opt for using Tailwind's +atomic CSS system instead. The goal is to eventually be able to fully remove bootstrap from the codebase. + ## building a release On each platform (OSX, Windows, Linux), run: @@ -99,11 +100,7 @@ On each platform (OSX, Windows, Linux), run: ``` git clone https://github.com/workbenchapp/solana-workbench new-release-dir cd new-release-dir -npm install --legacy-peer-deps -cd ./release/app/ -npm install --legacy-peer-deps -cd ../.. -npm install --legacy-peer-deps +npm install npm run package ``` @@ -113,4 +110,4 @@ then copy the appimage/dmg/exe to a staging dir, and run ``` -``` \ No newline at end of file +``` diff --git a/electron.Dockerfile b/electron.Dockerfile index aedcbb7f..5914016e 100644 --- a/electron.Dockerfile +++ b/electron.Dockerfile @@ -8,6 +8,6 @@ ENV DEBUG electron-rebuild RUN rm -f /usr/bin/python && ln -s /usr/bin/python3 /usr/bin/python # Need Typescript, etc. for native extension build to work -RUN npm install --legacy-peer-deps -RUN (cd ./release/app npm install --legacy-peer-deps) -RUN npm build \ No newline at end of file +RUN npm install +RUN (cd ./release/app npm install) +RUN npm build diff --git a/package.json b/package.json index 12a38e88..4525b80b 100644 --- a/package.json +++ b/package.json @@ -2,20 +2,28 @@ "name": "solana-workbench", "productName": "SolanaWorkbench", "description": "Solana workbench app for making development on Solana better", + "version": "0.3.1", + "main": "./release/dist/main/main.js", "scripts": { + "start": "npm run dev", + "dev": "concurrently \"npm run start:main\" \"npm run start:renderer\"", "build": "concurrently \"npm run build:main\" \"npm run build:renderer\"", - "build:main": "cross-env NODE_ENV=production webpack --config ./.erb/configs/webpack.config.main.prod.ts", - "build:renderer": "cross-env NODE_ENV=production webpack --config ./.erb/configs/webpack.config.renderer.prod.ts", - "rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir release/app", + "build:main": "tsc -p ./src/main/tsconfig.json", + "build:renderer": "vite build --config ./src/renderer/vite.config.ts", + "start:main": "npm run build:main && cross-env NODE_ENV=development electronmon -r ts-node/register/transpile-only ./src/main/main.ts", + "start:renderer": "vite dev --config ./src/renderer/vite.config.ts", + "package": "rimraf ./release && npm run build && electron-builder -- --publish always --win --mac --linux", + "package-nomac": "rimraf ./release && npm run build && electron-builder -- --publish always --win --linux", + "package:asarless": "npm run build && electron-builder build --config.asar=false", "lint": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx", - "package": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never", - "postinstall": "ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && cross-env NODE_ENV=development webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.ts", - "start": "ts-node ./.erb/scripts/check-port-in-use.js && npm run start:renderer", - "start:main": "cross-env NODE_ENV=development electron -r ts-node/register ./src/main/main.ts", - "start:renderer": "cross-env NODE_ENV=development webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts", - "test": "jest", - "prepare": "husky install" + "lint-fix": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx --fix", + "test": "vitest run --dir ./src --config ./src/renderer/vitest.config.ts", + "prepare": "husky install", + "postinstall": "electron-builder install-app-deps" }, + "browserslist": [ + "last 1 electron version" + ], "lint-staged": { "*.{js,jsx,ts,tsx}": [ "cross-env NODE_ENV=development eslint" @@ -30,17 +38,21 @@ "prettier --ignore-path .eslintignore --single-quote --write" ] }, + "electronmon": { + "patterns": [ + "!src/renderer/**" + ] + }, "build": { "productName": "Solana Workbench", "appId": "org.erb.SolanaWorkbench", "asar": true, "asarUnpack": "**\\*.{node,dll}", "files": [ - "dist", - "node_modules", + "./release/dist/**/*", + "!**/*.d.ts", "package.json" ], - "afterSign": ".erb/scripts/notarize.js", "mac": { "target": { "target": "default", @@ -81,12 +93,11 @@ "category": "Development" }, "directories": { - "app": "release/app", "buildResources": "assets", "output": "release/build" }, "extraResources": [ - "./assets/**" + "assets/**/*" ], "publish": { "provider": "github", @@ -99,8 +110,8 @@ "url": "git+https://github.com/electron-react-boilerplate/electron-react-boilerplate.git" }, "author": { - "name": "Electron React Boilerplate Maintainers", - "email": "nathan.leclaire@gmail.com", + "name": "Nathan LeClaire", + "email": "nathan@cryptoworkbench.io", "url": "https://github.com/workbenchapp/solana-workbench-releases" }, "contributors": [], @@ -115,84 +126,49 @@ "typescript", "ts", "sass", - "webpack", "hot", - "reload" + "reload", + "vite" ], "homepage": "https://github.com/workbenchapp/solana-workbench-releases", - "jest": { - "testURL": "http://localhost/", - "testEnvironment": "jsdom", - "transform": { - "\\.(ts|tsx|js|jsx)$": "ts-jest" - }, - "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/.erb/mocks/fileMock.js", - "\\.(css|less|sass|scss)$": "identity-obj-proxy" - }, - "moduleFileExtensions": [ - "js", - "jsx", - "ts", - "tsx", - "json" - ], - "moduleDirectories": [ - "node_modules", - "release/app/node_modules" - ], - "testPathIgnorePatterns": [ - "release/app/dist" - ], - "setupFiles": [ - "./.erb/scripts/check-build-exists.ts" - ] - }, "devDependencies": { - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.5", + "@iconify-json/mdi": "^1.1.20", "@solana/wallet-adapter-wallets": "^0.15.5", "@solana/web3.js": "^1.41.3", - "@solana/wallet-adapter-wallets": "^0.15.5", + "@svgr/core": "^6.2.1", "@teamsupercell/typings-for-css-modules-loader": "^2.5.1", - "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^13.2.0", "@types/amplitude-js": "^8.0.2", "@types/enzyme": "^3.10.10", "@types/history": "^5.0.0", - "@types/jest": "^27.0.3", "@types/logfmt": "^1.2.2", "@types/node": "^17.0.31", "@types/prop-types": "^15.7.4", "@types/react": "^18.0.9", "@types/react-dom": "^18.0.3", "@types/react-outside-click-handler": "^1.3.0", - "@types/react-router": "^5.1.18", - "@types/react-router-dom": "^5.3.3", "@types/react-test-renderer": "^18.0.0", "@types/shelljs": "^0.8.11", "@types/sqlite3": "^3.1.7", - "@types/terser-webpack-plugin": "^5.0.4", "@types/underscore": "^1.11.3", "@types/uuid": "^8.3.3", - "@types/webpack-bundle-analyzer": "^4.4.1", - "@types/webpack-env": "^1.16.3", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@typescript-eslint/typescript-estree": "^5.16.0", + "@vitejs/plugin-react": "^1.3.2", "bootswatch": "^5.1.3", - "browserslist-config-erb": "^0.0.3", "chalk": "^4.1.2", "concurrently": "^7.1.0", "core-js": "^3.20.1", "cross-env": "^7.0.3", "css-loader": "^6.5.1", - "css-minimizer-webpack-plugin": "^3.3.1", "detect-port": "^1.3.0", "electron": "^18.2.0", "electron-builder": "^23.0.3", "electron-devtools-installer": "^3.2.0", "electron-notarize": "^1.1.1", "electron-rebuild": "^3.2.5", + "electronmon": "^2.0.2", "enzyme": "^3.11.0", "enzyme-to-json": "^3.6.2", "eslint": "^8.15.0", @@ -203,17 +179,17 @@ "eslint-import-resolver-typescript": "^2.5.0", "eslint-plugin-compat": "^4.0.2", "eslint-plugin-import": "^2.25.4", - "eslint-plugin-jest": "^26.1.5", + "eslint-plugin-jest": "^26.5.3", "eslint-plugin-jsx-a11y": "^6.5.1", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-promise": "^6.0.0", "eslint-plugin-react": "^7.29.4", "eslint-plugin-react-hooks": "^4.3.0", "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.5.0", "husky": "^8.0.0", "identity-obj-proxy": "^3.0.0", - "jest": "^28.1.0", + "jest": "^28.1.1", + "jsdom": "^20.0.0", "lint-staged": "^12.4.1", "mini-css-extract-plugin": "^2.4.3", "opencollective-postinstall": "^2.0.3", @@ -222,23 +198,20 @@ "react-refresh-typescript": "^2.0.2", "react-test-renderer": "^18.1.0", "rimraf": "^3.0.2", - "sass": "^1.45.1", - "sass-loader": "^12.4.0", - "style-loader": "^3.3.1", - "terser-webpack-plugin": "^5.3.0", - "ts-jest": "^28.0.2", - "ts-loader": "^9.2.6", - "ts-node": "^10.4.0", + "sass": "^1.52.3", + "ts-node": "^10.8.1", "typescript": "^4.6.2", + "unplugin-auto-import": "^0.8.8", + "unplugin-icons": "^0.14.3", "url-loader": "^4.1.1", - "webpack": "^5.65.0", - "webpack-bundle-analyzer": "^4.5.0", - "webpack-cli": "^4.9.1", - "webpack-dev-server": "^4.7.1", - "webpack-merge": "^5.8.0" + "vite": "^2.9.12", + "vite-plugin-fonts": "^0.4.0", + "vite-plugin-inline-css-modules": "^0.0.4", + "vite-plugin-windicss": "^1.8.4", + "vitest": "^0.14.2", + "windicss": "^3.5.4" }, "dependencies": { - "@amplitude/node": "^1.9.1", "@fortawesome/fontawesome-svg-core": "^6.1.0", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.0", @@ -250,15 +223,13 @@ "amplitude-js": "^8.12.0", "bip39": "^3.0.4", "bootstrap": "^5.1.3", - "command-exists": "^1.2.9", - "crypto-browserify": "^3.12.0", + "classnames": "^2.3.1", "electron-cfg": "^1.2.7", "electron-debug": "^3.2.0", "electron-log": "^4.4.6", "electron-promise-ipc": "^2.2.4", "electron-updater": "^5.0.1", "hexdump-nodejs": "^0.1.0", - "history": "^5.2.0", "is-electron": "^2.2.1", "logfmt": "^1.3.2", "react": "^18.1.0", @@ -273,11 +244,8 @@ "react-toastify": "^9.0.1", "regenerator-runtime": "^0.13.9", "shelljs": "^0.8.5", - "stream-browserify": "^3.0.0", - "styled-components": "^5.3.3", "typescript-lru-cache": "^1.2.3", "underscore": "^1.13.1", - "uuid": "^8.3.2", "victory": "^36.3.2", "winston": "^3.3.3" }, @@ -285,7 +253,6 @@ "node": ">=16.15.0", "npm": ">=8.x" }, - "browserslist": [], "prettier": { "overrides": [ { diff --git a/release/app/.npmrc b/release/app/.npmrc deleted file mode 100644 index 43c97e71..00000000 --- a/release/app/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false diff --git a/release/app/package.json b/release/app/package.json deleted file mode 100644 index 4b340e26..00000000 --- a/release/app/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "solana-workbench-native-deps", - "productName": "solana-workbench-native-deps", - "version": "0.3.0", - "description": "Your one stop shop for Solana development", - "main": "./dist/main/main.js", - "author": { - "name": "Nathan LeClaire", - "email": "nathan@cryptoworkbench.io", - "url": "https://github.com/workbenchapp/solana-workbench-releases" - }, - "scripts": { - "electron-rebuild": "node -r ts-node/register ../../.erb/scripts/electron-rebuild.js", - "link-modules": "node -r ts-node/register ../../.erb/scripts/link-modules.ts", - "postinstall": "npm run electron-rebuild && npm run link-modules" - }, - "license": "MIT", - "dependencies": {} -} diff --git a/src/__tests__/App.test.tsx b/src/__tests__/App.test.tsx index b33466ff..b5609880 100644 --- a/src/__tests__/App.test.tsx +++ b/src/__tests__/App.test.tsx @@ -1,6 +1,6 @@ -import '@testing-library/jest-dom'; import { render } from '@testing-library/react'; import { Provider } from 'react-redux'; +import { describe, expect, it } from 'vitest'; import App from '../renderer/App'; import store from '../renderer/store'; diff --git a/src/main/main.ts b/src/main/main.ts index ed5a3c09..395ac8c6 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -1,22 +1,20 @@ -import 'core-js/stable'; -import 'regenerator-runtime/runtime'; -import path from 'path'; -import { app, BrowserWindow, shell, ipcMain } from 'electron'; -import { autoUpdater } from 'electron-updater'; +import { app, BrowserWindow, ipcMain, shell } from 'electron'; import log from 'electron-log'; -import MenuBuilder from './menu'; -import { resolveHtmlPath } from './util'; -import { logger, initLogging } from './logger'; -import { runValidator, stopValidator, validatorLogs } from './validator'; -import { initConfigPromises } from './ipc/config'; -import { initAccountPromises } from './ipc/accounts'; +import { autoUpdater } from 'electron-updater'; +import path from 'path'; +import 'regenerator-runtime/runtime'; import fetchAnchorIdl from './anchor'; - +import { RESOURCES_PATH } from './const'; +import { initAccountPromises } from './ipc/accounts'; +import { initConfigPromises } from './ipc/config'; +import { initLogging, logger } from './logger'; +import MenuBuilder from './menu'; import { subscribeTransactionLogs, unsubscribeTransactionLogs, } from './transactionLogs'; -import { RESOURCES_PATH } from './const'; +import { resolveHtmlPath } from './util'; +import { runValidator, stopValidator, validatorLogs } from './validator'; export default class AppUpdater { constructor() { diff --git a/src/main/transactionLogs.ts b/src/main/transactionLogs.ts index acbb0726..1176053a 100644 --- a/src/main/transactionLogs.ts +++ b/src/main/transactionLogs.ts @@ -1,6 +1,6 @@ import * as sol from '@solana/web3.js'; import Electron from 'electron'; -import { LogSubscriptionMap } from 'types/types'; +import { LogSubscriptionMap } from '@/types/types'; import { netToURL } from '../common/strings'; const logSubscriptions: LogSubscriptionMap = {}; diff --git a/src/main/tsconfig.json b/src/main/tsconfig.json new file mode 100644 index 00000000..4bd9c55b --- /dev/null +++ b/src/main/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["../*"] + }, + "outDir": "../../release/dist" + }, + "files": ["main.ts"], + "include": ["../types/**/*", "preload.js"] +} diff --git a/src/renderer/App.scss b/src/renderer/App.scss index 46dbc8d1..2c0b0228 100644 --- a/src/renderer/App.scss +++ b/src/renderer/App.scss @@ -5,224 +5,15 @@ @use 'sass:map'; // see https://bootswatch.com/ -@import '~bootswatch/dist/lux/variables'; -@import '~bootstrap/scss/bootstrap'; -@import '~bootswatch/dist/lux/bootswatch'; +@import "bootstrap/scss/functions"; -$solgreen: #14f195; +// 2. Include any default variable overrides here -:root { - --header-height: 3rem; - --nav-width: 68px; - --first-color: #4723d9; - --first-color-light: darkgray; - --white-color: lightgray; - --body-font: 'Nunito', sans-serif; - --normal-font-size: 1rem; - --z-fixed: 100; -} +// 3. Include remainder of required Bootstrap stylesheets +@import "bootstrap/scss/variables"; +@import "bootstrap/scss/mixins"; -.text { - &-solgreen { - color: $solgreen; - } -} - -@mixin inline-code() { - font-family: $font-family-code; - padding: 0.2em 0.4em; - border-radius: 6px; - background-color: $gray-200; - font-weight: 500; - color: $body-color; - border: 1px solid $gray-400; -} - -code { - @include inline-code; -} - -pre code { - background-color: $white; - border: none; -} - -h1, -h2, -h3, -h4, -h5, -h6, -th, -.navbar, -.btn { - text-transform: none; - box-shadow: none !important; -} - -// TODO: When using lux bootswatch, the .navbar padding-top and padding-bottom is too massive -.navbar { - padding-top: 0rem; - padding-bottom: 0rem; -} - -.btn { - border-radius: 1rem; - margin: 5px; - font-size: 1rem; - &-sm { - font-size: 0.8rem; - border-radius: 1rem; - } -} - -.page-content { - position: fixed; - top: 25px; - left: 70px; - padding-right: 100px; - scroll-behavior: auto; - bottom: 15px; -} - -.icon { - &-interactive { - // next two lines copied from bootstrap - // p-1 and rounded classes - border-radius: 0.25rem !important; - padding: 0.25rem !important; - &:hover { - background-color: $gray-200; - } - cursor: pointer; - } -} - -.almost-vh-100 { - height: 100%; -} -.almost-vh-80 { - height: 80%; -} -.almost-vh-20 { - height: 20%; -} - -.l-navbar { - position: fixed; - top: 70px; - left: 0px; - width: 60px; - background-color: white !important; // TODO: figure this out - transition: 0.5s; - z-index: var(--z-fixed); - padding: 0rem 0rem; -} - -.l-navbar .nav { - height: 100%; - display: flex; - flex-direction: column; - overflow: hidden; - column-gap: 0.2rem; -} - -.l-navbar .nav_logo, -.l-navbar .nav-link { - display: grid; - grid-template-columns: max-content max-content; - align-items: center; - margin-top: 1.5rem; - padding: 0rem 0rem 0rem 0.8rem; -} - -.l-navbar .nav_logo { - margin-bottom: 2rem; -} - -.l-navbar .nav_logo-icon { - font-size: 1.25rem; - color: var(--white-color); -} - -.l-navbar .nav_logo-name { - color: var(--white-color); - font-weight: 700; -} - -.l-navbar .nav-link { - position: relative; - color: var(--first-color-light); - margin-bottom: 1.5rem; - transition: 0.3s; -} - -.l-navbar .nav-link:hover { - color: var(--white-color); -} - -.l-navbar .nav_icon { - font-size: 1.25rem; -} - -.l-navbar .nav-link.active { - color: black; -} - -#l-navbar-dropdown { - margin: 0rem; - padding: 0rem; - border-radius: 0rem; - box-shadow: none; -} - -.bg { - &-lightblue { - background-color: $blue-100; - } -} - -.border-top { - border-top: 1px solid #eceeef; -} -.border-left { - border-left: 1px solid #eceeef; -} - -.vscroll { - overflow-y: auto; -} - -// Wallet adapter colours so its easier to see our dragonfly logo -// this isn't really right, but i don't like the purple, as it implies Phantom wallet? -// .wallet-adapter-button-trigger { -// background-color: powderblue !important; -// color: steelblue !important; -// } - -// EdiText - https://github.com/alioguzhan/react-editext#styling-with-styled-components -div[editext='view-container'], -div[editext='view'], -div[editext='edit-container'] { - flex: 0 0 auto; - width: 100%; -} - -div[editext='view'] { - width: calc(100% - 32px); -} -.align-center { - flex: 0 0 auto; - width: 100%; - display: flex; - align-items: center; - height: 2.4em; /* TODO: this is a terrible hack for aligning the "Editable Alias" with the inline edit box height. */ -} - -.gutter.gutter-horizontal { - cursor: col-resize; -} - -.gutter.gutter-vertical { - cursor: row-resize; -} \ No newline at end of file +// 4. Include any optional Bootstrap components as you like +@import "bootstrap/scss/dropdown"; +@import "bootstrap/scss/tooltip"; +@import "bootstrap/scss/popover"; diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 387bc01e..d71d245d 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1,33 +1,3 @@ -import PropTypes from 'prop-types'; -import isElectron from 'is-electron'; -import './App.scss'; -import * as sol from '@solana/web3.js'; - -import { Routes, Route, NavLink, Outlet } from 'react-router-dom'; - -import Container from 'react-bootstrap/Container'; -import Navbar from 'react-bootstrap/Navbar'; -import Nav from 'react-bootstrap/Nav'; -import { Form, Button } from 'react-bootstrap'; - -import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; -import Tooltip from 'react-bootstrap/Tooltip'; - -import { ToastContainer } from 'react-toastify'; -import 'react-toastify/dist/ReactToastify.css'; - -import { - faBook, - faTh, - faAnchor, - faNetworkWired, -} from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; - -import { SizeProp } from '@fortawesome/fontawesome-svg-core'; - -import { FC, useMemo, useState } from 'react'; - import { ConnectionProvider, WalletProvider, @@ -37,31 +7,38 @@ import { WalletModalProvider, WalletMultiButton, } from '@solana/wallet-adapter-react-ui'; -import { ElectronAppStorageWalletAdapter } from './wallet-adapter/electronAppStorage'; - -import Account from './nav/Account'; -import Anchor from './nav/Anchor'; -import Validator from './nav/Validator'; -import ValidatorNetworkInfo from './nav/ValidatorNetworkInfo'; - -import { useAppDispatch, useAppSelector } from './hooks'; +// Default styles that can be overridden by your app +import '@solana/wallet-adapter-react-ui/styles.css'; +import * as sol from '@solana/web3.js'; +import isElectron from 'is-electron'; +import { FC, useMemo, useState } from 'react'; +import { Button, Form } from 'react-bootstrap'; +import Container from 'react-bootstrap/Container'; +import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; +import Tooltip from 'react-bootstrap/Tooltip'; +import { NavLink, Outlet, Route, Routes } from 'react-router-dom'; +import { ToastContainer } from 'react-toastify'; +import { logger } from '@/common/globals'; +import 'react-toastify/dist/ReactToastify.css'; +import './App.scss'; +import { getElectronStorageWallet } from './data/accounts/account'; +import { useAccountsState } from './data/accounts/accountState'; import { - useConfigState, - setConfigValue, ConfigKey, + setConfigValue, + useConfigState, } from './data/Config/configState'; -import { useAccountsState } from './data/accounts/accountState'; import ValidatorNetwork from './data/ValidatorNetwork/ValidatorNetwork'; import { netToURL, selectValidatorNetworkState, } from './data/ValidatorNetwork/validatorNetworkState'; -import { getElectronStorageWallet } from './data/accounts/account'; - -// Default styles that can be overridden by your app -require('@solana/wallet-adapter-react-ui/styles.css'); - -const logger = window.electron.log; +import { useAppDispatch, useAppSelector } from './hooks'; +import Account from './nav/Account'; +import Anchor from './nav/Anchor'; +import Validator from './nav/Validator'; +import ValidatorNetworkInfo from './nav/ValidatorNetworkInfo'; +import { ElectronAppStorageWalletAdapter } from './wallet-adapter/electronAppStorage'; // So we can electron declare global { @@ -73,140 +50,76 @@ declare global { } } -function TooltipNavItem({ - to = '/', - title = 'nav', - tooltipMessage = 'nav tooltip', - eventKey = 'default', - icon = faBook, - iconsize = '1x', -}) { +const TooltipNavItem: React.FC<{ + to: string; + tooltipMessage: string; + eventKey: string; + children?: React.ReactNode; +}> = ({ to, tooltipMessage, eventKey, children }) => { return ( {tooltipMessage}} > - - {title} + + {children} ); -} // TODO: work out how propTypes work with fontAwesome -TooltipNavItem.propTypes = { - to: PropTypes.string.isRequired, - title: PropTypes.string.isRequired, - tooltipMessage: PropTypes.string.isRequired, - eventKey: PropTypes.string.isRequired, - icon: PropTypes.element.isRequired, // instanceOf(IconDefinition).isRequired, - iconsize: PropTypes.string.isRequired, }; -// TODO: work out TooltipNavItem.defaults -function Sidebar() { - return ( - - - - ); -} +TooltipNavItem.defaultProps = { + tooltipMessage: 'placeholder', + to: '/', + children: , + eventKey: 'placeholder', +}; -function TopbarNavItems() { - if (isElectron()) { - return <>; - } +function NavigationIcons() { return ( <> - + + + - + > + + + + + {' '} + > + + ); } +function Sidebar() { + return ( + + ); +} + function Topbar() { return ( - - - Solana Workbench - - - - -
- - -
-
-
+
+ Solana Workbench +
+ {isElectron() ? null : } + + +
); } @@ -303,15 +216,19 @@ export const GlobalContainer: FC = () => { return <>Config Loading ...${accounts.loading}; } return ( -
+
- - - - - +
+ +
+ +
+ +
+
+
@@ -323,7 +240,7 @@ function App() { const config = useConfigState(); const accounts = useAccountsState(); - Object.assign(console, logger.functions); + Object.assign(console, logger?.functions); if (config.loading) { return <>Config Loading ...${accounts.loading}; @@ -332,7 +249,7 @@ function App() { return ; } return ( -
+ <> }> } /> @@ -345,8 +262,8 @@ function App() { /> - -
+ + ); } diff --git a/src/renderer/auto-imports.d.ts b/src/renderer/auto-imports.d.ts new file mode 100644 index 00000000..c62197e5 --- /dev/null +++ b/src/renderer/auto-imports.d.ts @@ -0,0 +1,19 @@ +// Generated by 'unplugin-auto-import' +export {} +declare global { + const IconMdiAnchor: typeof import('~icons/mdi/anchor.jsx')['default'] + const IconMdiBookOpenOutline: typeof import('~icons/mdi/book-open-outline.jsx')['default'] + const IconMdiCheck: typeof import('~icons/mdi/check.jsx')['default'] + const IconMdiChevronDown: typeof import('~icons/mdi/chevron-down.jsx')['default'] + const IconMdiCircle: typeof import('~icons/mdi/circle.jsx')['default'] + const IconMdiClose: typeof import('~icons/mdi/close.jsx')['default'] + const IconMdiContentCopy: typeof import('~icons/mdi/content-copy.jsx')['default'] + const IconMdiKey: typeof import('~icons/mdi/key.jsx')['default'] + const IconMdiPencil: typeof import('~icons/mdi/pencil.jsx')['default'] + const IconMdiStar: typeof import('~icons/mdi/star.jsx')['default'] + const IconMdiStarOutline: typeof import('~icons/mdi/star-outline.jsx')['default'] + const IconMdiTable: typeof import('~icons/mdi/table.jsx')['default'] + const IconMdiUnfoldMoreHorizontal: typeof import('~icons/mdi/unfold-more-horizontal.jsx')['default'] + const IconMdiVectorTriangle: typeof import('~icons/mdi/vector-triangle.jsx')['default'] + const IconMdiWarning: typeof import('~icons/mdi/warning.jsx')['default'] +} diff --git a/src/renderer/common/analytics.ts b/src/renderer/common/analytics.ts index b8ce3eb0..2fe19e09 100644 --- a/src/renderer/common/analytics.ts +++ b/src/renderer/common/analytics.ts @@ -1,10 +1,10 @@ import amplitude from 'amplitude-js'; -import store from 'renderer/store'; +import { logger } from '@/common/globals'; +import store from '@/store'; import { ConfigKey } from '../data/Config/configState'; const AMPLITUDE_KEY = 'f1cde3642f7e0f483afbb7ac15ae8277'; const AMPLITUDE_HEARTBEAT_INTERVAL = 3600000; -const logger = window.electron.log; amplitude.getInstance().init(AMPLITUDE_KEY); diff --git a/src/renderer/common/globals.ts b/src/renderer/common/globals.ts new file mode 100644 index 00000000..3c0a96f9 --- /dev/null +++ b/src/renderer/common/globals.ts @@ -0,0 +1,4 @@ +// eslint-disable-next-line import/prefer-default-export +export const logger = (() => { + return window.electron?.log; +})(); diff --git a/src/renderer/components/AccountView.tsx b/src/renderer/components/AccountView.tsx index b5d239b7..6182a9f0 100644 --- a/src/renderer/components/AccountView.tsx +++ b/src/renderer/components/AccountView.tsx @@ -1,18 +1,9 @@ -import { useState, useEffect } from 'react'; -import { - faTerminal, - faEdit, - faSave, - faCancel, - faKey, -} from '@fortawesome/free-solid-svg-icons'; +import { faTerminal } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import Container from 'react-bootstrap/Container'; -import ButtonGroup from 'react-bootstrap/ButtonGroup'; +import { useEffect, useState } from 'react'; import ButtonToolbar from 'react-bootstrap/ButtonToolbar'; -import EdiText from 'react-editext'; -import { useInterval, useAppSelector, useAppDispatch } from '../hooks'; - +import Container from 'react-bootstrap/Container'; +import { logger } from '@/common/globals'; import analytics from '../common/analytics'; import { AccountInfo } from '../data/accounts/accountInfo'; import { @@ -20,10 +11,10 @@ import { useAccountMeta, } from '../data/accounts/accountState'; import { - truncateLamportAmount, + getAccount, getHumanName, renderData, - getAccount, + truncateLamportAmount, } from '../data/accounts/getAccount'; import { Net, @@ -31,12 +22,11 @@ import { netToURL, selectValidatorNetworkState, } from '../data/ValidatorNetwork/validatorNetworkState'; +import { useAppDispatch, useAppSelector, useInterval } from '../hooks'; +import AirDropSolButton from './AirDropSolButton'; +import EditableText from './base/EditableText'; import InlinePK from './InlinePK'; - import TransferSolButton from './TransferSolButton'; -import AirDropSolButton from './AirDropSolButton'; - -const logger = window.electron.log; const explorerURL = (net: Net, address: string) => { switch (net) { @@ -100,146 +90,120 @@ function AccountView(props: { pubKey: string | undefined }) { return ( - +
- +
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- Editable Alias -
-
-
- - } - saveButtonContent={} - cancelButtonContent={ - - } - /> - -
- Pubkey - - - {pubKey ? : 'None selected'} - -
- SOL - - - {account ? truncateLamportAmount(account) : 0} - -
- Executable - - {account?.accountInfo?.executable ? ( -
- - Yes -
- ) : ( - - No - - )} -
- Private key known - - {accountMeta?.privatekey ? ( -
- - Yes -
- ) : ( - - No - - )} -
- Explorer - - - {account ? ( - - analytics('clickExplorerLink', { net }) - } - href={explorerURL( - net, - account.accountId.toString() - )} - target="_blank" - className="sol-link" - rel="noreferrer" - > - Link - - ) : ( - 'No onchain account' - )} - -
-
-
-
-
- Data -
-
-
-                {renderData(account)}
-              
-
-
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Editable Alias +
+
+
+ + + +
+ Pubkey + + + {pubKey ? ( + + ) : ( + 'None selected' + )} + +
+ SOL + + {account ? truncateLamportAmount(account) : 0} +
+ Executable + + {account?.accountInfo?.executable ? ( +
+ + Yes +
+ ) : ( + No + )} +
+ Private key known + + {accountMeta?.privatekey ? ( +
+ + Yes +
+ ) : ( + No + )} +
+ Explorer + + + {account ? ( + analytics('clickExplorerLink', { net })} + href={explorerURL(net, account.accountId.toString())} + target="_blank" + className="sol-link" + rel="noreferrer" + > + Link + + ) : ( + 'No onchain account' + )} + +
+
+
+
+ Data +
+
+ + {renderData(account)} +
diff --git a/src/renderer/components/AirDropSolButton.tsx b/src/renderer/components/AirDropSolButton.tsx index 9d01817d..3a22f2f7 100644 --- a/src/renderer/components/AirDropSolButton.tsx +++ b/src/renderer/components/AirDropSolButton.tsx @@ -1,16 +1,13 @@ -import { useState, useEffect } from 'react'; - -import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; -import Popover from 'react-bootstrap/Popover'; +import { useEffect, useState } from 'react'; +import { Col, Row } from 'react-bootstrap'; import Button from 'react-bootstrap/Button'; import Form from 'react-bootstrap/Form'; -import { Row, Col } from 'react-bootstrap'; +import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; +import Popover from 'react-bootstrap/Popover'; import { toast } from 'react-toastify'; -import { useAppSelector } from '../hooks'; - -import { selectValidatorNetworkState } from '../data/ValidatorNetwork/validatorNetworkState'; - import { airdropSol } from '../data/accounts/account'; +import { selectValidatorNetworkState } from '../data/ValidatorNetwork/validatorNetworkState'; +import { useAppSelector } from '../hooks'; function AirDropPopover(props: { pubKey: string | undefined }) { const { pubKey } = props; @@ -98,7 +95,9 @@ function AirDropSolButton(props: { pubKey: string | undefined }) { overlay={AirDropPopover({ pubKey })} rootClose > - + ); } diff --git a/src/renderer/components/CopyIcon.tsx b/src/renderer/components/CopyIcon.tsx index 08c907b4..bea3bc6f 100644 --- a/src/renderer/components/CopyIcon.tsx +++ b/src/renderer/components/CopyIcon.tsx @@ -1,5 +1,3 @@ -import { faCopy } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useState } from 'react'; import { OverlayTrigger, Tooltip } from 'react-bootstrap'; @@ -36,9 +34,9 @@ function CopyIcon(props: { writeValue: string }) { // eslint-disable-next-line @typescript-eslint/no-unused-vars _ ) => window.setTimeout(() => setCopyTooltipText('Copy'), 500)} - className="icon-interactive ms-1" + className="icon-interactive p-2 hover:bg-contrast/10 rounded-full inline-flex items-center justify-center cursor-pointer" > - + ); diff --git a/src/renderer/components/InlinePK.tsx b/src/renderer/components/InlinePK.tsx index 16784c1c..94bdb3fe 100644 --- a/src/renderer/components/InlinePK.tsx +++ b/src/renderer/components/InlinePK.tsx @@ -1,29 +1,32 @@ +import classnames from 'classnames'; import { ACCOUNTS_NONE_KEY } from '../data/accounts/accountInfo'; - import CopyIcon from './CopyIcon'; -const prettifyPubkey = (pk = '') => { +const prettifyPubkey = (pk = '', formatLength: number) => { if (pk === null) { // cope with bad data in config return ''; } return pk !== ACCOUNTS_NONE_KEY - ? `${pk.slice(0, 4)}…${pk.slice(pk.length - 4, pk.length)}` + ? `${pk.slice(0, formatLength)}…${pk.slice( + pk.length - formatLength, + pk.length + )}` : ''; }; -function InlinePK(props: { pk: string; className?: string }) { - const { pk, className } = props; +const InlinePK: React.FC<{ + pk: string; + className?: string; + format?: boolean; + formatLength?: number; +}> = ({ pk, className, format, formatLength }) => { return ( - - {prettifyPubkey(pk)} + + {format ? prettifyPubkey(pk, formatLength || 4) : pk} ); -} - -InlinePK.defaultProps = { - className: '', }; export default InlinePK; diff --git a/src/renderer/components/LogView.tsx b/src/renderer/components/LogView.tsx index 03026a54..452c81e5 100644 --- a/src/renderer/components/LogView.tsx +++ b/src/renderer/components/LogView.tsx @@ -1,11 +1,12 @@ -import { useEffect, useState } from 'react'; import * as sol from '@solana/web3.js'; -import { useAppSelector } from '../hooks'; +import { useEffect, useState } from 'react'; +import { logger } from '@/common/globals'; import { NetStatus, netToURL, selectValidatorNetworkState, } from '../data/ValidatorNetwork/validatorNetworkState'; +import { useAppSelector } from '../hooks'; export interface LogSubscriptionMap { [net: string]: { @@ -13,7 +14,6 @@ export interface LogSubscriptionMap { solConn: sol.Connection; }; } -const logger = window.electron.log; // TODO: make this selectable - Return information at the selected commitment level // [possible values: processed, confirmed, finalized] @@ -73,17 +73,11 @@ function LogView() { }, [net, status]); return ( -
-