UNPKG

@aladas-org/cryptocalc

Version:
1,072 lines (891 loc) 46.5 kB
// ==================================================================================== // =============================== electron_main.js =============================== // ==================================================================================== // https://www.electronjs.org/docs/latest/tutorial/quick-start "use strict"; // =============================== ElectronMain class =============================== // NB: "Singleton" class // * static GetInstance() // createBrowserWindow( url ) // * getMainWindow() // * getMenuTemplate() // * createWindow() // updateWindowTitle() // // * async doFileNew() // * doFileSave() // * async doFileOpen() // * async doFileRead( json_data ) // // * getNewFortuneCookie() // * toggleDebugPanel() // * getUserSelectedFile() // // readOptionsFile() // * async loadOptions() // * async setDefaultOptions() // * async updateOptions( options_data ) // * async saveOptions( options_data ) // * async resetOptions() // // * setCallbacks() // ------------------------------------------------------ const MAIN_WINDOW_WIDTH = 1040; // NB: 'width' is wider because of 'Cardano' const MAIN_WINDOW_HEIGHT = 630; const { app, Menu, BrowserWindow, ipcMain, shell, remote, dialog } = require('electron'); // https://stackoverflow.com/questions/35916158/how-to-prevent-multiple-instances-in-electron require('v8-compile-cache'); const fs = require('fs'); const path = require('path'); const { v4: uuidv4 } = require('uuid'); const checkInternetConnected = require('check-internet-connected'); const PasswordGenerator = require('generate-password'); const { _CYAN_, _RED_, _PURPLE_, _YELLOW_, _END_ } = require('../util/color/color_console_codes.js'); const { pretty_func_header_log, pretty_log } = require('../util/log/log_utils.js'); const { Skribi } = require('../util/log/skribi.js'); const { APP_VERSION, PROGRAM, ELECTRON_LAUNCHER, EXE_LAUNCHER, WITS_PATH, PATH, ARGS, } = require('../const_keywords.js'); const { CMD_OPEN_WALLET, VIEW_TOGGLE_DEVTOOLS, TOOLS_OPTIONS, ToMain_RQ_QUIT_APP, ToMain_RQ_LOG_2_MAIN, ToMain_RQ_LOG_2_MAIN_SYNC, ToMain_RQ_EXEC_CMD, ToMain_RQ_SET_WINDOW_TITLE, ToMain_RQ_TOGGLE_DEBUG_PANEL, ToMain_RQ_NEW_WALLET_INFO, ToMain_RQ_OPEN_WALLET_INFO, ToMain_RQ_SAVE_WALLET_INFO, ToMain_RQ_OPEN_URL, ToMain_RQ_SHOW_OUTPUT_FOLDER_IN_EXPLORER, ToMain_RQ_LOAD_IMG_FROM_FILE, ToMain_RQ_DRAW_RND_CRYPTO_LOGO, ToMain_RQ_MNEMONICS_TO_ENTROPY_INFO, ToMain_RQ_MNEMONICS_TO_HD_WALLET_INFO, ToMain_RQ_GET_SIMPLE_WALLET, ToMain_RQ_ENTROPY_TO_MNEMONICS, ToMain_RQ_ENTROPY_TO_CHECKSUM, ToMain_RQ_ENTROPY_SRC_TO_ENTROPY, ToMain_RQ_MNEMONICS_AS_4LETTER, ToMain_RQ_MNEMONICS_AS_TWO_PARTS, ToMain_RQ_GET_UUID, ToMain_RQ_GET_L10N_KEYPAIRS, ToMain_RQ_GET_L10N_MSG, ToMain_RQ_SET_MENU_ITEM_STATE, ToMain_RQ_CHECK_MNEMONICS, ToMain_RQ_MNEMONICS_TO_WORD_INDEXES, ToMain_RQ_GUESS_MNEMONICS_LANG, ToMain_RQ_SAVE_OPTIONS, ToMain_RQ_RESET_OPTIONS, ToMain_RQ_UPDATE_OPTIONS, ToMain_RQ_GET_FORTUNE_COOKIE, ToMain_RQ_GENERATE_PASSWORD, ToMain_RQ_GET_HD_WALLET, FromMain_DID_FINISH_LOAD, FromMain_EXEC_CMD, FromMain_FILE_NEW, FromMain_FILE_OPEN, FromMain_FILE_SAVE, FromMain_HELP_ABOUT, FromMain_TOOLS_OPTIONS_DIALOG, FromMain_UPDATE_OPTIONS, FromMain_SEND_IMG_URL, FromMain_SET_FORTUNE_COOKIE, FromMain_SET_VARIABLE, FromMain_INTERNET_CONNECTED } = require('../const_events.js'); const { ENTROPY_SOURCE_IMG_ID } = require('../_renderer/const_renderer.js'); const { DEFAULT_OPTIONS } = require('../crypto/const_default_options.js'); const { getShortenedString } = require('../util/values/string_utils.js'); const { FileUtils } = require('../util/system/file_utils.js'); const { Bip39Utils } = require('../crypto/bip39_utils.js'); const { getRandomInt } = require('../crypto/hex_utils.js'); const { getFortuneCookie } = require('../util/fortune/fortune.js'); const { L10nUtils } = require('../L10n/L10n_utils.js'); const { Bip32Utils } = require('../crypto/HDWallet/bip32_utils.js'); const { HDWallet } = require('../crypto/HDWallet/hd_wallet.js'); const { SimpleWallet } = require('../crypto/SimpleWallet/simple_wallet.js'); const { MainModel } = require('./main_model.js'); const DEFAULT_APP_CONFIG = { "ToFile": true }; // DEFAULT_APP_CONFIG const gotTheLock = app.requestSingleInstanceLock(); const error_handler = (err) => { if (err) return Skribi.log("error: " + err); Skribi.log('saving file... '+ filename); }; // error_handler() class ElectronMain { //static #key = {}; static #key = Symbol(); static #_Singleton = new ElectronMain( this.#key ); static #_InstanceCount = 0; static GetInstance() { if( ElectronMain.#_Singleton == undefined ) { this.#_Singleton = new ElectronMain(); if (this.#_InstanceCount > 0) { throw new TypeError("ElectronMain constructor called more than once."); } this.#_InstanceCount++; } return ElectronMain.#_Singleton; } // ElectronMain.GetInstance() // ** Private constructor ** constructor( key ) { if ( key !== ElectronMain.#key ) { throw new TypeError("ElectronMain constructor is private."); } this.cryptocalc_version = "x.x.x"; this.app_config = DEFAULT_APP_CONFIG; this.cmd_line = {}; this.cmd_line[PROGRAM] = ELECTRON_LAUNCHER; this.cmd_line[PATH] = "."; this.cmd_line[ARGS] = ""; this.DidFinishLoad_FiredCount = 0; this.Show_DebugPanel = false; this.MainWindow = undefined; this.Options = {}; this.SupportedBlockchains = {}; this.FirstImageAsEntropySource = true; } // ** Private constructor ** createBrowserWindow( url ) { const win = new BrowserWindow( { height: 900, width: 1200 } ); win.loadURL( url ); } // createBrowserWindow getMainWindow() { return this.MainWindow; } // getMainWindow() isLaunchedFromExe() { if ( this.cmd_line[PROGRAM] == EXE_LAUNCHER ) return true; return false; } // isLaunchedFromExe() getCmdLineArgs() { let nb_args = process.argv.length; if ( nb_args > 0 ) { this.cmd_line[PROGRAM] = path.basename( process.argv[0] ); }; if ( nb_args > 1 ) { this.cmd_line[PATH] = process.argv[1]; }; if ( nb_args > 2 ) { this.cmd_line[ARGS] = process.argv[2]; }; let msg = "nb_args: " + nb_args + PROGRAM + ": " + this.cmd_line[PROGRAM] + PATH + ": " + this.cmd_line[PATH] + ARGS + ": " + this.cmd_line[ARGS]; return msg; } // getCmdLineArgs() // https://github.com/electron/electron/issues/19775 // https://stackoverflow.com/questions/44391448/electron-require-is-not-defined getMenuTemplate() { let ELECTRON_MAIN_MENU_TEMPLATE = [ { label: L10nUtils.GetLocalizedMsg("File"), submenu: [ { label: L10nUtils.GetLocalizedMsg("New"), async click() { await ElectronMain.GetInstance().doFileNew(); } }, { label: L10nUtils.GetLocalizedMsg("Open"), async click() { await ElectronMain.GetInstance().doFileOpen(); } }, { label: L10nUtils.GetLocalizedMsg("Save"), click() { ElectronMain.GetInstance().doFileSave(); }, id: "file_save_menu_item_id" }, { label: L10nUtils.GetLocalizedMsg("SaveAs"), click() { ElectronMain.GetInstance().doFileSaveAs(); }, id: "file_save_as_menu_item_id" }, { label: L10nUtils.GetLocalizedMsg("Quit"), click() { app.quit(); } } ] }, { label: L10nUtils.GetLocalizedMsg("View"), submenu: [ { label: L10nUtils.GetLocalizedMsg("ToggleDebug"), type: 'checkbox', click() { pretty_func_header_log( "[Electron]", VIEW_TOGGLE_DEVTOOLS ); ElectronMain.GetInstance().toggleDebugPanel(); } } ] }, { label: L10nUtils.GetLocalizedMsg("Tools"), submenu: [ { label: "Options...", click() { pretty_func_header_log( "[Electron]", TOOLS_OPTIONS ); ElectronMain.GetInstance().getMainWindow() .webContents.send ( 'fromMain', [ FromMain_TOOLS_OPTIONS_DIALOG, ElectronMain.GetInstance().Options ] ); } } ] }, { label: L10nUtils.GetLocalizedMsg("Help"), //"Help" submenu: [ { label: L10nUtils.GetLocalizedMsg("Resources"), submenu: [ { label: "Setup guide and User's Manual", click() { ElectronMain.GetInstance() .createBrowserWindow("https://github.com/ALADAS-org/Cryptocalc/blob/master/README.md"); } }, { label: "Ian Coleman BIP39", click() { // https://stackoverflow.com/questions/53390798/opening-new-window-electron ElectronMain.GetInstance() .createBrowserWindow("https://iancoleman.io/bip39/"); } }, { label: "Guarda", click() { // https://stackoverflow.com/questions/53390798/opening-new-window-electron ElectronMain.GetInstance() .createBrowserWindow("https://guarda.com/"); } } ] }, { label: L10nUtils.GetLocalizedMsg("About"), //'About...', click() { ElectronMain.GetInstance().getMainWindow() .webContents.send('fromMain', [ FromMain_HELP_ABOUT ]); } } ] } ]; // menu_template return ELECTRON_MAIN_MENU_TEMPLATE; } // getMenuTemplate() //==================== createWindow() ==================== // https://stackoverflow.com/questions/44391448/electron-require-is-not-defined createWindow() { pretty_func_header_log( "ElectronMain.createWindow" ); // Hide 'Security Warning' process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'; this.MainWindow = new BrowserWindow( { width: MAIN_WINDOW_WIDTH, height: MAIN_WINDOW_HEIGHT, icon: path.join(__dirname, "../../icons/Cryptocalc_Icon.png"), webPreferences: { contextIsolation: true, // NB: 'true' is default value but keep it there anyway preload: path.join(__dirname, "./preload.js") } } ); const menu_bar = Menu.buildFromTemplate( this.getMenuTemplate() ); Menu.setApplicationMenu( menu_bar ); // https://www.electronjs.org/docs/latest/api/web-contents#instance-events // https://stackoverflow.com/questions/42284627/electron-how-to-know-when-renderer-window-is-ready // Note: index.html loaded twice (first index.html redirect) // ==================== 'did-finish-load' event handler ==================== this.MainWindow.webContents.on( 'did-finish-load', async () => { Skribi.Initialize( this.app_config ); //Skribi.log(">> " + _CYAN_ + "[*Electron*] " + _YELLOW_ + " did-finish-load --" + _END_); // Note: must load twice (I suspect because of first 'index.html' redirect) this.DidFinishLoad_FiredCount++; this.app_config = this.readAppConfig(); //Skribi.log(">> " + _CYAN_ + "eMain.evtH('did-finish-load')> FiredCount: " + this.DidFinishLoad_FiredCount + _END_ ); if ( this.DidFinishLoad_FiredCount == 2 ) { Skribi.log(">> " + _CYAN_ + "eMain.evtH('" + _YELLOW_ + "'did-finish-load'" + _CYAN_ + ")> this.DidFinishLoad_FiredCount == 2" + _END_ ); this.getCmdLineArgs(); // Skribi.log(" ** this.cmd_line: " + JSON.stringify(this.cmd_line) ); Skribi.log(">> " + _CYAN_ + "eMain.evtH('" + _YELLOW_ + "'did-finish-load'" + _CYAN_ + ")> this.cmd_line[PROGRAM]: " + _END_ + this.cmd_line[PROGRAM]); //---------- Set 'Cryptocalc_version' in Renderer GUI ---------- this.cryptocalc_version = MainModel.GetInstance().getAppVersion(); //Skribi.log(">> " + _CYAN_ + "eMain.evtH('" + _YELLOW_ + "'did-finish-load'" // + _CYAN_ + ")> cryptocalc_version: " + _END_ + this.cryptocalc_version); this.updateWindowTitle(); //---------- Set 'this.cryptocalc_version' in Renderer GUI this.MainWindow.webContents.send ( "fromMain", [ FromMain_DID_FINISH_LOAD ] ); //Skribi.log(" Send : " + FromMain_SET_VARIABLE + " = " + Cryptocalc_version); this.MainWindow.webContents.send ( "fromMain", [ FromMain_SET_VARIABLE, APP_VERSION, this.cryptocalc_version ] ); // https://stackoverflow.com/questions/31749625/make-a-link-from-electron-open-in-browser // Open urls in the user's browser // nB: Triggered by 'RendererGUI.OnExploreWallet()' this.MainWindow.webContents.setWindowOpenHandler( (edata) => { shell.openExternal(edata.url); return { action: "deny" }; } ); this.setCallbacks(); this.Options = await this.loadOptions(); // =================== Open file by association =================== let wits_path = this.cmd_line[PATH]; //let wits_path = "D:\\_010_Michel\\_00_Lab\\_11_Daniel Rodet\\_00_Lab\\_01_github\\" // + "Cryptocalc\\_output\\2024_09_26_0h-6m-11s-5_ADA_EN\\wallet_info.wits"; if ( wits_path.endsWith(".wits") ) { Skribi.log(">> " + _CYAN_ + "eMain.evtH('" + _RED_ + "'did-finish-load'" + _CYAN_ + ")> SET_VARIABLE(" + WITS_PATH + ") in Renderer" + _END_ + wits_path); // await this.openWits( wits_path ); await this.MainWindow.webContents.send ( "fromMain", [ FromMain_SET_VARIABLE, WITS_PATH, wits_path ] ); } // =================== Open file by association // Note: will require 'Main' to 'Open Wits' if: // - 'wits_path' in Renderer is not empty // and // - not 'first_time' in Renderer await this.doFileNew(); } } // 'did-finish-load' callback ); // ==================== 'did-finish-load' event handler this.MainWindow.loadFile( './index.html' ); } // createWindow() async openWits( wits_path ) { if ( wits_path == undefined ) { wits_path = "D:\\_010_Michel\\_00_Lab\\_11_Daniel Rodet\\_00_Lab\\_01_github\\" + "Cryptocalc\\_output\\2024_09_26_0h-6m-11s-5_ADA_EN\\wallet_info.wits"; } Skribi.log(">> " + _CYAN_ + "eMain.evtH('" + _YELLOW_ + "'openWits'" + _CYAN_ + ")> *TEST* Trying to Open '.wits': " + _END_ + wits_path); if ( wits_path.endsWith(".wits") ) { const json_data_str = fs.readFileSync( wits_path, { encoding: 'utf8', flag: 'r' }); let wits_json_data = JSON.parse( json_data_str ); this.Options = await this.loadOptions(); await this.MainWindow.webContents.send ( "fromMain", [ FromMain_EXEC_CMD, CMD_OPEN_WALLET, wits_json_data ] ); } } // openWits() updateWindowTitle( coin, wallet_mode ) { //pretty_func_header_log( "ElectronMain.updateWindowTitle" ); let window_title = 'Cryptocalc ' + this.cryptocalc_version; if ( wallet_mode != undefined && wallet_mode != "" ) window_title += " - " + wallet_mode; if ( coin != undefined && coin != "" ) window_title += ": " + coin; this.MainWindow.setTitle( window_title ); } // updateWindowTitle() async doFileNew() { Skribi.log( ">> " + _CYAN_ + "ElectronMain.doFileNew" + _END_ ); //this.Options = this.readOptionsFile(); this.Options = await this.loadOptions(); await this.MainWindow.webContents .send( "fromMain", [ FromMain_FILE_NEW, this.Options ] ); } // doFileNew() doFileSave() { // pretty_func_header_log( "ElectronMain.doFileSave" ); Skribi.log( ">> " + _CYAN_ + "ElectronMain.doFileSave" + _END_ ); this.MainWindow.webContents.send( "fromMain", [ FromMain_FILE_SAVE ] ); } // doFileSave() doFileSaveAs() { // pretty_func_header_log( "ElectronMain.doFileSaveAs" ); Skribi.log( ">> " + _CYAN_ + "ElectronMain.doFileSaveAs" + _END_ ); this.MainWindow.webContents.send( "fromMain", [ FromMain_FILE_SAVE ] ); } // doFileSaveAs() async doFileOpen( in_file_path ) { // pretty_func_header_log( "ElectronMain.doFileOpen" ); Skribi.log( ">> " + _CYAN_ + "ElectronMain.doFileOpen" + _END_ ); if ( in_file_path == undefined ) { let input_path = app.getAppPath() + "\\_output"; in_file_path = await this.selectFileWithDialogBox( input_path, "Wallet Informations", "wits" ); pretty_log( "in_file_path", in_file_path ); if ( in_file_path == "" ) return; } const json_data_str = fs.readFileSync( in_file_path, { encoding: 'utf8', flag: 'r' }); //pretty_log( "json_data_str", json_data_str ); let json_data = JSON.parse( json_data_str ); await this.MainWindow.webContents .send( "fromMain", [ FromMain_FILE_OPEN, json_data ] ); } // doFileOpen() async selectFileWithDialogBox( input_path, label, extension ) { pretty_func_header_log( "ElectronMain.selectFileWithDialogBox" ); let in_file_path = ""; // Modal window let result = await dialog.showOpenDialog( this.MainWindow, { defaultPath: input_path, filters: [ { name: label, extensions: [extension] } ], properties: ['openFile'] }); //.then( result => { //pretty_log( "result", JSON.stringify(result)); if ( result.filePaths.length > 0 ) { //pretty_log( "filePaths", JSON.stringify(result.filePaths)); in_file_path = result.filePaths[0]; // pretty_log( "in_file_path", in_file_path); } //}).catch(err => { // Skribi.log(err) //}); // pretty_log( "in_file_path", in_file_path); return in_file_path; } // selectFileWithDialogBox() // https://stackoverflow.com/questions/43991267/electron-open-file-directory-in-specific-application showFolderInExplorer( folder_path) { pretty_func_header_log( "ElectronMain.showFolderInExplorer" ); shell.openPath( folder_path ); } // showFolderInExplorer() // File/Import/Random Fortune Cookie getNewFortuneCookie() { pretty_func_header_log( "ElectronMain.getNewFortuneCookie" ); let fortune_cookie = getFortuneCookie(); Skribi.log(" fortune_cookie: " + getShortenedString( fortune_cookie) ); this.MainWindow.webContents .send( "fromMain", [ FromMain_SET_FORTUNE_COOKIE, fortune_cookie ] ); } // getNewFortuneCookie() toggleDebugPanel() { pretty_func_header_log( "ElectronMain.toggleDebugPanel" ); this.Show_DebugPanel = ! this.Show_DebugPanel; if ( this.Show_DebugPanel ) this.MainWindow.webContents.openDevTools(); else this.MainWindow.webContents.closeDevTools(); } // toggleDebugPanel() readAppConfig() { pretty_func_header_log( "ElectronMain.readAppConfig" ); let app_config = DEFAULT_APP_CONFIG; let config_path = app.getAppPath() + '/www/config'; let app_config_path = config_path + '/app_config.json'; if ( fs.existsSync( app_config_path ) ) { const app_config_str = fs.readFileSync( app_config_path ); if ( app_config_str != "" && app_config_str != "[]" && app_config_str != "{}" ) { app_config = JSON.parse( app_config_str ); } } return app_config; } // readAppConfig() readOptionsFile() { pretty_func_header_log( "ElectronMain.readOptionsFile" ); let current_options = DEFAULT_OPTIONS; let config_path = app.getAppPath() + '/www/config'; //Skribi.log("config_path: " + config_path); let options_path = config_path + '/options.json'; if ( fs.existsSync( options_path ) ) { const options_str = fs.readFileSync( options_path ); //Skribi.log(" options_str: " + options_str); if ( options_str != "" && options_str != "[]" && options_str != "{}" ) { current_options = JSON.parse( options_str ); } } return current_options; } // readOptionsFile() async loadOptions() { pretty_func_header_log( "ElectronMain.loadOptions" ); this.Options = this.readOptionsFile(); //Skribi.log(" A this.Options: " + JSON.stringify( this.Options )); await this.MainWindow.webContents .send('fromMain', [ FromMain_UPDATE_OPTIONS, this.Options ]); return this.Options; } // async loadOptions() async setDefaultOptions() { pretty_func_header_log( "ElectronMain.setDefaultOptions" ); this.Options = DEFAULT_OPTIONS; await this.saveOptions( this.Options ); } // async setDefaultOptions() async updateOptions( options_data ) { pretty_func_header_log( "ElectronMain.updateOptions" ); this.Options = options_data; Skribi.log(" this.Options: " + JSON.stringify( this.Options )); } // async updateOptions() async saveOptions( options_data ) { pretty_func_header_log( "ElectronMain.saveOptions" ); this.Options = options_data; let config_path = app.getAppPath() + '/www/config'; let options_path = config_path + '/options.json'; fs.writeFileSync( options_path, JSON.stringify( this.Options ) ); Skribi.log(" B this.Options: " + JSON.stringify( this.Options )); await this.MainWindow.webContents .send('fromMain', [ FromMain_UPDATE_OPTIONS, options_data ]); } // async saveOptions() async resetOptions() { pretty_func_header_log( "ElectronMain.resetOptions" ); let config_path = app.getAppPath() + '/www/config'; let default_options_path = config_path + '/defaults/options.json'; let default_options_str = fs.readFileSync( default_options_path ).toString(); Skribi.log(" default_options_str: " + default_options_str); this.Options = JSON.parse( default_options_str ); await this.saveOptions( this.Options ); } // async resetOptions() setCallbacks() { Skribi.log(">> " + _CYAN_ + "ElectronMain.setCallbacks" + _END_); // ====================== ToMain_RQ_QUIT_APP ====================== //Skribi.log(">> register: " + ToMain_RQ_QUIT_APP); // called like this by Renderer: await window.ipcMain.QuitApp(data) ipcMain.handle( ToMain_RQ_QUIT_APP, async (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_QUIT_APP ); app.quit(); }); // "ToMain:Request/quit_app" event handler // ====================== ToMain_RQ_EXEC_CMD ====================== // called like this by Renderer: await window.ipcMain.ExecuteCommand(data) ipcMain.handle( ToMain_RQ_EXEC_CMD, async (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_EXEC_CMD ); const { cmd_name, cmd_args } = data; Skribi.log( "eMain.evtH('ExecCmd')> cmd_name: " + cmd_name ); Skribi.log( "eMain.evtH('ExecCmd')> cmd_args: " + cmd_args ); if ( cmd_name == CMD_OPEN_WALLET ) { await ElectronMain.GetInstance().openWits( cmd_args ); } }); // "ToMain:Request/ExecCmd" event handler // ====================== ToMain_RQ_LOG_2_MAIN_SYNC ====================== // called like this by Renderer: window.ipcMain.logToMain(data) ipcMain.on( ToMain_RQ_LOG_2_MAIN, (event, data) => { Skribi.log( data ); }); // "ToMain:Request/log2main" event handler // ====================== ToMain_RQ_LOG_2_MAIN_SYNC ====================== // called like this by Renderer: await window.ipcMain.logToMainSync(data) ipcMain.handle( ToMain_RQ_LOG_2_MAIN_SYNC, async (event, data) => { Skribi.log( data ); }); // "ToMain:Request/log2main_sync" event handler // ====================== ToMain_RQ_SET_WINDOW_TITLE ====================== // called like this by Renderer: window.ipcMain.SetWindowTitle(data) ipcMain.on( ToMain_RQ_SET_WINDOW_TITLE, (event, data) => { // pretty_func_header_log( "[Electron]", ToMain_RQ_SET_WINDOW_TITLE ); const { coin, wallet_mode } = data; ElectronMain.GetInstance().updateWindowTitle( coin, wallet_mode ); }); // "ToMain:Request/set_window_title" event handler // ========================== ToMain_RQ_TOGGLE_DEBUG_PANEL ========================== // called like this by Renderer: window.ipcMain.ToggleDebugPanel(data) ipcMain.on( ToMain_RQ_TOGGLE_DEBUG_PANEL, (event, data) => { ElectronMain.GetInstance().toggleDebugPanel(); }); // "ToMain:Request/toggle_debug_panel" event handler // ==================== ToMain_RQ_SHOW_OUTPUT_FOLDER_IN_EXPLORER ==================== // called like this by Renderer: window.ipcMain.ShowOutputFolderInExplorer() ipcMain.on( ToMain_RQ_SHOW_OUTPUT_FOLDER_IN_EXPLORER, (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_SHOW_OUTPUT_FOLDER_IN_EXPLORER ); let output_path = app.getAppPath() + "\\_output"; this.showFolderInExplorer( output_path ); }); // "ToMain:Request/show_output_folder_in_explorer" event handler // ====================== ToMain_RQ_OPEN_URL ====================== // called like this by Renderer: window.ipcMain.OpenURL(url) ipcMain.on( ToMain_RQ_OPEN_URL, (event, url) => { pretty_func_header_log( "[Electron]", ToMain_RQ_OPEN_URL ); Skribi.log("eMain.evtH('OpnURL')> " + url); // https://stackoverflow.com/questions/31749625/make-a-link-from-electron-open-in-browser this.MainWindow.location = url; }); // "ToMain:Request/open_URL" event handler // ====================== ToMain_RQ_NEW_WALLET_INFO ====================== //Skribi.log(">> register: " + ToMain_RQ_NEW_WALLET_INFO); // called like this by Renderer: await window.ipcMain.NewWalletInfo( data ) ipcMain.handle( ToMain_RQ_NEW_WALLET_INFO, async ( event, data ) => { pretty_func_header_log( "[Electron]", ToMain_RQ_NEW_WALLET_INFO ); Skribi.log( "eMain.evtH('NewWinf')>" ); await ElectronMain.GetInstance().doFileNew(); }); // "ToMain:Request/new_wallet_info" event handler // ====================== ToMain_RQ_OPEN_WALLET_INFO ====================== // called like this by Renderer: await window.ipcMain.OpenWalletInfo( data ) ipcMain.handle( ToMain_RQ_OPEN_WALLET_INFO, async ( event, data ) => { pretty_func_header_log( "[Electron]", ToMain_RQ_OPEN_WALLET_INFO ); await ElectronMain.GetInstance().doFileOpen(); }); // "ToMain:Request/open_wallet_info" event handler // ====================== ToMain_RQ_SAVE_WALLET_INFO ====================== //Skribi.log(">> register: " + ToMain_RQ_SAVE_WALLET_INFO); // called like this by Renderer: window.ipcMain.SaveWalletInfo( data ) ipcMain.on( ToMain_RQ_SAVE_WALLET_INFO, ( event, crypto_info ) => { pretty_func_header_log( "[Electron]", ToMain_RQ_SAVE_WALLET_INFO ); Skribi.log( "eMain.evtH('SaveWinf')>" ); MainModel.GetInstance().saveWalletInfo( crypto_info ); }); // "ToMain:Request/save_wallet_info" event handler // ====================== ToMain_RQ_RESET_OPTIONS ====================== // called like this by Renderer: await window.ipcMain.ResetOptions( data ) ipcMain.handle( ToMain_RQ_RESET_OPTIONS, async (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_RESET_OPTIONS ); Skribi.log( "eMain.evtH('RstOpt')>" ); ElectronMain.GetInstance().resetOptions(); }); // "ToMain:Request/reset_options" event handler // ====================== ToMain_RQ_UPDATE_OPTIONS ====================== // called like this by Renderer: await window.ipcMain.UpdateOptions( options_data ) ipcMain.handle( ToMain_RQ_UPDATE_OPTIONS, async (event, options_data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_UPDATE_OPTIONS ); if ( options_data == undefined ) { Skribi.log(">> " + _RED_ + ToMain_RQ_QUIT_APP + _END_); app.quit(); } // Skribi.log( "eMain.evtH('UpOpt')> options_data: " + JSON.stringify(options_data)); this.Options = options_data; Skribi.log( "eMain.evtH('UpOpt')> this.Options: " + JSON.stringify( this.Options )); await this.MainWindow.webContents .send('fromMain', [ FromMain_UPDATE_OPTIONS, this.Options ]); }); // "ToMain:Request/update_options" event handler // ====================== ToMain_RQ_SAVE_OPTIONS ====================== //Skribi.log(">> register: " + ToMain_RQ_SAVE_OPTIONS); // called like this by Renderer: await window.ipcMain.SaveOptions( options_data ) ipcMain.handle( ToMain_RQ_SAVE_OPTIONS, async (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_SAVE_OPTIONS ); let options_data = data; pretty_log( "eMain.evtH('SaveOpt')> options_data: " + JSON.stringify(options_data) ); ElectronMain.GetInstance().saveOptions( options_data ); pretty_log( "eMain.evtH('SaveOpt')> this.Options: " + JSON.stringify( this.Options ) ); await this.MainWindow.webContents .send( 'fromMain', [ FromMain_UPDATE_OPTIONS, options_data ] ); }); // "ToMain:Request/save_options" event handler const loadImageFromFile = ( image_file_path ) => { let img_data_asURL = fs.readFileSync( image_file_path, {encoding: 'base64'} ); let image_file_path_items = image_file_path.split('.'); let image_file_extension = "png"; let items_count = image_file_path_items.length; if (items_count > 1) { image_file_extension = image_file_path_items[items_count-1].toLowerCase(); if (image_file_extension == "jpg") { image_file_extension = "jpeg"; } else if (image_file_extension == "svg") { image_file_extension = "svg+xml"; } } //Skribi.log(" image_file_extension: " + image_file_extension); //Skribi.log(" img_data_asURL:\n" + img_data_asURL); this.MainWindow.webContents .send('fromMain', [ FromMain_SEND_IMG_URL, ENTROPY_SOURCE_IMG_ID, img_data_asURL, image_file_extension ]); return img_data_asURL; }; // loadImageFromFile // ====================== ToMain_RQ_LOAD_IMG_FROM_FILE ====================== // called like this by Renderer: await window.ipcMain.LoadImageFromFile(data) ipcMain.handle( ToMain_RQ_LOAD_IMG_FROM_FILE, async (event, image_file_path) => { pretty_func_header_log( "[Electron]", ToMain_RQ_LOAD_IMG_FROM_FILE ); let img_data_asURL = loadImageFromFile( image_file_path ); return img_data_asURL; }); // "ToMain:Request/load_image_from_file" event handler // ====================== ToMain_RQ_DRAW_RND_CRYPTO_LOGO ====================== //Skribi.log(">> register: " + ToMain_RQ_DRAW_RND_CRYPTO_LOGO); // called like this by Renderer: await window.ipcMain.DrawRandomCryptoLogo(data) ipcMain.handle( ToMain_RQ_DRAW_RND_CRYPTO_LOGO, (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_DRAW_RND_CRYPTO_LOGO ); let app_path = app.getAppPath(); //Skribi.log(" app_path: " + app_path); let crypto_logos_path = app_path + "/www/img/CryptoCurrency"; let crypto_logos = FileUtils.GetFilesInFolder( crypto_logos_path ); //Skribi.log(" crypto_logos.length: " + crypto_logos.length); let image_file_path = crypto_logos_path + "/"; let crypto_logo_filename = "Zilver_64px.svg"; if (! this.FirstImageAsEntropySource ) { let random_index = getRandomInt( crypto_logos.length ); crypto_logo_filename = crypto_logos[random_index]; } this.FirstImageAsEntropySource = false; image_file_path += crypto_logo_filename; let img_data_asURL = loadImageFromFile( image_file_path ); return img_data_asURL; }); // "ToMain:Request/drop_rnd_crypto_logo" event handler // ================== ToMain_RQ_GENERATE_PASSWORD ================== // called like this by Renderer: await window.ipcMain.GeneratePassword( data ) // https://www.npmjs.com/package/generate-password ipcMain.handle( ToMain_RQ_GENERATE_PASSWORD, async (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_GENERATE_PASSWORD ); // const {} = data; let new_password = PasswordGenerator.generate({ length: 24, numbers: true, //symbols: true, uppercase: true, lowercase: true, excludeSimilarCharacters : true, strict: true }); return new_password; }); // "ToMain:Request/GeneratePassword event handler // ================== ToMain_RQ_GET_HD_WALLET ================== // called like this by Renderer: await window.ipcMain.GetHDWallet( data ) ipcMain.handle( ToMain_RQ_GET_HD_WALLET, async (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_GET_HD_WALLET ); const { entropy_hex, salt_uuid, blockchain, crypto_net, password, account, address_index } = data; // pretty_log( "eMain.evtH('getHDW')> blockchain", blockchain ); // pretty_log( "account", account ); // pretty_log( "address_index", address_index ); // Skribi.log(" options: " + JSON.stringify(options)); let wallet = await HDWallet.GetWallet ( entropy_hex, salt_uuid, blockchain, crypto_net, password, account, address_index ); return wallet; }); // "ToMain:Request/get_hd_wallet" event handler // ================== ToMain_RQ_GET_SIMPLE_WALLET ================== // called like this by Renderer: await window.ipcMain.GetSimpleWallet( data ) ipcMain.handle( ToMain_RQ_GET_SIMPLE_WALLET, async ( event, data ) => { pretty_func_header_log( "[Electron]", ToMain_RQ_GET_SIMPLE_WALLET ); const { private_key, salt_uuid, blockchain, crypto_net } = data; // pretty_log( "eMain.evtH('getSW')> private_key", private_key ); // pretty_log( "eMain.evtH('getSW')> salt_uuid", salt_uuid ); // pretty_log( "eMain.evtH('getSW')> blockchain", blockchain ); // pretty_log( "eMain.evtH('getSW')> crypto_net", crypto_net ); if ( private_key == undefined || private_key == "" ) { throw new Error("ElectronMain ToMain_RQ_GET_SIMPLE_WALLET handler: 'private_key' NOT DEFINED"); } // Skribi.log(" options: " + JSON.stringify(options)); let wallet = await SimpleWallet .GetWallet( private_key, salt_uuid, blockchain, crypto_net ); return wallet; }); // "ToMain:Request/get_simple_wallet" event handler // ================== ToMain_RQ_MNEMONICS_TO_HD_WALLET_INFO ================== // called like this by Renderer: await window.ipcMain.MnemonicsToHDWalletInfo( data ) ipcMain.handle( ToMain_RQ_MNEMONICS_TO_HD_WALLET_INFO, async (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_MNEMONICS_TO_HD_WALLET_INFO ); const { mnemonics, options } = data; // Skribi.log(" options: " + JSON.stringify(options)); let hdwallet_info = await Bip32Utils.MnemonicsToHDWalletInfo( mnemonics, options ); return hdwallet_info; }); // "ToMain:Request/mnemonics_to_hd_wallet_info" event handler // ================== ToMain_RQ_MNEMONICS_TO_ENTROPY_INFO =================== // called like this by Renderer: await window.ipcMain.MnemonicsToEntropyInfo( data ) ipcMain.handle( ToMain_RQ_MNEMONICS_TO_ENTROPY_INFO, async (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_MNEMONICS_TO_ENTROPY_INFO ); const { mnemonics, lang } = data; // Skribi.log(" lang: " + lang); let entropy_info = Bip39Utils.MnemonicsToEntropyInfo( mnemonics, lang ); return entropy_info; }); // "ToMain:Request/mnemonics_to_entropy_info" event handler // ================== ToMain_RQ_ENTROPY_TO_MNEMONICS =================== // called like this by Renderer: await window.ipcMain.EntropyToMnemonics( data ) ipcMain.handle( ToMain_RQ_ENTROPY_TO_MNEMONICS, async (event, data) => { // Skribi.log(">> " + _CYAN_ + "[Electron] " + _YELLOW_ + ToMain_RQ_ENTROPY_TO_MNEMONICS + _END_); const { entropy, lang } = data; // Skribi.log(" data_hex: " + data_hex); // Skribi.log(" options: " + JSON.stringify(options)); let mnemonics = Bip39Utils.EntropyToMnemonics( entropy, lang ); // Skribi.log(">> mnemonics: " + mnemonics); return mnemonics; }); // "ToMain:Request/entropy_to_mnemonics" event handler // ================== ToMain_RQ_ENTROPY_TO_CHECKSUM =================== // called like this by Renderer: await window.ipcMain.EntropyToChecksum( data ) ipcMain.handle( ToMain_RQ_ENTROPY_TO_CHECKSUM, async (event, data) => { // Skribi.log(">> " + _CYAN_ + "[Electron] " + _YELLOW_ + ToMain_RQ_ENTROPY_TO_CHECKSUM + _END_); const { entropy, options } = data; let checksum = Bip39Utils.EntropyToChecksum( entropy, options ); return checksum; }); // "ToMain:Request/entropy_to_checksum" event handler // ================== ToMain_RQ_ENTROPY_SRC_TO_ENTROPY ================== // called like this by Renderer: await window.ipcMain.EntropySourceToEntropy( data ) ipcMain.handle( ToMain_RQ_ENTROPY_SRC_TO_ENTROPY, async (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_ENTROPY_SRC_TO_ENTROPY ); const { salted_entropy_src_str, options } = data; let entropy = Bip39Utils.EntropySourceToEntropy( salted_entropy_src_str, options ); return entropy; }); // "ToMain:Request/entropy_src_to_entropy" event handler // ================== ToMain_RQ_MNEMONICS_AS_4LETTER ================== // called like this by Renderer: await window.ipcMain.MnemonicsAs4letter( data ) ipcMain.handle( ToMain_RQ_MNEMONICS_AS_4LETTER, async (event, mnemonics) => { // Skribi.log(">> " + _CYAN_ + "[Electron] " + _YELLOW_ + ToMain_RQ_MNEMONICS_AS_4LETTER + _END_); // Skribi.log(">> data: " + data); let mnemonics_as_4letter = Bip39Utils.MnemonicsAs4letter( mnemonics ); // Skribi.log(">> mnemonics: " + mnemonics); return mnemonics_as_4letter; }); // "ToMain:Request/mnemonics_as_4letter" event handler // ================== ToMain_RQ_MNEMONICS_AS_TWO_PARTS ================== // called like this by Renderer: await window.ipcMain.MnemonicsAsTwoParts( data ) ipcMain.handle( ToMain_RQ_MNEMONICS_AS_TWO_PARTS, async (event, mnemonics) => { // Skribi.log(">> " + _CYAN_ + "[Electron] " + _YELLOW_ + ToMain_RQ_MNEMONICS_AS_TWO_PARTS + _END_); // Skribi.log(">> data: " + data); let mnemonics_as_two_parts = Bip39Utils.MnemonicsAsTwoParts( mnemonics ); // Skribi.log(">> mnemonics: " + mnemonics); return mnemonics_as_two_parts; }); // "ToMain:Request/mnemonics_as_two_parts" event handler // ================== ToMain_RQ_GET_SECP256K1 ================== // called like this by Renderer: await window.ipcMain.GetSecp256k1( entropy ) //ipcMain.handle( ToMain_RQ_GET_SECP256K1, (event, entropy) => { // Skribi.log(">> " + _CYAN_ + "[Electron] " + _YELLOW_ + ToMain_RQ_GET_SECP256K1 + _END_); // let entropy_is_hex = isHexString( entropy ); // Skribi.log(" entropy_is_hex: " + entropy_is_hex); // let entropy_bytes = entropy.length/2; // Skribi.log(" entropy(" + entropy_bytes + "): " + entropy); // let result = getSecp256k1PK( entropy ); // return result; //}); // "ToMain:Request/get_Secp256k1" event handler // =================== ToMain_RQ_GET_FORTUNE_COOKIE ================== // called like this by Renderer: await window.ipcMain.GetFortuneCookie() ipcMain.handle( ToMain_RQ_GET_FORTUNE_COOKIE, async (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_GET_FORTUNE_COOKIE ); let fortune_cookie = this.getNewFortuneCookie(); return fortune_cookie; }); // "ToMain:Request/get_FortuneCookie" // =============== ToMain_RQ_MNEMONICS_TO_WORD_INDEXES =============== // called like this by Renderer: await window.ipcMain.MnemonicsToWordIndexes( data ) ipcMain.handle( ToMain_RQ_MNEMONICS_TO_WORD_INDEXES, async (event, data) => { // Skribi.log(">> " + _CYAN_ + "[Electron] " + _YELLOW_ + ToMain_RQ_MNEMONICS_TO_WORD_INDEXES + _END_); const { mnemonics, options } = data; let word_indexes = Bip39Utils.GetWordIndexes( mnemonics, options ); return word_indexes; }); // "ToMain:Request/mnemonics_to_word_indexes" event handler // =============== ToMain_RQ_GUESS_MNEMONICS_LANG =============== // called like this by Renderer: await window.ipcMain.GuessMnemonicsLang( data ) ipcMain.handle( ToMain_RQ_GUESS_MNEMONICS_LANG, async (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_GUESS_MNEMONICS_LANG ); const { mnemonics } = data; let lang = Bip39Utils.GuessMnemonicsLang( mnemonics ); return lang; }); // "ToMain:Request/guess_mnemonics_lang" event handler // =================== ToMain_RQ_CHECK_MNEMONICS ==================== // called like this by Renderer: await window.ipcMain.CheckMnemonics(data) ipcMain.handle( ToMain_RQ_CHECK_MNEMONICS, async (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_CHECK_MNEMONICS ); const { mnemonics, options } = data; let check_result = Bip39Utils.CheckMnemonics( mnemonics, options ); return check_result; }); // "ToMain:Request/check_mnemonics" event handler // ====================== ToMain_RQ_GET_UUID ======================= // called like this by Renderer: await window.ipcMain.GetUUID(data) ipcMain.handle( ToMain_RQ_GET_UUID, async (event, data) => { // Skribi.log(">> " + _CYAN_ + "[Electron] " + _YELLOW_ + ToMain_RQ_GET_UUID + _END_); let new_uuidv4 = uuidv4(); return new_uuidv4; }); // "ToMain:Request/get_UUID" event handler // ==================== ToMain_RQ_GET_L10N_KEYPAIRS ===================== // called like this by Renderer: await window.ipcMain.GetL10nKeyPairs() ipcMain.handle( ToMain_RQ_GET_L10N_KEYPAIRS, (event, data) => { // Skribi.log(">> " + _CYAN_ + "[Electron] " + _YELLOW_ + ToMain_RQ_GET_L10N_KEYPAIRS + _END_); let L10n_keypairs = L10nUtils.GetKeyPairs(); return L10n_keypairs; }); // "ToMain:Request/get_L10n_keypairs" event handler // ==================== ToMain_RQ_GET_L10N_MSG ===================== // called like this by Renderer: await window.ipcMain.GetLocalizedMsg(msg_id) ipcMain.handle( ToMain_RQ_GET_L10N_MSG, (event, msg_id) => { // Skribi.log(">> " + _CYAN_ + "[Electron] " + _YELLOW_ + ToMain_RQ_GET_L10N_MSG + _END_); let L10n_msg = L10nUtils.GetLocalizedMsg( msg_id ); return L10n_msg; }); // "ToMain:Request/get_L10n_Msg" event handler // https://chasingcode.dev/blog/electron-toggle-menu-item-dynamically/ // called like this by Renderer: window.ipcMain.SetMenuItemState( data ) ipcMain.on( ToMain_RQ_SET_MENU_ITEM_STATE, (event, data) => { pretty_func_header_log( "[Electron]", ToMain_RQ_SET_MENU_ITEM_STATE ); const { menu_item_id, enabled } = data; // pretty_log( "eMain.evtH('setMenuItemState')> menu_item_id(state)", menu_item_id + "(" + enabled + ")" ); let menu_elt = Menu.getApplicationMenu().getMenuItemById( menu_item_id ); // pretty_log( "eMain.evtH('setMenuItemState')> menu_elt", menu_elt ); menu_elt.enabled = enabled; }); // "ToMain:Request/set_menu_item_state" event handler } // setCallbacks() } // ElectronMain class // ========== Prevent Multiple instances of Electron main process ========== // https://stackoverflow.com/questions/35916158/how-to-prevent-multiple-instances-in-electron if ( ! gotTheLock ) { app.quit(); } else { app.on('second-instance', (event, commandLine, workingDirectory) => { // Someone tried to run a second instance, we should focus our window. let main_window = ElectronMain.GetInstance().getMainWindow(); // Skribi.log(" >> 1. process.argv: " + JSON.stringify(process.argv)); if ( main_window != null ) { if ( main_window ) { main_window.restore(); } main_window.focus() } }); // Manage case of second instance //ipcMain.on( 'get-file-data', (event, data) => { // Skribi.log(" >> 'get-file-data': $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"); // this.getCmdLineArgs(); //} ); // 'get-file-data' // Create Electron main window, load the rest of the app, etc... app.whenReady().then( async () => { if ( process.platform.startsWith('win') && process.argv.length >= 2 ) { const in_file_path = process.argv[1]; // Skribi.log( " 2. process.argv: " + JSON.stringify(process.argv) ); // Skribi.log( " >>> in_file_path: " + in_file_path ); } ElectronMain.GetInstance().createWindow(); const TIME_OUT = 1250; let test_internet_connection_count = 0; let internet_connected = true; const check_internet_connection_fn = async () => { // https://stackoverflow.com/questions/44663864/correct-try-catch-syntax-using-async-await internet_connected = true; await checkInternetConnected().catch( error => { internet_connected = false; }); // console.log("> test_internet_connection_count: " + test_internet_connection_count++ + " internet_connected = " + internet_connected); ElectronMain.GetInstance().getMainWindow().webContents.send ( "fromMain", [ FromMain_INTERNET_CONNECTED, internet_connected ] ); setTimeout( check_internet_connection_fn, TIME_OUT ); }; // check_internet_connection_fn() await check_internet_connection_fn(); }); } // ========== Prevent Multiple instances of Electron main process // =====================================================================================