diff --git a/app/user_config.js b/app/user_config.js index 4da1f62a0..44a0b1171 100644 --- a/app/user_config.js +++ b/app/user_config.js @@ -1,6 +1,6 @@ const path = require('path'); -const { app } = require('electron'); +const app = require('electron').app || require('electron').remote.app; const { start } = require('./base_config'); const config = require('./config'); diff --git a/js/launcher_start.js b/js/launcher_start.js new file mode 100644 index 000000000..38ec1e9e5 --- /dev/null +++ b/js/launcher_start.js @@ -0,0 +1,7 @@ +/* global $, Whisper, storage */ +const $body = $(document.body); + +// eslint-disable-next-line strict +window.view = new Whisper.LauncherView(); +$body.html(''); +window.view.$el.prependTo($body); diff --git a/js/views/launcher_view.js b/js/views/launcher_view.js new file mode 100644 index 000000000..4c7c8282b --- /dev/null +++ b/js/views/launcher_view.js @@ -0,0 +1,26 @@ +/* global i18n: false */ +/* global Whisper: false */ +/* global $: false */ + +/* eslint-disable no-new */ + +// eslint-disable-next-line func-names +(function() { + 'use strict'; + + window.Whisper = window.Whisper || {}; + + Whisper.LauncherView = Whisper.View.extend({ + className: 'launcher', + templateName: 'launcher', + initialize() { + this.render(); + }, + render_attributes() { + return { + title: 'WOOOWEEE', + }; + }, + }); + +})(); diff --git a/launcher.html b/launcher.html new file mode 100644 index 000000000..448dfe36b --- /dev/null +++ b/launcher.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + +
+
+ +
+ + + +
+
+
+
+ + + diff --git a/launcher_preload.js b/launcher_preload.js new file mode 100644 index 000000000..fdd5041da --- /dev/null +++ b/launcher_preload.js @@ -0,0 +1,31 @@ +/* global window */ + +const { ipcRenderer } = require('electron'); +const url = require('url'); +const i18n = require('./js/modules/i18n'); + +const userConfig = require('./app/user_config'); + +const config = url.parse(window.location.toString(), true).query; +const { locale } = config; +const localeMessages = ipcRenderer.sendSync('locale-data'); + +window.theme = config.theme; +window.i18n = i18n.setup(locale, localeMessages); + +// So far we're only using this for Signal.Types +const Signal = require('./js/modules/signal'); + +window.Signal = Signal.setup({ + Attachments: null, + userDataPath: null, + getRegionCode: () => null, +}); + +window.userConfig = userConfig; +window.getEnvironment = () => config.environment; +window.getVersion = () => config.version; +window.getAppInstance = () => config.appInstance; + +window.onLogin = (passPhrase) => ipcRenderer.send('launcher_login', passPhrase); +require('./js/logging'); diff --git a/main.js b/main.js index 1e794c6da..94a02b58f 100644 --- a/main.js +++ b/main.js @@ -420,6 +420,93 @@ function setupAsStandalone() { } } +let launcherWindow; +function showLauncher() { + if (launcherWindow) { + launcherWindow.show(); + return; + } + + const windowOptions = Object.assign( + { + show: !startInTray, // allow to start minimised in tray + width: DEFAULT_WIDTH, + height: DEFAULT_HEIGHT, + minWidth: MIN_WIDTH, + minHeight: MIN_HEIGHT, + autoHideMenuBar: false, + webPreferences: { + nodeIntegration: false, + nodeIntegrationInWorker: false, + // sandbox: true, + preload: path.join(__dirname, 'launcher_preload.js'), + nativeWindowOpen: true, + }, + icon: path.join(__dirname, 'images', 'icon_256.png'), + }, + _.pick(windowConfig, [ + 'maximized', + 'autoHideMenuBar', + 'width', + 'height', + 'x', + 'y', + ]) + ); + + launcherWindow = new BrowserWindow(windowOptions); + + launcherWindow.loadURL(prepareURL([__dirname, 'launcher.html'])); + + captureClicks(launcherWindow); + + // Ingested in preload.js via a sendSync call + ipc.on('locale-data', event => { + // eslint-disable-next-line no-param-reassign + event.returnValue = locale.messages; + }); + + launcherWindow.on('close', e => { + // If the application is terminating, just do the default + if ( + config.environment === 'test' || + config.environment === 'test-lib' || + (windowState.shouldQuit()) + ) { + return; + } + + // Prevent the shutdown + e.preventDefault(); + launcherWindow.hide(); + + // On Mac, or on other platforms when the tray icon is in use, the window + // should be only hidden, not closed, when the user clicks the close button + if ( + !windowState.shouldQuit() && + (usingTrayIcon || process.platform === 'darwin') + ) { + // toggle the visibility of the show/hide tray icon menu entries + if (tray) { + tray.updateContextMenu(); + } + + return; + } + + launcherWindow.readyForShutdown = true; + app.quit(); + }); + + launcherWindow.on('closed', () => { + launcherWindow = null; + }); + + launcherWindow.once('ready-to-show', () => { + launcherWindow.show(); + }); +} + let aboutWindow; function showAbout() { if (aboutWindow) { @@ -654,7 +741,21 @@ app.on('ready', async () => { key = crypto.randomBytes(32).toString('hex'); userConfig.set('key', key); } - await sql.initialize({ configDir: userDataPath, key }); + + // If we have a password set then show the launcher + // Otherwise show the main window + const passHash = userConfig.get('passHash'); + if (!passHash) { + showLauncher(); + } else { + await showMainWindow(key); + } +}); + +async function showMainWindow(sqlKey) { + const userDataPath = await getRealPath(app.getPath('userData')); + + await sql.initialize({ configDir: userDataPath, key: sqlKey }); await sqlChannels.initialize(); try { @@ -698,7 +799,7 @@ app.on('ready', async () => { } setupMenu(); -}); +} function setupMenu(options) { const { platform } = process; diff --git a/package.json b/package.json index ef51ba443..b37c2b068 100644 --- a/package.json +++ b/package.json @@ -224,6 +224,7 @@ "background.html", "about.html", "settings.html", + "launcher.html", "permissions_popup.html", "debug_log.html", "_locales/**",