From 70b06680dbe5479176f69a9f756d2dcd534e43bc Mon Sep 17 00:00:00 2001 From: Will Stone Date: Thu, 14 Dec 2017 22:14:13 +0000 Subject: [PATCH 01/19] start update checker --- .eslintrc.json | 43 ++++++++++++++++++++++--------------------- package.json | 24 +++++++++++++++++++----- src/main.js | 21 +++++++++++++++++++++ yarn.lock | 2 +- 4 files changed, 63 insertions(+), 27 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 7db57548..6997fc71 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,23 +1,24 @@ { - "env": { - "browser": true, - "commonjs": true, - "es6": true, - "node": true + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "node": true + }, + "parserOptions": { + "ecmaVersion": 2017, + "ecmaFeatures": { + "jsx": true }, - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "sourceType": "module" - }, - "rules": { - "no-const-assign": "warn", - "no-this-before-super": "warn", - "no-undef": "warn", - "no-unreachable": "warn", - "no-unused-vars": "warn", - "constructor-super": "warn", - "valid-typeof": "warn" - } -} \ No newline at end of file + "sourceType": "module" + }, + "rules": { + "no-const-assign": "warn", + "no-this-before-super": "warn", + "no-undef": "warn", + "no-unreachable": "warn", + "no-unused-vars": "warn", + "constructor-super": "warn", + "valid-typeof": "warn" + } +} diff --git a/package.json b/package.json index 6c2815be..dc41870f 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,10 @@ "bugs": { "url": "https://github.com/will-stone/browserosaurus/issues" }, - "keywords": ["Electron", "Browser chooser"], + "keywords": [ + "Electron", + "Browser chooser" + ], "author": "Will Stone", "license": "MIT", "devDependencies": { @@ -39,28 +42,39 @@ "electron-store": "^1.3.0", "jsonpath": "^0.2.11", "mousetrap": "^1.6.1", + "node-fetch": "^1.7.3", "opn": "^5.1.0", "pretty-checkbox": "^3.0.3", + "semver": "^5.4.1", "sortablejs": "^1.7.0", "xml2json": "^0.11.0" }, "config": { "forge": { "make_targets": { - "darwin": ["dmg"] + "darwin": [ + "dmg" + ] }, "electronPackagerConfig": { "packageManager": "yarn", "icon": "src/images/icon/icon.icns", - "ignore": ["docs"], + "ignore": [ + "docs" + ], "protocols": [ { "name": "HTTP link", - "schemes": ["http", "https"] + "schemes": [ + "http", + "https" + ] }, { "name": "File", - "schemes": ["file"] + "schemes": [ + "file" + ] } ] } diff --git a/src/main.js b/src/main.js index f929ad56..b01add3f 100644 --- a/src/main.js +++ b/src/main.js @@ -4,6 +4,8 @@ import { app, BrowserWindow, Tray, Menu, ipcMain } from 'electron' import Store from 'electron-store' import jp from 'jsonpath' import parser from 'xml2json' +import fetch from 'node-fetch' +import semver from 'semver' import defaultBrowsers from './browsers' @@ -155,6 +157,12 @@ function createTrayIcon() { tray.setPressedImage(`${__dirname}/images/icon/tray_iconHighlight.png`) const contextMenu = Menu.buildFromTemplate([ + { + label: 'Check for update...', + click: function() { + createUpdateWindow() + } + }, { label: 'Preferences', click: function() { @@ -188,6 +196,19 @@ function createTrayIcon() { return null } +function checkForUpdate() { + return fetch( + 'https://api.github.com/repos/will-stone/browserosaurus/releases/latest' + ) + .then(response => response.json()) + .then(response => semver.gt(response.tag_name, app.getVersion())) +} + +async function createUpdateWindow() { + const updateAvailable = await checkForUpdate() + console.log(updateAvailable) +} + /** * Send URL to picker * diff --git a/yarn.lock b/yarn.lock index bbb542ea..55555677 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4066,7 +4066,7 @@ node-expat@^2.3.15: bindings "^1.2.1" nan "^2.3.5" -node-fetch@^1.6.3: +node-fetch@^1.6.3, node-fetch@^1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" dependencies: From ec5a923092d9732a2dc32a81d85174e9a9036390 Mon Sep 17 00:00:00 2001 From: Will Stone Date: Fri, 15 Dec 2017 18:14:32 +0000 Subject: [PATCH 02/19] update window. button sends download link to Browserosaurus --- package.json | 22 +++++----------------- src/main.js | 39 ++++++++++++++++++++++++++++++++++----- src/main.scss | 40 ++++++++++++++++++++++++++++++++++++++++ src/update.html | 27 +++++++++++++++++++++++++++ src/updateRenderer.js | 17 +++++++++++++++++ 5 files changed, 123 insertions(+), 22 deletions(-) create mode 100644 src/update.html create mode 100644 src/updateRenderer.js diff --git a/package.json b/package.json index dc41870f..edd0ccf3 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,7 @@ "bugs": { "url": "https://github.com/will-stone/browserosaurus/issues" }, - "keywords": [ - "Electron", - "Browser chooser" - ], + "keywords": ["Electron", "Browser chooser"], "author": "Will Stone", "license": "MIT", "devDependencies": { @@ -52,29 +49,20 @@ "config": { "forge": { "make_targets": { - "darwin": [ - "dmg" - ] + "darwin": ["dmg"] }, "electronPackagerConfig": { "packageManager": "yarn", "icon": "src/images/icon/icon.icns", - "ignore": [ - "docs" - ], + "ignore": ["docs"], "protocols": [ { "name": "HTTP link", - "schemes": [ - "http", - "https" - ] + "schemes": ["http", "https"] }, { "name": "File", - "schemes": [ - "file" - ] + "schemes": ["file"] } ] } diff --git a/src/main.js b/src/main.js index b01add3f..082c8595 100644 --- a/src/main.js +++ b/src/main.js @@ -13,6 +13,7 @@ import defaultBrowsers from './browsers' // be closed automatically when the JavaScript object is garbage collected. let pickerWindow = null let preferencesWindow = null +let updateWindow = null let tray = null let appIsReady = false let installedBrowsers = [] @@ -201,14 +202,42 @@ function checkForUpdate() { 'https://api.github.com/repos/will-stone/browserosaurus/releases/latest' ) .then(response => response.json()) - .then(response => semver.gt(response.tag_name, app.getVersion())) + .then(response => { + if (semver.gt(response.tag_name, app.getVersion())) { + return response.assets[0].browser_download_url + } else { + return false + } + }) } -async function createUpdateWindow() { - const updateAvailable = await checkForUpdate() - console.log(updateAvailable) +function createUpdateWindow() { + updateWindow = new BrowserWindow({ + width: 400, + height: 200, + icon: `${__dirname}/images/icon/icon.png`, + resizable: false, + show: true, + alwaysOnTop: true, + frame: true, + hasShadow: true, + minimizable: false, + maximizable: false + }) + + updateWindow.loadURL(`file://${__dirname}/update.html`) } +ipcMain.on('check-for-update', async () => { + const updateAvailable = await checkForUpdate() + updateWindow.webContents.send('updateAvailable', updateAvailable) +}) + +ipcMain.on('open-download-link', (event, url) => { + updateWindow.close() + sendUrlToPicker(url) +}) + /** * Send URL to picker * @@ -242,8 +271,8 @@ function createPreferencesWindow(callback) { maximizable: false }) - // preferencesWindow.installedBrowsers = installedBrowsers preferencesWindow.loadURL(`file://${__dirname}/preferences.html`) + // allow window to be opened again preferencesWindow.on('close', e => { if (wantToQuit == false) { diff --git a/src/main.scss b/src/main.scss index 3bf7f424..d96df799 100644 --- a/src/main.scss +++ b/src/main.scss @@ -100,3 +100,43 @@ body { cursor: pointer; } } + +#update { + padding: 1rem; + text-align: center; + + .showUpdate, + .noUpdate { + display: none; + } + + &.updateAvailable { + .checking, + .noUpdate { + display: none; + } + + .showUpdate { + display: block; + } + } + + &.noUpdate { + .checking, + .showUpdate { + display: none; + } + + .noUpdate { + display: block; + } + } +} + +.button { + padding: 1rem; + color: white; + border: 0; + background-color: #126edb; + font-size: inherit; +} diff --git a/src/update.html b/src/update.html new file mode 100644 index 00000000..d4eb2ec7 --- /dev/null +++ b/src/update.html @@ -0,0 +1,27 @@ + + + + + + Browserosaurus Update + + + + + +
Checking…
+ +
+

Update available!

+ +
+ +
You have the latest version.
+ + + + + + \ No newline at end of file diff --git a/src/updateRenderer.js b/src/updateRenderer.js new file mode 100644 index 00000000..963cec13 --- /dev/null +++ b/src/updateRenderer.js @@ -0,0 +1,17 @@ +const electron = require('electron') + +electron.ipcRenderer.on('updateAvailable', (event, updateUrl) => { + const update = document.getElementById('update') + const downloadButton = document.getElementById('downloadButton') + + if (updateUrl) { + downloadButton.addEventListener('click', () => + electron.ipcRenderer.send('open-download-link', updateUrl) + ) + update.classList.add('updateAvailable') + } else { + update.classList.add('noUpdate') + } +}) + +electron.ipcRenderer.send('check-for-update') From 5416581a31a530b2e9cea6e861eec7e96ebb78e2 Mon Sep 17 00:00:00 2001 From: Will Stone Date: Sun, 17 Dec 2017 22:40:04 +0000 Subject: [PATCH 03/19] merge about into prefs. Tabs in prefs --- .compilerc | 36 ++++++ .eslintrc.json | 1 + package.json | 23 +++- src/main.js | 132 +++++++++------------- src/main.scss | 79 ++++++++------ src/picker.html | 3 +- src/preferences.html | 20 +++- src/preferencesRenderer.js | 218 ++++++++++++++++++++++--------------- src/update.html | 27 ----- yarn.lock | 11 ++ 10 files changed, 310 insertions(+), 240 deletions(-) create mode 100644 .compilerc delete mode 100644 src/update.html diff --git a/.compilerc b/.compilerc new file mode 100644 index 00000000..6ecf29bf --- /dev/null +++ b/.compilerc @@ -0,0 +1,36 @@ +{ + "env": { + "development": { + "application/javascript": { + "presets": [ + [ + "env", + { + "targets": { + "chrome": 58 + } + } + ] + ], + "plugins": ["transform-object-rest-spread"], + "sourceMaps": "inline" + } + }, + "production": { + "application/javascript": { + "presets": [ + [ + "env", + { + "targets": { + "chrome": 58 + } + } + ] + ], + "plugins": ["transform-object-rest-spread"], + "sourceMaps": "none" + } + } + } +} diff --git a/.eslintrc.json b/.eslintrc.json index 6997fc71..f806e9df 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,4 +1,5 @@ { + "parser": "babel-eslint", "env": { "browser": true, "commonjs": true, diff --git a/package.json b/package.json index edd0ccf3..90ebb431 100644 --- a/package.json +++ b/package.json @@ -16,12 +16,16 @@ "bugs": { "url": "https://github.com/will-stone/browserosaurus/issues" }, - "keywords": ["Electron", "Browser chooser"], + "keywords": [ + "Electron", + "Browser chooser" + ], "author": "Will Stone", "license": "MIT", "devDependencies": { "babel-eslint": "7.2.3", "babel-plugin-transform-async-to-generator": "^6.24.1", + "babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-preset-env": "^1.5.1", "babel-preset-react": "^6.24.1", "electron-forge": "^4.1.3", @@ -49,20 +53,29 @@ "config": { "forge": { "make_targets": { - "darwin": ["dmg"] + "darwin": [ + "dmg" + ] }, "electronPackagerConfig": { "packageManager": "yarn", "icon": "src/images/icon/icon.icns", - "ignore": ["docs"], + "ignore": [ + "docs" + ], "protocols": [ { "name": "HTTP link", - "schemes": ["http", "https"] + "schemes": [ + "http", + "https" + ] }, { "name": "File", - "schemes": ["file"] + "schemes": [ + "file" + ] } ] } diff --git a/src/main.js b/src/main.js index 082c8595..9a1b62e4 100644 --- a/src/main.js +++ b/src/main.js @@ -1,4 +1,4 @@ -import openAboutWindow from 'about-window' +// import openAboutWindow from 'about-window' import { spawn } from 'child_process' import { app, BrowserWindow, Tray, Menu, ipcMain } from 'electron' import Store from 'electron-store' @@ -71,39 +71,41 @@ function loadConfig() { * if rejected. */ function findInstalledBrowsers() { - return new Promise((fulfill, reject) => { - const sp = spawn('system_profiler', ['-xml', 'SPApplicationsDataType']) + // return new Promise((fulfill, reject) => { + const sp = spawn('system_profiler', ['-xml', 'SPApplicationsDataType']) - let profile = '' + let profile = '' - sp.stdout.setEncoding('utf8') - sp.stdout.on('data', data => { - profile += data - }) - sp.stderr.on('data', data => { - console.log(`stderr: ${data}`) - reject(data) - }) - sp.stdout.on('end', () => { - profile = parser.toJson(profile, { object: true }) - const installedApps = jp.query( - profile, - 'plist.array.dict.array[1].dict[*].string[0]' - ) - installedBrowsers = userConfig.browsers - .map(browser => { - for (let i = 0; i < installedApps.length; i++) { - //const browser = installedApps[i] - if (browser.name === installedApps[i]) { - return browser - } + sp.stdout.setEncoding('utf8') + sp.stdout.on('data', data => { + profile += data + }) + sp.stderr.on('data', data => { + console.log(`stderr: ${data}`) + // reject(data) + }) + sp.stdout.on('end', () => { + profile = parser.toJson(profile, { object: true }) + const installedApps = jp.query( + profile, + 'plist.array.dict.array[1].dict[*].string[0]' + ) + installedBrowsers = userConfig.browsers + .map(browser => { + for (let i = 0; i < installedApps.length; i++) { + //const browser = installedApps[i] + if (browser.name === installedApps[i]) { + return browser } - return false - }) - .filter(x => x) // remove empties - fulfill(installedBrowsers) - }) + } + return false + }) + .filter(x => x) // remove empties + // fulfill(installedBrowsers) + pickerWindow.webContents.send('incomingBrowsers', installedBrowsers) + preferencesWindow.webContents.send('incomingBrowsers', installedBrowsers) }) + // }) } /** @@ -126,7 +128,7 @@ function createPickerWindow(callback) { show: false, title: 'Browserosaurus', hasShadow: true, - backgroundColor: '#111111' + backgroundColor: '#21252b' }) pickerWindow.loadURL(`file://${__dirname}/picker.html`) @@ -158,29 +160,10 @@ function createTrayIcon() { tray.setPressedImage(`${__dirname}/images/icon/tray_iconHighlight.png`) const contextMenu = Menu.buildFromTemplate([ - { - label: 'Check for update...', - click: function() { - createUpdateWindow() - } - }, { label: 'Preferences', click: function() { - togglePreferencesWindow(() => { - preferencesWindow.webContents.send( - 'incomingBrowsers', - installedBrowsers - ) - }) - } - }, - { - label: 'About', - click: function() { - openAboutWindow({ - icon_path: `${__dirname}/images/icon/icon.png` - }) + togglePreferencesWindow() } }, { @@ -211,22 +194,9 @@ function checkForUpdate() { }) } -function createUpdateWindow() { - updateWindow = new BrowserWindow({ - width: 400, - height: 200, - icon: `${__dirname}/images/icon/icon.png`, - resizable: false, - show: true, - alwaysOnTop: true, - frame: true, - hasShadow: true, - minimizable: false, - maximizable: false - }) - - updateWindow.loadURL(`file://${__dirname}/update.html`) -} +ipcMain.on('scan-for-browsers', () => { + findInstalledBrowsers() +}) ipcMain.on('check-for-update', async () => { const updateAvailable = await checkForUpdate() @@ -255,12 +225,11 @@ function sendUrlToPicker(url) { * * Creates the window used to display the preferences, triggered from the * menubar icon. - * @param {Function} callback function to run after this one finishes. */ -function createPreferencesWindow(callback) { +function createPreferencesWindow() { preferencesWindow = new BrowserWindow({ width: 400, - height: 64 + 24, + height: 146, icon: `${__dirname}/images/icon/icon.png`, resizable: false, show: false, @@ -268,6 +237,10 @@ function createPreferencesWindow(callback) { frame: true, hasShadow: true, minimizable: false, + maximizable: false, + titleBarStyle: 'hidden', + backgroundColor: '#21252b', + minimizable: false, maximizable: false }) @@ -280,26 +253,20 @@ function createPreferencesWindow(callback) { preferencesWindow.hide() } }) - - if (callback) { - callback() - } } /** * Toggle Preferences Window * * Shows and brings preferences window to front. - * @param {Function} callback function to run after this one finishes. */ -function togglePreferencesWindow(callback) { +function togglePreferencesWindow() { if (!preferencesWindow) { - createPreferencesWindow(callback) + createPreferencesWindow() } else { // Bring to front preferencesWindow.center() preferencesWindow.show() - callback() } } @@ -379,12 +346,11 @@ app.on('ready', () => { }) }) createPreferencesWindow() - findInstalledBrowsers().then(installedBrowsers => { - pickerWindow.webContents.send('incomingBrowsers', installedBrowsers) - preferencesWindow.webContents.send('incomingBrowsers', installedBrowsers) - }) + findInstalledBrowsers() //.then(installedBrowsers => { + // pickerWindow.webContents.send('incomingBrowsers', installedBrowsers) + // preferencesWindow.webContents.send('incomingBrowsers', installedBrowsers) + // }) }) - //) }) // Hide dock icon. Also prevents Browserosaurus from appearing in cmd-tab. diff --git a/src/main.scss b/src/main.scss index d96df799..0266c8da 100644 --- a/src/main.scss +++ b/src/main.scss @@ -14,6 +14,7 @@ body { body { margin: 0; padding: 0; + color: white; } #url { @@ -81,55 +82,59 @@ body { font-weight: bold; } +#titleBar { + height: 2rem; + -webkit-app-region: drag; + text-align: center; + line-height: 2rem; +} + #preferences { - color: #111111; + #navbar { + display: flex; + justify-content: space-around; + height: 64px; + margin: 0; + padding: 0; + list-style: none; + -webkit-app-region: drag; + border-bottom: 1px solid #111111; + line-height: 64px; + + li { + cursor: default; + -webkit-app-region: no-drag; + opacity: 0.5; + + &.is-active, + &:hover { + opacity: 1; + } + } + } - #browserList > li { - cursor: default; + #browserList { + > li { + cursor: default; - &.sortable-ghost { - opacity: 0; + &.sortable-ghost { + opacity: 0; + } } } } #picker { - color: white; - #browserList > li { cursor: pointer; } } -#update { - padding: 1rem; - text-align: center; +.tab-pane { + display: none; - .showUpdate, - .noUpdate { - display: none; - } - - &.updateAvailable { - .checking, - .noUpdate { - display: none; - } - - .showUpdate { - display: block; - } - } - - &.noUpdate { - .checking, - .showUpdate { - display: none; - } - - .noUpdate { - display: block; - } + &.is-active { + display: block; } } @@ -140,3 +145,7 @@ body { background-color: #126edb; font-size: inherit; } + +.text-center { + text-align: center; +} diff --git a/src/picker.html b/src/picker.html index a557e35e..3b95d4e8 100644 --- a/src/picker.html +++ b/src/picker.html @@ -12,8 +12,7 @@
    -
  • Loading... -
  • +
  • Loading...
diff --git a/src/preferences.html b/src/preferences.html index 6febae02..73803368 100644 --- a/src/preferences.html +++ b/src/preferences.html @@ -9,11 +9,25 @@ -
    -
  • Loading… -
  • +
    Preferences
    + + + +
      +
    • Loading…
    +
    +

    + +

    +

    Browserosaurus

    +

    +
    + - - \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 55555677..7936e086 100644 --- a/yarn.lock +++ b/yarn.lock @@ -588,6 +588,10 @@ babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" +babel-plugin-syntax-object-rest-spread@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + babel-plugin-syntax-trailing-function-commas@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" @@ -783,6 +787,13 @@ babel-plugin-transform-flow-strip-types@^6.22.0: babel-plugin-syntax-flow "^6.18.0" babel-runtime "^6.22.0" +babel-plugin-transform-object-rest-spread@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06" + dependencies: + babel-plugin-syntax-object-rest-spread "^6.8.0" + babel-runtime "^6.26.0" + babel-plugin-transform-react-display-name@^6.23.0: version "6.25.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz#67e2bf1f1e9c93ab08db96792e05392bf2cc28d1" From de4c16936013b48f34bab571252600521fa8c47a Mon Sep 17 00:00:00 2001 From: Will Stone Date: Mon, 18 Dec 2017 13:18:11 +0000 Subject: [PATCH 04/19] start updater --- src/main.js | 15 ++++++++++++--- src/preferences.html | 1 + src/preferencesRenderer.js | 15 +++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/main.js b/src/main.js index 9a1b62e4..df4a5e40 100644 --- a/src/main.js +++ b/src/main.js @@ -186,7 +186,12 @@ function checkForUpdate() { ) .then(response => response.json()) .then(response => { - if (semver.gt(response.tag_name, app.getVersion())) { + if ( + response.message && + response.message.startsWith('API rate limit exceeded') + ) { + return 'API rate limit exceeded, please try again later' + } else if (semver.gt(response.tag_name, app.getVersion())) { return response.assets[0].browser_download_url } else { return false @@ -199,8 +204,12 @@ ipcMain.on('scan-for-browsers', () => { }) ipcMain.on('check-for-update', async () => { - const updateAvailable = await checkForUpdate() - updateWindow.webContents.send('updateAvailable', updateAvailable) + try { + const updateAvailable = await checkForUpdate() + preferencesWindow.webContents.send('updateAvailable', updateAvailable) + } catch (err) { + console.log(err) + } }) ipcMain.on('open-download-link', (event, url) => { diff --git a/src/preferences.html b/src/preferences.html index 73803368..cd64ffaf 100644 --- a/src/preferences.html +++ b/src/preferences.html @@ -26,6 +26,7 @@

    Browserosaurus

    +
    Checking for update...
    diff --git a/src/preferencesRenderer.js b/src/preferencesRenderer.js index 0cfa57ed..51c14f74 100644 --- a/src/preferencesRenderer.js +++ b/src/preferencesRenderer.js @@ -38,6 +38,7 @@ function switchTab(tabId) { browserList.classList.remove('is-active') browsersTab.classList.remove('is-active') currentWindow.setSize(400, 300 + 97) + electron.ipcRenderer.send('check-for-update') break default: @@ -45,6 +46,20 @@ function switchTab(tabId) { } } +electron.ipcRenderer.on('updateAvailable', (event, updateUrl) => { + const updateStatus = document.getElementById('updateStatus') + // const downloadButton = document.getElementById('downloadButton') + + if (updateUrl) { + // downloadButton.addEventListener('click', () => + // electron.ipcRenderer.send('open-download-link', updateUrl) + // ) + updateStatus.innerText = updateUrl + } else { + updateStatus.innerText = updateUrl + } +}) + navItems.forEach(item => item.addEventListener('click', function() { const tabId = item.id From 40e71d8d475aa6b96222b216bd78809fa4a96166 Mon Sep 17 00:00:00 2001 From: Will Stone Date: Mon, 18 Dec 2017 22:15:19 +0000 Subject: [PATCH 05/19] remove update renderer --- src/updateRenderer.js | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 src/updateRenderer.js diff --git a/src/updateRenderer.js b/src/updateRenderer.js deleted file mode 100644 index 963cec13..00000000 --- a/src/updateRenderer.js +++ /dev/null @@ -1,17 +0,0 @@ -const electron = require('electron') - -electron.ipcRenderer.on('updateAvailable', (event, updateUrl) => { - const update = document.getElementById('update') - const downloadButton = document.getElementById('downloadButton') - - if (updateUrl) { - downloadButton.addEventListener('click', () => - electron.ipcRenderer.send('open-download-link', updateUrl) - ) - update.classList.add('updateAvailable') - } else { - update.classList.add('noUpdate') - } -}) - -electron.ipcRenderer.send('check-for-update') From 4945aef45db7aa2ba50333aea3e2d3887ef911e4 Mon Sep 17 00:00:00 2001 From: Will Stone Date: Sun, 7 Jan 2018 22:13:35 +0000 Subject: [PATCH 06/19] start v2 Much to do yet... enable/disable and sorting currently broken. --- package.json | 1 + src/browsers.js | 37 +++--- src/main.js | 236 +++++++++++++++++-------------------- src/pickerRenderer.js | 14 +-- src/preferencesRenderer.js | 30 +++-- 5 files changed, 147 insertions(+), 171 deletions(-) diff --git a/package.json b/package.json index 90ebb431..0ab04037 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "electron-compile": "^6.4.2", "electron-store": "^1.3.0", "jsonpath": "^0.2.11", + "lodash": "^4.17.4", "mousetrap": "^1.6.1", "node-fetch": "^1.7.3", "opn": "^5.1.0", diff --git a/src/browsers.js b/src/browsers.js index 66caf358..0ddac9e5 100644 --- a/src/browsers.js +++ b/src/browsers.js @@ -1,21 +1,16 @@ -export default [ - { name: 'Brave', key: 'b', enabled: true }, - { name: 'Chromium', key: 'c', enabled: true }, - { name: 'Firefox', key: 'f', enabled: true }, - { - name: 'FirefoxDeveloperEdition', - alias: 'Firefox Developer Edition', - key: 'd', - enabled: true - }, - { name: 'FirefoxNightly', alias: 'Nightly', key: 'n', enabled: true }, - { name: 'Google Chrome', key: 'g', enabled: true }, - { name: 'Google Chrome Canary', key: 'y', enabled: true }, - { name: 'Iridium', key: 'i', enabled: true }, - { name: 'Maxthon', key: 'm', enabled: true }, - { name: 'Min', key: '-', enabled: true }, - { name: 'Opera', key: 'o', enabled: true }, - { name: 'Safari', key: 's', enabled: true }, - { name: 'TorBrowser', key: 't', enabled: true }, - { name: 'Vivaldi', key: 'v', enabled: true } -] +export default { + Brave: { key: 'b' }, + Chromium: { key: 'c' }, + Firefox: { key: 'f' }, + FirefoxDeveloperEdition: { alias: 'Firefox Developer Edition', key: 'd' }, + FirefoxNightly: { alias: 'Nightly', key: 'n' }, + 'Google Chrome': { key: 'g' }, + 'Google Chrome Canary': { key: 'y' }, + Iridium: { key: 'i' }, + Maxthon: { key: 'm' }, + Min: { key: '-' }, + Opera: { key: 'o' }, + Safari: { key: 's' }, + TorBrowser: { key: 't' }, + Vivaldi: { key: 'v' } +} diff --git a/src/main.js b/src/main.js index df4a5e40..ccdc64cd 100644 --- a/src/main.js +++ b/src/main.js @@ -6,6 +6,7 @@ import jp from 'jsonpath' import parser from 'xml2json' import fetch from 'node-fetch' import semver from 'semver' +import unionBy from 'lodash/unionBy' import defaultBrowsers from './browsers' @@ -13,99 +14,92 @@ import defaultBrowsers from './browsers' // be closed automatically when the JavaScript object is garbage collected. let pickerWindow = null let preferencesWindow = null -let updateWindow = null let tray = null let appIsReady = false -let installedBrowsers = [] let wantToQuit = false -const defaultConfig = { browsers: defaultBrowsers } -const userConfig = {} - -const store = new Store({ defaults: defaultConfig }) - -/** - * Load config - * - * Syncs the default browser configuration with the store. - * @returns {Promise} Simply returns true once config has been loaded. - */ -function loadConfig() { - return new Promise(fulfill => { - userConfig.browsers = store.get('browsers') - - let userBrowserFound - - // Create clone of the default browsers - let defaultBrowsersClone = defaultConfig.browsers.slice(0) - - userConfig.browsers.map((userBrowser, userBrowserId) => { - userBrowserFound = false - - defaultBrowsersClone.map((defBrowser, defBrowserId) => { - if (defBrowser.name == userBrowser.name) { - defaultBrowsersClone[defBrowserId] = false - userBrowserFound = true - } - }) - - if (userBrowserFound === false) { - userConfig.browsers[userBrowserId] = false - } - }) - - userConfig.browsers = userConfig.browsers.concat(defaultBrowsersClone) - userConfig.browsers = userConfig.browsers.filter(x => x) - - store.set('browsers', userConfig.browsers) - - fulfill(true) - }) -} +// Start store and set browsers if it is the first run +const store = new Store({ defaults: { browsers: [] } }) /** * Find installed browsers * - * Scans the system for all known browsers (in browsers.js file). + * Scans the system for all known browsers (white-listed in browsers.js file). * @returns {Promise} returns array of browsers if resolved, and string * if rejected. */ function findInstalledBrowsers() { - // return new Promise((fulfill, reject) => { - const sp = spawn('system_profiler', ['-xml', 'SPApplicationsDataType']) + return new Promise((fulfill, reject) => { + const sp = spawn('system_profiler', ['-xml', 'SPApplicationsDataType']) - let profile = '' + let profile = '' - sp.stdout.setEncoding('utf8') - sp.stdout.on('data', data => { - profile += data - }) - sp.stderr.on('data', data => { - console.log(`stderr: ${data}`) - // reject(data) - }) - sp.stdout.on('end', () => { - profile = parser.toJson(profile, { object: true }) - const installedApps = jp.query( - profile, - 'plist.array.dict.array[1].dict[*].string[0]' - ) - installedBrowsers = userConfig.browsers - .map(browser => { - for (let i = 0; i < installedApps.length; i++) { - //const browser = installedApps[i] - if (browser.name === installedApps[i]) { - return browser + sp.stdout.setEncoding('utf8') + sp.stdout.on('data', data => { + profile += data + }) + sp.stderr.on('data', data => { + console.log(`stderr: ${data}`) + reject(data) + }) + sp.stdout.on('end', () => { + profile = parser.toJson(profile, { object: true }) + const installedApps = jp.query( + profile, + 'plist.array.dict.array[1].dict[*].string[0]' + ) + const installedBrowsers = Object.keys(defaultBrowsers) + .map(name => { + for (let i = 0; i < installedApps.length; i++) { + if (name === installedApps[i]) { + return name + } } - } - return false - }) - .filter(x => x) // remove empties - // fulfill(installedBrowsers) - pickerWindow.webContents.send('incomingBrowsers', installedBrowsers) - preferencesWindow.webContents.send('incomingBrowsers', installedBrowsers) + }) + .filter(x => x) // remove empties + fulfill(installedBrowsers) + }) }) - // }) +} + +function sendBrowsersToRenderers(browsers) { + const enabledBrowsers = browsers.filter(browser => browser.enabled) + pickerWindow.webContents.send('incomingBrowsers', enabledBrowsers) + preferencesWindow.webContents.send('incomingBrowsers', browsers) +} + +function initBrowsers() { + findInstalledBrowsers() + .then(installedBrowsers => { + const storedBrowsers = store.get('browsers') + + // remove unistalled browsers from user config + const storedBrowsersPruned = storedBrowsers + .map(browser => { + if (installedBrowsers.indexOf(browser.name) === -1) { + return null + } + return browser + }) + .filter(x => x) + + const installedBrowsersWithDetails = installedBrowsers.map(name => ({ + name, + key: defaultBrowsers[name].key, + alias: defaultBrowsers[name].alias || null, + enabled: true + })) + + const mergedBrowsers = unionBy( + storedBrowsersPruned, + installedBrowsersWithDetails, + 'name' + ) + + sendBrowsersToRenderers(mergedBrowsers) + store.set('browsers', mergedBrowsers) + }) + .catch(err => console.error(err)) } /** @@ -199,10 +193,6 @@ function checkForUpdate() { }) } -ipcMain.on('scan-for-browsers', () => { - findInstalledBrowsers() -}) - ipcMain.on('check-for-update', async () => { try { const updateAvailable = await checkForUpdate() @@ -212,11 +202,6 @@ ipcMain.on('check-for-update', async () => { } }) -ipcMain.on('open-download-link', (event, url) => { - updateWindow.close() - sendUrlToPicker(url) -}) - /** * Send URL to picker * @@ -287,10 +272,10 @@ function togglePreferencesWindow() { * @param {Number} fromIndex * @param {Number} toIndex */ -function arrayMove(array, fromIndex, toIndex) { - array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]) - return array -} +// function arrayMove(array, fromIndex, toIndex) { +// array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]) +// return array +// } /** * Toggle browser event @@ -300,14 +285,15 @@ function arrayMove(array, fromIndex, toIndex) { * @param {String} browserName * @param {Boolean} enabled */ -ipcMain.on('toggle-browser', (event, { browserName, enabled }) => { - const browserIndex = userConfig.browsers.findIndex( - browser => browser.name === browserName - ) - userConfig.browsers[browserIndex].enabled = enabled - store.set('browsers', userConfig.browsers) - pickerWindow.webContents.send('incomingBrowsers', installedBrowsers) -}) +// ipcMain.on('toggle-browser', (event, { browserName, enabled }) => { +// const storedBrowsers = store.get('browsers') +// const browserIndex = userConfig.browsers.findIndex( +// browser => browser.name === browserName +// ) +// userConfig.browsers[browserIndex].enabled = enabled +// store.set('browsers', userConfig.browsers) +// pickerWindow.webContents.send('incomingBrowsers', userConfig.browsers) +// }) /** * Sort browser event @@ -317,22 +303,22 @@ ipcMain.on('toggle-browser', (event, { browserName, enabled }) => { * @param {Number} oldIndex index of browser being moved from. * @param {Number} newIndex index of place browser is being moved to. */ -ipcMain.on('sort-browser', (event, { oldIndex, newIndex }) => { - const from = installedBrowsers[oldIndex].name - const to = installedBrowsers[newIndex].name +// ipcMain.on('sort-browser', (event, { oldIndex, newIndex }) => { +// const from = installedBrowsers[oldIndex].name +// const to = installedBrowsers[newIndex].name - const fromIndex = userConfig.browsers.findIndex( - browser => browser.name === from - ) - const toIndex = userConfig.browsers.findIndex(browser => browser.name === to) +// const fromIndex = userConfig.browsers.findIndex( +// browser => browser.name === from +// ) +// const toIndex = userConfig.browsers.findIndex(browser => browser.name === to) - userConfig.browsers = arrayMove(userConfig.browsers, fromIndex, toIndex) - installedBrowsers = arrayMove(installedBrowsers, oldIndex, newIndex) +// userConfig.browsers = arrayMove(userConfig.browsers, fromIndex, toIndex) +// installedBrowsers = arrayMove(installedBrowsers, oldIndex, newIndex) - store.set('browsers', userConfig.browsers) - pickerWindow.webContents.send('incomingBrowsers', installedBrowsers) - preferencesWindow.webContents.send('incomingBrowsers', installedBrowsers) -}) +// store.set('browsers', userConfig.browsers) +// pickerWindow.webContents.send('incomingBrowsers', installedBrowsers) +// preferencesWindow.webContents.send('incomingBrowsers', installedBrowsers) +// }) /** * App on ready @@ -343,22 +329,17 @@ app.on('ready', () => { // Prompt to set as default browser app.setAsDefaultProtocolClient('http') - loadConfig().then(() => { - createTrayIcon() - createPickerWindow(() => { - pickerWindow.once('ready-to-show', () => { - if (global.URLToOpen) { - sendUrlToPicker(global.URLToOpen) - global.URLToOpen = null - } - appIsReady = true - }) + createTrayIcon() + createPreferencesWindow() + createPickerWindow(() => { + pickerWindow.once('ready-to-show', () => { + initBrowsers() + if (global.URLToOpen) { + sendUrlToPicker(global.URLToOpen) + global.URLToOpen = null + } + appIsReady = true }) - createPreferencesWindow() - findInstalledBrowsers() //.then(installedBrowsers => { - // pickerWindow.webContents.send('incomingBrowsers', installedBrowsers) - // preferencesWindow.webContents.send('incomingBrowsers', installedBrowsers) - // }) }) }) @@ -376,6 +357,7 @@ app.on('open-url', (event, url) => { if (appIsReady) { sendUrlToPicker(url) } else { - global.URLToOpen = url // this will be handled later in the createWindow callback + // this will be handled later in the createWindow callback + global.URLToOpen = url } }) diff --git a/src/pickerRenderer.js b/src/pickerRenderer.js index 41d05077..143bee57 100644 --- a/src/pickerRenderer.js +++ b/src/pickerRenderer.js @@ -25,9 +25,9 @@ electron.ipcRenderer.on('incomingURL', (event, message) => { }) // Update browsers -electron.ipcRenderer.on('incomingBrowsers', (event, message) => { +electron.ipcRenderer.on('incomingBrowsers', (event, browsers) => { emptiesPicker() - populatePicker(message) + populatePicker(browsers) }) // Listen for window close event, i.e. on blur, escape etc. @@ -72,18 +72,18 @@ function emptiesPicker() { * Populate picker * * Injects all present and enabled browsers as list items of picker. - * @param {Array} installedBrowsers + * @param {Array} browsers */ -function populatePicker(installedBrowsers) { - if (installedBrowsers.length > 0) { +function populatePicker(browsers) { + if (browsers.length > 0) { // Populate installedBrowsers currentWindow.setSize( 400, - installedBrowsers.filter(browser => browser.enabled).length * 64 + 48 + browsers.filter(browser => browser.enabled).length * 64 + 48 ) - installedBrowsers + browsers .filter(browser => browser.enabled) .map(browser => { // use alias as label if available, otherwise use name diff --git a/src/preferencesRenderer.js b/src/preferencesRenderer.js index 51c14f74..fb4933eb 100644 --- a/src/preferencesRenderer.js +++ b/src/preferencesRenderer.js @@ -1,5 +1,6 @@ +// import browsers from './browsers' // Modules -const Sortable = require('sortablejs') +import sortable from 'sortablejs' const electron = require('electron') // Window @@ -51,9 +52,6 @@ electron.ipcRenderer.on('updateAvailable', (event, updateUrl) => { // const downloadButton = document.getElementById('downloadButton') if (updateUrl) { - // downloadButton.addEventListener('click', () => - // electron.ipcRenderer.send('open-download-link', updateUrl) - // ) updateStatus.innerText = updateUrl } else { updateStatus.innerText = updateUrl @@ -77,17 +75,17 @@ navItems.forEach(item => */ function toggleBrowser(browserName, enabled) { // update local copy of browsers - installedBrowsers = installedBrowsers.map(browser => { - if (browser.name === browserName) { - return { - ...browser, - enabled - } - } else { - return browser - } - }) - // update main.js copy of browsers + // installedBrowsers = installedBrowsers.map(browser => { + // if (browser.name === browserName) { + // return { + // ...browser, + // enabled + // } + // } else { + // return browser + // } + // }) + // // update main.js copy of browsers electron.ipcRenderer.send('toggle-browser', { browserName, enabled }) } @@ -187,7 +185,7 @@ function populatePreferences() { browserList.innerHTML = '' browserList.appendChild(browserListFrag) - Sortable.create(browserList, { + sortable.create(browserList, { draggable: '.browserItem', handle: '.handle', onEnd: e => sortBrowser(e.oldIndex, e.newIndex) From f77c7e83aeba27140797ea88be471ab345a4183f Mon Sep 17 00:00:00 2001 From: Will Stone Date: Sun, 7 Jan 2018 22:24:23 +0000 Subject: [PATCH 07/19] sorting and enable/disable put back and improved --- src/main.js | 72 +++++++++++++++++--------------------- src/preferencesRenderer.js | 20 +++++------ 2 files changed, 42 insertions(+), 50 deletions(-) diff --git a/src/main.js b/src/main.js index ccdc64cd..c0c9cc2f 100644 --- a/src/main.js +++ b/src/main.js @@ -25,8 +25,8 @@ const store = new Store({ defaults: { browsers: [] } }) * Find installed browsers * * Scans the system for all known browsers (white-listed in browsers.js file). - * @returns {Promise} returns array of browsers if resolved, and string - * if rejected. + * @returns {Promise} returns array of browser names (Strings) if resolved, and + * string if rejected. */ function findInstalledBrowsers() { return new Promise((fulfill, reject) => { @@ -264,19 +264,6 @@ function togglePreferencesWindow() { } } -/** - * Array Move - * - * Utility function to move an array item to a specific spot in the array. - * @param {Array} array - * @param {Number} fromIndex - * @param {Number} toIndex - */ -// function arrayMove(array, fromIndex, toIndex) { -// array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]) -// return array -// } - /** * Toggle browser event * @@ -285,15 +272,28 @@ function togglePreferencesWindow() { * @param {String} browserName * @param {Boolean} enabled */ -// ipcMain.on('toggle-browser', (event, { browserName, enabled }) => { -// const storedBrowsers = store.get('browsers') -// const browserIndex = userConfig.browsers.findIndex( -// browser => browser.name === browserName -// ) -// userConfig.browsers[browserIndex].enabled = enabled -// store.set('browsers', userConfig.browsers) -// pickerWindow.webContents.send('incomingBrowsers', userConfig.browsers) -// }) +ipcMain.on('toggle-browser', (event, { browserName, enabled }) => { + const browsers = store.get('browsers') + const browserIndex = browsers.findIndex( + browser => browser.name === browserName + ) + browsers[browserIndex].enabled = enabled + store.set('browsers', browsers) + pickerWindow.webContents.send('incomingBrowsers', browsers) +}) + +/** + * Array Move + * + * Utility function to move an array item to a specific spot in the array. + * @param {Array} array + * @param {Number} fromIndex + * @param {Number} toIndex + */ +function arrayMove(array, fromIndex, toIndex) { + array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]) + return array +} /** * Sort browser event @@ -303,22 +303,14 @@ function togglePreferencesWindow() { * @param {Number} oldIndex index of browser being moved from. * @param {Number} newIndex index of place browser is being moved to. */ -// ipcMain.on('sort-browser', (event, { oldIndex, newIndex }) => { -// const from = installedBrowsers[oldIndex].name -// const to = installedBrowsers[newIndex].name - -// const fromIndex = userConfig.browsers.findIndex( -// browser => browser.name === from -// ) -// const toIndex = userConfig.browsers.findIndex(browser => browser.name === to) - -// userConfig.browsers = arrayMove(userConfig.browsers, fromIndex, toIndex) -// installedBrowsers = arrayMove(installedBrowsers, oldIndex, newIndex) - -// store.set('browsers', userConfig.browsers) -// pickerWindow.webContents.send('incomingBrowsers', installedBrowsers) -// preferencesWindow.webContents.send('incomingBrowsers', installedBrowsers) -// }) +ipcMain.on('sort-browser', (event, { oldIndex, newIndex }) => { + let browsers = store.get('browsers') + + browsers = arrayMove(browsers, oldIndex, newIndex) + + store.set('browsers', browsers) + pickerWindow.webContents.send('incomingBrowsers', browsers) +}) /** * App on ready diff --git a/src/preferencesRenderer.js b/src/preferencesRenderer.js index fb4933eb..686e82f5 100644 --- a/src/preferencesRenderer.js +++ b/src/preferencesRenderer.js @@ -75,16 +75,16 @@ navItems.forEach(item => */ function toggleBrowser(browserName, enabled) { // update local copy of browsers - // installedBrowsers = installedBrowsers.map(browser => { - // if (browser.name === browserName) { - // return { - // ...browser, - // enabled - // } - // } else { - // return browser - // } - // }) + installedBrowsers = installedBrowsers.map(browser => { + if (browser.name === browserName) { + return { + ...browser, + enabled + } + } else { + return browser + } + }) // // update main.js copy of browsers electron.ipcRenderer.send('toggle-browser', { browserName, enabled }) } From fe7eab2a8f37e8470eef2cffb4dbcd5554548330 Mon Sep 17 00:00:00 2001 From: Will Stone Date: Mon, 8 Jan 2018 21:52:21 +0000 Subject: [PATCH 08/19] use es6 class --- src/pickerRenderer.js | 279 +++++++++++++++++++++++------------------- 1 file changed, 150 insertions(+), 129 deletions(-) diff --git a/src/pickerRenderer.js b/src/pickerRenderer.js index 143bee57..8652e772 100644 --- a/src/pickerRenderer.js +++ b/src/pickerRenderer.js @@ -1,142 +1,163 @@ -const electron = require('electron') -const opn = require('opn') -const currentWindow = electron.remote.getCurrentWindow() -const Mousetrap = require('mousetrap') -let url = null -const browserList = document.getElementById('browserList') -const urlField = document.getElementById('url') - -// Hide the picker window -const closeWindow = () => { - urlField.innerText = '' - - setTimeout(() => { - // if not paused, escape causes an audible error (beep). Presumably there's some sort of race condition here. Anyway, the timeout seems to solve it. - currentWindow.hide() - url = null - }, 0) -} - -// Listen for URL -electron.ipcRenderer.on('incomingURL', (event, message) => { - urlField.innerText = message - url = message - currentWindow.show() -}) - -// Update browsers -electron.ipcRenderer.on('incomingBrowsers', (event, browsers) => { - emptiesPicker() - populatePicker(browsers) -}) - -// Listen for window close event, i.e. on blur, escape etc. -electron.ipcRenderer.on('close', () => { - closeWindow() -}) - -// Escape key hides picker window. -Mousetrap.bind('esc', () => { - closeWindow() -}) - -/** - * Open Browser - * - * Sends the URL to the chosen browser and tells OS to open it. - * @param {String} appName name of browser as recognised by macOS - */ -function openBrowser(appName) { - opn(url, { app: appName, wait: false }) - .then(() => closeWindow()) - .catch(() => - alert( - 'Oh no! An error just occurred, please report this as a GitHub issue. Opened URL was ' + - url - ) - ) -} +import electron from 'electron' +import opn from 'opn' +import Mousetrap from 'mousetrap' + +class PickerWindow { + constructor() { + this.url = null + this.window = electron.remote.getCurrentWindow() + this.urlField = document.getElementById('url') + this.browserList = document.getElementById('browserList') + + /** + * Event: Listen for URL + * Update URL global var and show window + */ + electron.ipcRenderer.on('incomingURL', (event, incomingURL) => { + this.urlField.innerText = incomingURL + this.url = incomingURL + this.window.show() + }) + + /** + * Event: Update browsers + * Re/populate browser list + */ + electron.ipcRenderer.on('incomingBrowsers', (event, browsers) => { + this.emptiesPicker() + this.populatePicker(browsers) + }) + + /** + * Event: Close window i.e. on blur, escape etc. + * Hide picker window + */ + electron.ipcRenderer.on('close', () => { + this.hideWindow() + }) + + /** + * Event: Escape key + * Hide picker window + */ + Mousetrap.bind('esc', () => { + this.hideWindow() + }) + } -/** - * Empties Picker - * - * Clears the picker window of all browsers, readying it to be repopulated. - */ -function emptiesPicker() { - while (browserList.firstChild) { - browserList.removeChild(browserList.firstChild) + /** + * Hide Window + * Hides the window, resetting the URL text and global var + */ + hideWindow() { + // remove url from field + this.urlField.innerText = '' + setTimeout(() => { + // if not paused, escape causes an audible error (beep). Presumably there's some sort of race condition here. Anyway, the timeout seems to solve it. + this.window.hide() + this.url = null + }, 0) } -} -/** - * Populate picker - * - * Injects all present and enabled browsers as list items of picker. - * @param {Array} browsers - */ -function populatePicker(browsers) { - if (browsers.length > 0) { - // Populate installedBrowsers - - currentWindow.setSize( - 400, - browsers.filter(browser => browser.enabled).length * 64 + 48 - ) - - browsers - .filter(browser => browser.enabled) - .map(browser => { - // use alias as label if available, otherwise use name - if (!browser.alias) { - browser.alias = browser.name - } - return browser + /** + * Open Browser + * Sends the URL to the chosen browser and tells OS to open it. + * @param {String} appName name of browser as recognised by macOS + */ + openBrowser(appName) { + opn(this.url, { app: appName, wait: false }) + .then(() => this.hideWindow()) + .catch(() => { + alert( + `Oh no! An error just occurred, please report this as a GitHub issue. Opened URL was ${ + this.url + }` + ) + this.hideWindow() }) - .map(browser => { - const listItem = document.createElement('li') - - const browserLogo = document.createElement('img') - browserLogo.classList.add('browserLogo') - browserLogo.src = `images/browser-logos/${browser.name}.png` - listItem.appendChild(browserLogo) - - const browserName = document.createElement('span') - browserName.classList.add('browserName') - browserName.innerText = browser.alias - listItem.appendChild(browserName) - - const browserKey = document.createElement('span') - browserKey.classList.add('browserKey') - browserKey.innerText = browser.key - listItem.appendChild(browserKey) - - listItem.addEventListener('click', () => { - listItem.classList.remove('active') - openBrowser(browser.name) - }) - listItem.addEventListener('mouseenter', () => { - listItem.classList.add('active') - }) - listItem.addEventListener('mouseleave', () => { - listItem.classList.remove('active') + } + + /** + * Empties Picker + * Clears the picker window of all browsers, readying it to be repopulated. + */ + emptiesPicker() { + while (this.browserList.firstChild) { + this.browserList.removeChild(this.browserList.firstChild) + } + } + + /** + * Populate picker + * Injects all present and enabled browsers as list items of picker. + * @param {Array} browsers + */ + populatePicker(browsers) { + if (browsers.length > 0) { + // Populate installedBrowsers + + this.window.setSize( + 400, + browsers.filter(browser => browser.enabled).length * 64 + 48 + ) + + browsers + .filter(browser => browser.enabled) + .map(browser => { + // use alias as label if available, otherwise use name + if (!browser.alias) { + browser.alias = browser.name + } + return browser }) + .map(browser => { + const listItem = document.createElement('li') + + const browserLogo = document.createElement('img') + browserLogo.classList.add('browserLogo') + browserLogo.src = `images/browser-logos/${browser.name}.png` + listItem.appendChild(browserLogo) + + const browserName = document.createElement('span') + browserName.classList.add('browserName') + browserName.innerText = browser.alias + listItem.appendChild(browserName) - browserList.appendChild(listItem) + const browserKey = document.createElement('span') + browserKey.classList.add('browserKey') + browserKey.innerText = browser.key + listItem.appendChild(browserKey) - Mousetrap.bind(browser.key, () => { - listItem.classList.add('active') - setTimeout(() => { + listItem.addEventListener('click', () => { listItem.classList.remove('active') - openBrowser(browser.name) - }, 200) + this.openBrowser(browser.name) + }) + listItem.addEventListener('mouseenter', () => { + listItem.classList.add('active') + }) + listItem.addEventListener('mouseleave', () => { + listItem.classList.remove('active') + }) + + this.browserList.appendChild(listItem) + + Mousetrap.bind(browser.key, () => { + listItem.classList.add('active') + setTimeout(() => { + listItem.classList.remove('active') + this.openBrowser(browser.name) + }, 200) + }) }) - }) - } else { - const listItem = document.createElement('li') + } else { + const listItem = document.createElement('li') - listItem.innerText = 'Loading...' + listItem.innerText = 'Loading...' - browserList.appendChild(listItem) - currentWindow.setSize(400, 64 + 48) + this.browserList.appendChild(listItem) + this.window.setSize(400, 64 + 48) + } } } + +new PickerWindow() From c629a9525a31a6d0618c5f5012f6531561907d89 Mon Sep 17 00:00:00 2001 From: Will Stone Date: Tue, 9 Jan 2018 22:10:16 +0000 Subject: [PATCH 09/19] start Window class. fix: sorting not saving enabled/disabled status --- src/main.js | 4 +-- src/pickerRenderer.js | 70 +++++++++++++++++++------------------- src/preferencesRenderer.js | 28 +++++++-------- 3 files changed, 50 insertions(+), 52 deletions(-) diff --git a/src/main.js b/src/main.js index c0c9cc2f..458b5d7c 100644 --- a/src/main.js +++ b/src/main.js @@ -135,7 +135,7 @@ function createPickerWindow(callback) { }) pickerWindow.on('blur', () => { - pickerWindow.webContents.send('close', true) + pickerWindow.hide() }) if (callback) { @@ -279,7 +279,7 @@ ipcMain.on('toggle-browser', (event, { browserName, enabled }) => { ) browsers[browserIndex].enabled = enabled store.set('browsers', browsers) - pickerWindow.webContents.send('incomingBrowsers', browsers) + sendBrowsersToRenderers(browsers) }) /** diff --git a/src/pickerRenderer.js b/src/pickerRenderer.js index 8652e772..54a46ef1 100644 --- a/src/pickerRenderer.js +++ b/src/pickerRenderer.js @@ -2,13 +2,30 @@ import electron from 'electron' import opn from 'opn' import Mousetrap from 'mousetrap' -class PickerWindow { +class Window { constructor() { - this.url = null this.window = electron.remote.getCurrentWindow() - this.urlField = document.getElementById('url') this.browserList = document.getElementById('browserList') + /** + * Event: Update browsers + */ + electron.ipcRenderer.on('incomingBrowsers', (event, browsers) => + this.onReceiveBrowsers(browsers) + ) + } + + onReceiveBrowsers() { + return false + } +} + +class PickerWindow extends Window { + constructor() { + super() + this.url = null + this.urlField = document.getElementById('url') + /** * Event: Listen for URL * Update URL global var and show window @@ -19,23 +36,6 @@ class PickerWindow { this.window.show() }) - /** - * Event: Update browsers - * Re/populate browser list - */ - electron.ipcRenderer.on('incomingBrowsers', (event, browsers) => { - this.emptiesPicker() - this.populatePicker(browsers) - }) - - /** - * Event: Close window i.e. on blur, escape etc. - * Hide picker window - */ - electron.ipcRenderer.on('close', () => { - this.hideWindow() - }) - /** * Event: Escape key * Hide picker window @@ -45,6 +45,10 @@ class PickerWindow { }) } + onReceiveBrowsers(browsers) { + this.populatePicker(browsers) + } + /** * Hide Window * Hides the window, resetting the URL text and global var @@ -77,16 +81,6 @@ class PickerWindow { }) } - /** - * Empties Picker - * Clears the picker window of all browsers, readying it to be repopulated. - */ - emptiesPicker() { - while (this.browserList.firstChild) { - this.browserList.removeChild(this.browserList.firstChild) - } - } - /** * Populate picker * Injects all present and enabled browsers as list items of picker. @@ -101,6 +95,8 @@ class PickerWindow { browsers.filter(browser => browser.enabled).length * 64 + 48 ) + const browserListFrag = document.createDocumentFragment() + browsers .filter(browser => browser.enabled) .map(browser => { @@ -139,7 +135,7 @@ class PickerWindow { listItem.classList.remove('active') }) - this.browserList.appendChild(listItem) + browserListFrag.appendChild(listItem) Mousetrap.bind(browser.key, () => { listItem.classList.add('active') @@ -149,13 +145,17 @@ class PickerWindow { }, 200) }) }) + + this.browserList.innerHTML = '' + this.browserList.appendChild(browserListFrag) } else { - const listItem = document.createElement('li') + // const listItem = document.createElement('li') - listItem.innerText = 'Loading...' + // listItem.innerText = 'No browsers' - this.browserList.appendChild(listItem) - this.window.setSize(400, 64 + 48) + this.browserList.innerHTML = '' + // this.browserList.appendChild(listItem) + this.window.setSize(400, 48) } } } diff --git a/src/preferencesRenderer.js b/src/preferencesRenderer.js index 686e82f5..cc35042c 100644 --- a/src/preferencesRenderer.js +++ b/src/preferencesRenderer.js @@ -1,7 +1,5 @@ -// import browsers from './browsers' -// Modules import sortable from 'sortablejs' -const electron = require('electron') +import electron from 'electron' // Window const currentWindow = electron.remote.getCurrentWindow() @@ -74,17 +72,17 @@ navItems.forEach(item => * @param {Bool} enabled */ function toggleBrowser(browserName, enabled) { - // update local copy of browsers - installedBrowsers = installedBrowsers.map(browser => { - if (browser.name === browserName) { - return { - ...browser, - enabled - } - } else { - return browser - } - }) + // // update local copy of browsers + // installedBrowsers = installedBrowsers.map(browser => { + // if (browser.name === browserName) { + // return { + // ...browser, + // enabled + // } + // } else { + // return browser + // } + // }) // // update main.js copy of browsers electron.ipcRenderer.send('toggle-browser', { browserName, enabled }) } @@ -95,7 +93,7 @@ function toggleBrowser(browserName, enabled) { * Sends the sort-browser event to main.js. This allows browsers to be * reordered. * @param {Number} oldIndex index of browser being moved from. - * @param {*} newIndex index of place browser is being moved to. + * @param {Number} newIndex index of place browser is being moved to. */ function sortBrowser(oldIndex, newIndex) { electron.ipcRenderer.send('sort-browser', { oldIndex, newIndex }) From 959b2df4123342607cad36179d92f4d0cf4b4999 Mon Sep 17 00:00:00 2001 From: Will Stone Date: Tue, 9 Jan 2018 22:10:33 +0000 Subject: [PATCH 10/19] cleanup --- src/pickerRenderer.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/pickerRenderer.js b/src/pickerRenderer.js index 54a46ef1..77e028a9 100644 --- a/src/pickerRenderer.js +++ b/src/pickerRenderer.js @@ -149,12 +149,7 @@ class PickerWindow extends Window { this.browserList.innerHTML = '' this.browserList.appendChild(browserListFrag) } else { - // const listItem = document.createElement('li') - - // listItem.innerText = 'No browsers' - this.browserList.innerHTML = '' - // this.browserList.appendChild(listItem) this.window.setSize(400, 48) } } From 39a38588dba3e543c1b34786c4e885596b752ebe Mon Sep 17 00:00:00 2001 From: Will Stone Date: Tue, 9 Jan 2018 22:13:16 +0000 Subject: [PATCH 11/19] start removing global vars from prefs currently breaks window size update on tab change --- src/preferencesRenderer.js | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/preferencesRenderer.js b/src/preferencesRenderer.js index cc35042c..ef4a6b79 100644 --- a/src/preferencesRenderer.js +++ b/src/preferencesRenderer.js @@ -18,7 +18,6 @@ const bVersion = document.getElementById('browserosaurusVersion') bVersion.innerText = version // Global vars -let installedBrowsers = [] let currentTab = 'browsers-tab' function switchTab(tabId) { @@ -72,18 +71,6 @@ navItems.forEach(item => * @param {Bool} enabled */ function toggleBrowser(browserName, enabled) { - // // update local copy of browsers - // installedBrowsers = installedBrowsers.map(browser => { - // if (browser.name === browserName) { - // return { - // ...browser, - // enabled - // } - // } else { - // return browser - // } - // }) - // // update main.js copy of browsers electron.ipcRenderer.send('toggle-browser', { browserName, enabled }) } @@ -104,10 +91,9 @@ function sortBrowser(oldIndex, newIndex) { * * Listens for installed browsers from main.js, repopulating the window. */ -electron.ipcRenderer.on('incomingBrowsers', (event, message) => { - installedBrowsers = message +electron.ipcRenderer.on('incomingBrowsers', (event, browsers) => { if (currentTab === 'browsers-tab') { - populatePreferences() + populatePreferences(browsers) } }) @@ -116,12 +102,12 @@ electron.ipcRenderer.on('incomingBrowsers', (event, message) => { * * Injects all browsers as list items of preferences. */ -function populatePreferences() { - currentWindow.setSize(400, installedBrowsers.length * 64 + 97) +function populatePreferences(browsers) { + currentWindow.setSize(400, browsers.length * 64 + 97) var browserListFrag = document.createDocumentFragment() - installedBrowsers + browsers .map(browser => { // use alias as label if available, otherwise use name if (!browser.alias) { From 9a44d8e15881a9c4781a46a2416adeaa2a6106f7 Mon Sep 17 00:00:00 2001 From: Will Stone Date: Wed, 10 Jan 2018 22:01:44 +0000 Subject: [PATCH 12/19] start prefs class --- src/Window.js | 21 +++ src/picker.html | 2 +- src/pickerRenderer.js | 18 +-- src/preferences.html | 2 +- src/preferencesRenderer.js | 309 ++++++++++++++++++------------------- 5 files changed, 177 insertions(+), 175 deletions(-) create mode 100644 src/Window.js diff --git a/src/Window.js b/src/Window.js new file mode 100644 index 00000000..254f4fd0 --- /dev/null +++ b/src/Window.js @@ -0,0 +1,21 @@ +import electron from 'electron' + +class Window { + constructor() { + this.window = electron.remote.getCurrentWindow() + this.browserList = document.getElementById('browserList') + + /** + * Event: Update browsers + */ + electron.ipcRenderer.on('incomingBrowsers', (event, browsers) => + this.onReceiveBrowsers(browsers) + ) + } + + onReceiveBrowsers() { + return false + } +} + +export default Window diff --git a/src/picker.html b/src/picker.html index 3b95d4e8..a97d23ef 100644 --- a/src/picker.html +++ b/src/picker.html @@ -18,7 +18,7 @@ \ No newline at end of file diff --git a/src/pickerRenderer.js b/src/pickerRenderer.js index 77e028a9..27eab946 100644 --- a/src/pickerRenderer.js +++ b/src/pickerRenderer.js @@ -2,23 +2,7 @@ import electron from 'electron' import opn from 'opn' import Mousetrap from 'mousetrap' -class Window { - constructor() { - this.window = electron.remote.getCurrentWindow() - this.browserList = document.getElementById('browserList') - - /** - * Event: Update browsers - */ - electron.ipcRenderer.on('incomingBrowsers', (event, browsers) => - this.onReceiveBrowsers(browsers) - ) - } - - onReceiveBrowsers() { - return false - } -} +import Window from './Window' class PickerWindow extends Window { constructor() { diff --git a/src/preferences.html b/src/preferences.html index cd64ffaf..d0aa93f6 100644 --- a/src/preferences.html +++ b/src/preferences.html @@ -32,7 +32,7 @@

    \ No newline at end of file diff --git a/src/preferencesRenderer.js b/src/preferencesRenderer.js index ef4a6b79..5e16453b 100644 --- a/src/preferencesRenderer.js +++ b/src/preferencesRenderer.js @@ -1,177 +1,174 @@ import sortable from 'sortablejs' import electron from 'electron' -// Window -const currentWindow = electron.remote.getCurrentWindow() - -// Elements -const navbar = document.getElementById('navbar') -const navItems = navbar.querySelectorAll('li') -const browsersTab = document.getElementById('browsers-tab') -const aboutTab = document.getElementById('about-tab') -const browserList = document.getElementById('browserList') -const about = document.getElementById('about') - -// Version -const version = electron.remote.app.getVersion() -const bVersion = document.getElementById('browserosaurusVersion') -bVersion.innerText = version - -// Global vars -let currentTab = 'browsers-tab' - -function switchTab(tabId) { - switch (tabId) { - case 'browsers-tab': - about.classList.remove('is-active') - aboutTab.classList.remove('is-active') - browserList.classList.add('is-active') - browsersTab.classList.add('is-active') - populatePreferences() - break - - case 'about-tab': - about.classList.add('is-active') - aboutTab.classList.add('is-active') - browserList.classList.remove('is-active') - browsersTab.classList.remove('is-active') - currentWindow.setSize(400, 300 + 97) - electron.ipcRenderer.send('check-for-update') - break - - default: - break - } -} +import Window from './Window' + +class PreferencesWindow extends Window { + constructor() { + super() + this.navbar = document.getElementById('navbar') + this.navItems = this.navbar.querySelectorAll('li') + this.browsersTab = document.getElementById('browsers-tab') + this.aboutTab = document.getElementById('about-tab') + this.about = document.getElementById('about') + this.bVersion = document.getElementById('browserosaurusVersion') + + this.localVersion = electron.remote.app.getVersion() + this.bVersion.innerText = this.localVersion + + this.browserTabWindowHeight = 0 + + electron.ipcRenderer.on('updateAvailable', (event, updateUrl) => { + const updateStatus = document.getElementById('updateStatus') + if (updateUrl) { + updateStatus.innerText = updateUrl + } else { + updateStatus.innerText = updateUrl + } + }) -electron.ipcRenderer.on('updateAvailable', (event, updateUrl) => { - const updateStatus = document.getElementById('updateStatus') - // const downloadButton = document.getElementById('downloadButton') + /** + * Incoming browsers event + * + * Listens for installed browsers from main.js, repopulating the window. + */ + electron.ipcRenderer.on('incomingBrowsers', (event, browsers) => { + this.populatePreferences(browsers) + }) - if (updateUrl) { - updateStatus.innerText = updateUrl - } else { - updateStatus.innerText = updateUrl + this.navItems.forEach(tab => + tab.addEventListener('click', function() { + this.switchTab(tab.id) + }) + ) } -}) - -navItems.forEach(item => - item.addEventListener('click', function() { - const tabId = item.id - currentTab = tabId - switchTab(tabId) - }) -) - -/** - * Toggle browser - * - * Sends the toggle-browser event to main.js. This enable/disables the browser. - * @param {String} browserName - * @param {Bool} enabled - */ -function toggleBrowser(browserName, enabled) { - electron.ipcRenderer.send('toggle-browser', { browserName, enabled }) -} -/** - * Sort browser - * - * Sends the sort-browser event to main.js. This allows browsers to be - * reordered. - * @param {Number} oldIndex index of browser being moved from. - * @param {Number} newIndex index of place browser is being moved to. - */ -function sortBrowser(oldIndex, newIndex) { - electron.ipcRenderer.send('sort-browser', { oldIndex, newIndex }) -} + switchTab(tabId) { + switch (tabId) { + case 'browsers-tab': + this.about.classList.remove('is-active') + this.aboutTab.classList.remove('is-active') + this.browserList.classList.add('is-active') + this.browsersTab.classList.add('is-active') + this.window.setSize(400, this.browserTabWindowHeight) + break + + case 'about-tab': + this.about.classList.add('is-active') + this.aboutTab.classList.add('is-active') + this.browserList.classList.remove('is-active') + this.browsersTab.classList.remove('is-active') + this.window.setSize(400, 300 + 97) + electron.ipcRenderer.send('check-for-update') + break + + default: + break + } + } -/** - * Incoming browsers event - * - * Listens for installed browsers from main.js, repopulating the window. - */ -electron.ipcRenderer.on('incomingBrowsers', (event, browsers) => { - if (currentTab === 'browsers-tab') { - populatePreferences(browsers) + /** + * Toggle browser + * + * Sends the toggle-browser event to main.js. This enable/disables the browser. + * @param {String} browserName + * @param {Bool} enabled + */ + toggleBrowser(browserName, enabled) { + electron.ipcRenderer.send('toggle-browser', { browserName, enabled }) } -}) - -/** - * Populate preferences - * - * Injects all browsers as list items of preferences. - */ -function populatePreferences(browsers) { - currentWindow.setSize(400, browsers.length * 64 + 97) - - var browserListFrag = document.createDocumentFragment() - - browsers - .map(browser => { - // use alias as label if available, otherwise use name - if (!browser.alias) { - browser.alias = browser.name - } - return browser - }) - .map(browser => { - const li = document.createElement('li') - li.classList.add('browserItem') - - const handle = document.createElement('span') - handle.classList.add('handle') - li.appendChild(handle) - - const logo = document.createElement('img') - logo.classList.add('browserLogo') - logo.src = `images/browser-logos/${browser.name}.png` - li.appendChild(logo) - - const name = document.createElement('span') - name.classList.add('browserName') - name.innerText = browser.alias - li.appendChild(name) - - const checkboxWrapper = document.createElement('div') - checkboxWrapper.classList.add('pretty') - checkboxWrapper.classList.add('p-svg') - - const checkbox = document.createElement('input') - checkbox.type = 'checkbox' - checkboxWrapper.appendChild(checkbox) - - if (browser.enabled) { - checkbox.checked = true - } - checkbox.addEventListener('change', e => { - toggleBrowser(browser.name, e.target.checked) - }) + /** + * Sort browser + * + * Sends the sort-browser event to main.js. This allows browsers to be + * reordered. + * @param {Number} oldIndex index of browser being moved from. + * @param {Number} newIndex index of place browser is being moved to. + */ + sortBrowser(oldIndex, newIndex) { + electron.ipcRenderer.send('sort-browser', { oldIndex, newIndex }) + } - const checkState = document.createElement('div') - checkState.classList.add('state') - checkState.classList.add('p-success') - // check icon - checkState.innerHTML = ` + /** + * Populate preferences + * + * Injects all browsers as list items of preferences. + */ + populatePreferences(browsers) { + this.browserTabWindowHeight = browsers.length * 64 + 97 + this.window.setSize(400, this.browserTabWindowHeight) + + var browserListFrag = document.createDocumentFragment() + + browsers + .map(browser => { + // use alias as label if available, otherwise use name + if (!browser.alias) { + browser.alias = browser.name + } + return browser + }) + .map(browser => { + const li = document.createElement('li') + li.classList.add('browserItem') + + const handle = document.createElement('span') + handle.classList.add('handle') + li.appendChild(handle) + + const logo = document.createElement('img') + logo.classList.add('browserLogo') + logo.src = `images/browser-logos/${browser.name}.png` + li.appendChild(logo) + + const name = document.createElement('span') + name.classList.add('browserName') + name.innerText = browser.alias + li.appendChild(name) + + const checkboxWrapper = document.createElement('div') + checkboxWrapper.classList.add('pretty') + checkboxWrapper.classList.add('p-svg') + + const checkbox = document.createElement('input') + checkbox.type = 'checkbox' + checkboxWrapper.appendChild(checkbox) + + if (browser.enabled) { + checkbox.checked = true + } + + checkbox.addEventListener('change', e => { + this.toggleBrowser(browser.name, e.target.checked) + }) + + const checkState = document.createElement('div') + checkState.classList.add('state') + checkState.classList.add('p-success') + // check icon + checkState.innerHTML = ` ` - checkboxWrapper.appendChild(checkState) + checkboxWrapper.appendChild(checkState) - li.appendChild(checkboxWrapper) + li.appendChild(checkboxWrapper) - browserListFrag.appendChild(li) - }) + browserListFrag.appendChild(li) + }) - browserList.innerHTML = '' - browserList.appendChild(browserListFrag) + this.browserList.innerHTML = '' + this.browserList.appendChild(browserListFrag) - sortable.create(browserList, { - draggable: '.browserItem', - handle: '.handle', - onEnd: e => sortBrowser(e.oldIndex, e.newIndex) - }) + sortable.create(this.browserList, { + draggable: '.browserItem', + handle: '.handle', + onEnd: e => this.sortBrowser(e.oldIndex, e.newIndex) + }) + } } + +new PreferencesWindow() From 8e876a492c0841ddbb9d4ca518acd622ee67cac0 Mon Sep 17 00:00:00 2001 From: Will Stone Date: Sat, 13 Jan 2018 22:47:20 +0000 Subject: [PATCH 13/19] pref cleanup. start splitting out functions --- package.json | 25 +--- src/{ => config}/browsers.js | 0 src/main.js | 139 ++++++------------- src/{ => picker}/picker.html | 2 +- src/{ => picker}/pickerRenderer.js | 4 +- src/{ => preferences}/preferences.html | 4 +- src/{ => preferences}/preferencesRenderer.js | 46 +++--- src/{ => shared}/Window.js | 0 src/utils/arrayMove.js | 20 +++ src/utils/findInstalledBrowsers.js | 46 ++++++ yarn.lock | 10 +- 11 files changed, 145 insertions(+), 151 deletions(-) rename src/{ => config}/browsers.js (100%) rename src/{ => picker}/picker.html (85%) rename src/{ => picker}/pickerRenderer.js (97%) rename src/{ => preferences}/preferences.html (84%) rename src/{ => preferences}/preferencesRenderer.js (81%) rename src/{ => shared}/Window.js (100%) create mode 100644 src/utils/arrayMove.js create mode 100644 src/utils/findInstalledBrowsers.js diff --git a/package.json b/package.json index 0ab04037..39b218f4 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,7 @@ "bugs": { "url": "https://github.com/will-stone/browserosaurus/issues" }, - "keywords": [ - "Electron", - "Browser chooser" - ], + "keywords": ["Electron", "Browser chooser"], "author": "Will Stone", "license": "MIT", "devDependencies": { @@ -35,10 +32,9 @@ "eslint-plugin-import": "2.2.0", "eslint-plugin-jsx-a11y": "5.0.1", "eslint-plugin-react": "7.0.1", - "prettier": "^1.9.1" + "prettier": "^1.10.2" }, "dependencies": { - "about-window": "^1.7.1", "electron-compile": "^6.4.2", "electron-store": "^1.3.0", "jsonpath": "^0.2.11", @@ -54,29 +50,20 @@ "config": { "forge": { "make_targets": { - "darwin": [ - "dmg" - ] + "darwin": ["dmg"] }, "electronPackagerConfig": { "packageManager": "yarn", "icon": "src/images/icon/icon.icns", - "ignore": [ - "docs" - ], + "ignore": ["docs"], "protocols": [ { "name": "HTTP link", - "schemes": [ - "http", - "https" - ] + "schemes": ["http", "https"] }, { "name": "File", - "schemes": [ - "file" - ] + "schemes": ["file"] } ] } diff --git a/src/browsers.js b/src/config/browsers.js similarity index 100% rename from src/browsers.js rename to src/config/browsers.js diff --git a/src/main.js b/src/main.js index 458b5d7c..42a354e9 100644 --- a/src/main.js +++ b/src/main.js @@ -1,14 +1,13 @@ -// import openAboutWindow from 'about-window' -import { spawn } from 'child_process' import { app, BrowserWindow, Tray, Menu, ipcMain } from 'electron' import Store from 'electron-store' -import jp from 'jsonpath' -import parser from 'xml2json' import fetch from 'node-fetch' import semver from 'semver' import unionBy from 'lodash/unionBy' -import defaultBrowsers from './browsers' +import whiteListedBrowsers from './config/browsers' + +import findInstalledBrowsers from './utils/findInstalledBrowsers' +import arrayMove from './utils/arrayMove' // Keep a global reference of the window objects, if you don't, the windows will // be closed automatically when the JavaScript object is garbage collected. @@ -21,87 +20,12 @@ let wantToQuit = false // Start store and set browsers if it is the first run const store = new Store({ defaults: { browsers: [] } }) -/** - * Find installed browsers - * - * Scans the system for all known browsers (white-listed in browsers.js file). - * @returns {Promise} returns array of browser names (Strings) if resolved, and - * string if rejected. - */ -function findInstalledBrowsers() { - return new Promise((fulfill, reject) => { - const sp = spawn('system_profiler', ['-xml', 'SPApplicationsDataType']) - - let profile = '' - - sp.stdout.setEncoding('utf8') - sp.stdout.on('data', data => { - profile += data - }) - sp.stderr.on('data', data => { - console.log(`stderr: ${data}`) - reject(data) - }) - sp.stdout.on('end', () => { - profile = parser.toJson(profile, { object: true }) - const installedApps = jp.query( - profile, - 'plist.array.dict.array[1].dict[*].string[0]' - ) - const installedBrowsers = Object.keys(defaultBrowsers) - .map(name => { - for (let i = 0; i < installedApps.length; i++) { - if (name === installedApps[i]) { - return name - } - } - }) - .filter(x => x) // remove empties - fulfill(installedBrowsers) - }) - }) -} - function sendBrowsersToRenderers(browsers) { const enabledBrowsers = browsers.filter(browser => browser.enabled) pickerWindow.webContents.send('incomingBrowsers', enabledBrowsers) preferencesWindow.webContents.send('incomingBrowsers', browsers) } -function initBrowsers() { - findInstalledBrowsers() - .then(installedBrowsers => { - const storedBrowsers = store.get('browsers') - - // remove unistalled browsers from user config - const storedBrowsersPruned = storedBrowsers - .map(browser => { - if (installedBrowsers.indexOf(browser.name) === -1) { - return null - } - return browser - }) - .filter(x => x) - - const installedBrowsersWithDetails = installedBrowsers.map(name => ({ - name, - key: defaultBrowsers[name].key, - alias: defaultBrowsers[name].alias || null, - enabled: true - })) - - const mergedBrowsers = unionBy( - storedBrowsersPruned, - installedBrowsersWithDetails, - 'name' - ) - - sendBrowsersToRenderers(mergedBrowsers) - store.set('browsers', mergedBrowsers) - }) - .catch(err => console.error(err)) -} - /** * Create picker window * @@ -125,7 +49,7 @@ function createPickerWindow(callback) { backgroundColor: '#21252b' }) - pickerWindow.loadURL(`file://${__dirname}/picker.html`) + pickerWindow.loadURL(`file://${__dirname}/picker/picker.html`) pickerWindow.on('close', e => { if (wantToQuit == false) { @@ -238,7 +162,7 @@ function createPreferencesWindow() { maximizable: false }) - preferencesWindow.loadURL(`file://${__dirname}/preferences.html`) + preferencesWindow.loadURL(`file://${__dirname}/preferences/preferences.html`) // allow window to be opened again preferencesWindow.on('close', e => { @@ -282,19 +206,6 @@ ipcMain.on('toggle-browser', (event, { browserName, enabled }) => { sendBrowsersToRenderers(browsers) }) -/** - * Array Move - * - * Utility function to move an array item to a specific spot in the array. - * @param {Array} array - * @param {Number} fromIndex - * @param {Number} toIndex - */ -function arrayMove(array, fromIndex, toIndex) { - array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]) - return array -} - /** * Sort browser event * @@ -304,10 +215,7 @@ function arrayMove(array, fromIndex, toIndex) { * @param {Number} newIndex index of place browser is being moved to. */ ipcMain.on('sort-browser', (event, { oldIndex, newIndex }) => { - let browsers = store.get('browsers') - - browsers = arrayMove(browsers, oldIndex, newIndex) - + const browsers = arrayMove(store.get('browsers'), oldIndex, newIndex) store.set('browsers', browsers) pickerWindow.webContents.send('incomingBrowsers', browsers) }) @@ -325,7 +233,38 @@ app.on('ready', () => { createPreferencesWindow() createPickerWindow(() => { pickerWindow.once('ready-to-show', () => { - initBrowsers() + findInstalledBrowsers(whiteListedBrowsers) + .then(installedBrowsers => { + const storedBrowsers = store.get('browsers') + + // remove unistalled browsers from user config + const storedBrowsersPruned = storedBrowsers + .map(browser => { + if (installedBrowsers.indexOf(browser.name) === -1) { + return null + } + return browser + }) + .filter(x => x) + + const installedBrowsersWithDetails = installedBrowsers.map(name => ({ + name, + key: whiteListedBrowsers[name].key, + alias: whiteListedBrowsers[name].alias || null, + enabled: true + })) + + const mergedBrowsers = unionBy( + storedBrowsersPruned, + installedBrowsersWithDetails, + 'name' + ) + + sendBrowsersToRenderers(mergedBrowsers) + store.set('browsers', mergedBrowsers) + }) + .catch(err => console.error(err)) + if (global.URLToOpen) { sendUrlToPicker(global.URLToOpen) global.URLToOpen = null diff --git a/src/picker.html b/src/picker/picker.html similarity index 85% rename from src/picker.html rename to src/picker/picker.html index a97d23ef..529975eb 100644 --- a/src/picker.html +++ b/src/picker/picker.html @@ -4,7 +4,7 @@ Browserosaurus - + diff --git a/src/pickerRenderer.js b/src/picker/pickerRenderer.js similarity index 97% rename from src/pickerRenderer.js rename to src/picker/pickerRenderer.js index 27eab946..b6a534c3 100644 --- a/src/pickerRenderer.js +++ b/src/picker/pickerRenderer.js @@ -2,7 +2,7 @@ import electron from 'electron' import opn from 'opn' import Mousetrap from 'mousetrap' -import Window from './Window' +import Window from '../shared/Window' class PickerWindow extends Window { constructor() { @@ -95,7 +95,7 @@ class PickerWindow extends Window { const browserLogo = document.createElement('img') browserLogo.classList.add('browserLogo') - browserLogo.src = `images/browser-logos/${browser.name}.png` + browserLogo.src = `../images/browser-logos/${browser.name}.png` listItem.appendChild(browserLogo) const browserName = document.createElement('span') diff --git a/src/preferences.html b/src/preferences/preferences.html similarity index 84% rename from src/preferences.html rename to src/preferences/preferences.html index d0aa93f6..41836b2f 100644 --- a/src/preferences.html +++ b/src/preferences/preferences.html @@ -4,7 +4,7 @@ Browserosaurus Preferences - + @@ -22,7 +22,7 @@

    - +

    Browserosaurus

    diff --git a/src/preferencesRenderer.js b/src/preferences/preferencesRenderer.js similarity index 81% rename from src/preferencesRenderer.js rename to src/preferences/preferencesRenderer.js index 5e16453b..c5d6a7dd 100644 --- a/src/preferencesRenderer.js +++ b/src/preferences/preferencesRenderer.js @@ -1,21 +1,11 @@ import sortable from 'sortablejs' import electron from 'electron' -import Window from './Window' +import Window from '../shared/Window' class PreferencesWindow extends Window { constructor() { super() - this.navbar = document.getElementById('navbar') - this.navItems = this.navbar.querySelectorAll('li') - this.browsersTab = document.getElementById('browsers-tab') - this.aboutTab = document.getElementById('about-tab') - this.about = document.getElementById('about') - this.bVersion = document.getElementById('browserosaurusVersion') - - this.localVersion = electron.remote.app.getVersion() - this.bVersion.innerText = this.localVersion - this.browserTabWindowHeight = 0 electron.ipcRenderer.on('updateAvailable', (event, updateUrl) => { @@ -36,28 +26,44 @@ class PreferencesWindow extends Window { this.populatePreferences(browsers) }) - this.navItems.forEach(tab => - tab.addEventListener('click', function() { + this.attachNavClickEvents() + this.populateVersion() + } + + populateVersion() { + const bVersion = document.getElementById('browserosaurusVersion') + const localVersion = electron.remote.app.getVersion() + bVersion.innerText = localVersion + } + + attachNavClickEvents() { + const navbar = document.getElementById('navbar') + const navItems = navbar.querySelectorAll('li') + navItems.forEach(tab => + tab.addEventListener('click', () => { this.switchTab(tab.id) }) ) } switchTab(tabId) { + const browsersTab = document.getElementById('browsers-tab') + const aboutTab = document.getElementById('about-tab') + const about = document.getElementById('about') switch (tabId) { case 'browsers-tab': - this.about.classList.remove('is-active') - this.aboutTab.classList.remove('is-active') + about.classList.remove('is-active') + aboutTab.classList.remove('is-active') this.browserList.classList.add('is-active') - this.browsersTab.classList.add('is-active') + browsersTab.classList.add('is-active') this.window.setSize(400, this.browserTabWindowHeight) break case 'about-tab': - this.about.classList.add('is-active') - this.aboutTab.classList.add('is-active') + about.classList.add('is-active') + aboutTab.classList.add('is-active') this.browserList.classList.remove('is-active') - this.browsersTab.classList.remove('is-active') + browsersTab.classList.remove('is-active') this.window.setSize(400, 300 + 97) electron.ipcRenderer.send('check-for-update') break @@ -119,7 +125,7 @@ class PreferencesWindow extends Window { const logo = document.createElement('img') logo.classList.add('browserLogo') - logo.src = `images/browser-logos/${browser.name}.png` + logo.src = `../images/browser-logos/${browser.name}.png` li.appendChild(logo) const name = document.createElement('span') diff --git a/src/Window.js b/src/shared/Window.js similarity index 100% rename from src/Window.js rename to src/shared/Window.js diff --git a/src/utils/arrayMove.js b/src/utils/arrayMove.js new file mode 100644 index 00000000..2e9c7a22 --- /dev/null +++ b/src/utils/arrayMove.js @@ -0,0 +1,20 @@ +import cloneDeep from 'lodash/cloneDeep' + +/** + * Array Move + * + * Utility function to move an array item to a specific spot in the array. + * @param {array} array + * @param {number} fromIndex + * @param {number} toIndex + * @returns {array} + */ +function arrayMove(array, fromIndex, toIndex) { + const returnArray = cloneDeep(array) + + returnArray.splice(toIndex, 0, returnArray.splice(fromIndex, 1)[0]) + + return returnArray +} + +export default arrayMove diff --git a/src/utils/findInstalledBrowsers.js b/src/utils/findInstalledBrowsers.js new file mode 100644 index 00000000..622a1607 --- /dev/null +++ b/src/utils/findInstalledBrowsers.js @@ -0,0 +1,46 @@ +import { spawn } from 'child_process' +import jp from 'jsonpath' +import parser from 'xml2json' + +/** + * Find installed browsers + * + * Scans the system for all known browsers (white-listed in browsers.js file). + * @returns {Promise} returns array of browser names (Strings) if resolved, and + * string if rejected. + */ +function findInstalledBrowsers(whiteListedBrowsers) { + return new Promise((fulfill, reject) => { + const sp = spawn('system_profiler', ['-xml', 'SPApplicationsDataType']) + + let profile = '' + + sp.stdout.setEncoding('utf8') + sp.stdout.on('data', data => { + profile += data + }) + sp.stderr.on('data', data => { + console.log(`stderr: ${data}`) + reject(data) + }) + sp.stdout.on('end', () => { + profile = parser.toJson(profile, { object: true }) + const installedApps = jp.query( + profile, + 'plist.array.dict.array[1].dict[*].string[0]' + ) + const installedBrowsers = Object.keys(whiteListedBrowsers) + .map(name => { + for (let i = 0; i < installedApps.length; i++) { + if (name === installedApps[i]) { + return name + } + } + }) + .filter(x => x) // remove empties + fulfill(installedBrowsers) + }) + }) +} + +export default findInstalledBrowsers diff --git a/yarn.lock b/yarn.lock index 7936e086..d8fd42e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -48,10 +48,6 @@ abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" -about-window@^1.7.1: - version "1.8.0" - resolved "https://registry.yarnpkg.com/about-window/-/about-window-1.8.0.tgz#3e183cfaef4342e1fea6c442f4e43f682e9da580" - acorn-globals@^1.0.3, acorn-globals@^1.0.4: version "1.0.9" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-1.0.9.tgz#55bb5e98691507b74579d0513413217c380c54cf" @@ -4714,9 +4710,9 @@ prepend-http@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" -prettier@^1.9.1: - version "1.9.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.9.2.tgz#96bc2132f7a32338e6078aeb29727178c6335827" +prettier@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.10.2.tgz#1af8356d1842276a99a5b5529c82dd9e9ad3cc93" pretty-bytes@^1.0.2: version "1.0.4" From e1ee139d8047fc62d9c9ab1bea9fbf1a5e7a5b8e Mon Sep 17 00:00:00 2001 From: Will Stone Date: Sun, 14 Jan 2018 12:30:28 +0000 Subject: [PATCH 14/19] more cleanup, splitting, and comments --- src/config/browsers.js | 12 + src/main.js | 281 ++++++++++-------- src/picker/pickerRenderer.js | 39 ++- .../preferences.html => prefs/prefs.html} | 2 +- .../prefsRenderer.js} | 76 +++-- src/shared/Window.js | 9 +- ...indInstalledBrowsers.js => scanForApps.js} | 21 +- 7 files changed, 257 insertions(+), 183 deletions(-) rename src/{preferences/preferences.html => prefs/prefs.html} (95%) rename src/{preferences/preferencesRenderer.js => prefs/prefsRenderer.js} (77%) rename src/utils/{findInstalledBrowsers.js => scanForApps.js} (53%) diff --git a/src/config/browsers.js b/src/config/browsers.js index 0ddac9e5..76bda82b 100644 --- a/src/config/browsers.js +++ b/src/config/browsers.js @@ -1,3 +1,15 @@ +/** + * The white-listed browsers. + * + * { + * AppName: { + * key: {string} - keyboard shortcut, single key. + * Uses Mousetrap: https://craig.is/killing/mice + * alias: {string} - actual text shown in prefs and picker windows. + * } + * } + */ + export default { Brave: { key: 'b' }, Chromium: { key: 'c' }, diff --git a/src/main.js b/src/main.js index 42a354e9..ec55b5fb 100644 --- a/src/main.js +++ b/src/main.js @@ -1,37 +1,32 @@ import { app, BrowserWindow, Tray, Menu, ipcMain } from 'electron' import Store from 'electron-store' +import unionBy from 'lodash/unionBy' import fetch from 'node-fetch' import semver from 'semver' -import unionBy from 'lodash/unionBy' import whiteListedBrowsers from './config/browsers' -import findInstalledBrowsers from './utils/findInstalledBrowsers' import arrayMove from './utils/arrayMove' +import scanForApps from './utils/scanForApps' // Keep a global reference of the window objects, if you don't, the windows will // be closed automatically when the JavaScript object is garbage collected. let pickerWindow = null -let preferencesWindow = null +let prefsWindow = null let tray = null let appIsReady = false let wantToQuit = false -// Start store and set browsers if it is the first run +// Start store and set browsers if first run const store = new Store({ defaults: { browsers: [] } }) -function sendBrowsersToRenderers(browsers) { - const enabledBrowsers = browsers.filter(browser => browser.enabled) - pickerWindow.webContents.send('incomingBrowsers', enabledBrowsers) - preferencesWindow.webContents.send('incomingBrowsers', browsers) -} - /** - * Create picker window + * Create Picker Window * * Creates the window that is used to display browser selection after clicking * a link. - * @param {Function} callback function to run after this one finishes. + * @param {function} callback - function to run at the end of this one. + * @returns {null} */ function createPickerWindow(callback) { pickerWindow = new BrowserWindow({ @@ -65,10 +60,12 @@ function createPickerWindow(callback) { if (callback) { callback() } + + return null } /** - * Create tray icon + * Create Tray Icon * * Creates the menubar icon and menu items. * @returns {null} @@ -81,7 +78,13 @@ function createTrayIcon() { { label: 'Preferences', click: function() { - togglePreferencesWindow() + if (!prefsWindow) { + createPrefsWindow() + } else { + // Bring to front + prefsWindow.center() + prefsWindow.show() + } } }, { @@ -98,54 +101,15 @@ function createTrayIcon() { return null } -function checkForUpdate() { - return fetch( - 'https://api.github.com/repos/will-stone/browserosaurus/releases/latest' - ) - .then(response => response.json()) - .then(response => { - if ( - response.message && - response.message.startsWith('API rate limit exceeded') - ) { - return 'API rate limit exceeded, please try again later' - } else if (semver.gt(response.tag_name, app.getVersion())) { - return response.assets[0].browser_download_url - } else { - return false - } - }) -} - -ipcMain.on('check-for-update', async () => { - try { - const updateAvailable = await checkForUpdate() - preferencesWindow.webContents.send('updateAvailable', updateAvailable) - } catch (err) { - console.log(err) - } -}) - -/** - * Send URL to picker - * - * When url is clicked, this sends the url to the picker renderer so browsers - * know what to open. - * @param {String} url clicked link. - */ -function sendUrlToPicker(url) { - pickerWindow.center() // moves window to current screen - pickerWindow.webContents.send('incomingURL', url) -} - /** - * Create Preferences Window + * Create Prefs Window * * Creates the window used to display the preferences, triggered from the * menubar icon. + * @returns {null} */ -function createPreferencesWindow() { - preferencesWindow = new BrowserWindow({ +function createPrefsWindow() { + prefsWindow = new BrowserWindow({ width: 400, height: 146, icon: `${__dirname}/images/icon/icon.png`, @@ -162,39 +126,90 @@ function createPreferencesWindow() { maximizable: false }) - preferencesWindow.loadURL(`file://${__dirname}/preferences/preferences.html`) + prefsWindow.loadURL(`file://${__dirname}/prefs/prefs.html`) // allow window to be opened again - preferencesWindow.on('close', e => { + prefsWindow.on('close', e => { if (wantToQuit == false) { e.preventDefault() - preferencesWindow.hide() + prefsWindow.hide() } }) + + return null } /** - * Toggle Preferences Window + * Check For Update * - * Shows and brings preferences window to front. + * Checks GitHub API for Browserosaurus's latest release. + * NB: only allowed 60 requests per IP address per hour. + * @returns {object} - {update: boolean, message: string} if update true, + * message is URL to latest release, else message is string to be displayed. */ -function togglePreferencesWindow() { - if (!preferencesWindow) { - createPreferencesWindow() - } else { - // Bring to front - preferencesWindow.center() - preferencesWindow.show() - } +function checkForUpdate() { + return fetch( + 'https://api.github.com/repos/will-stone/browserosaurus/releases/latest' + ) + .then(response => response.json()) + .then(response => { + if ( + response.message && + response.message.startsWith('API rate limit exceeded') + ) { + return { + update: false, + message: 'API rate limit exceeded, please try again later' + } + } else if (semver.gt(response.tag_name, app.getVersion())) { + return { + update: true, + message: + 'https://github.com/will-stone/browserosaurus/releases/latest' + } + } else { + return { update: false, message: 'You have the latest version' } + } + }) +} + +/** + * Send Browsers To Renderers + * + * Takes an array of browsers and sends it to the prefs and picker windows + * @param {array} browsers - array of browsers and their details. + * @returns {null} + */ +function sendBrowsersToRenderers(browsers) { + const enabledBrowsers = browsers.filter(browser => browser.enabled) + pickerWindow.webContents.send('incomingBrowsers', enabledBrowsers) + prefsWindow.webContents.send('incomingBrowsers', browsers) + return null } /** - * Toggle browser event + * Event: Check For Update * - * Listens for the toggle-browser event, triggered from the picker renderer and - * updates the enabled/disabled status of the checked/unchecked browser. - * @param {String} browserName - * @param {Boolean} enabled + * Listens for call to check for update, triggered when navigating to "About" + * tab in prefs window. + */ +ipcMain.on('check-for-update', async () => { + try { + const updateAvailable = await checkForUpdate() + prefsWindow.webContents.send('updateAvailable', updateAvailable) + } catch (err) { + console.log(err) + } +}) + +/** + * Event: Toggle Browser + * + * Listens for the toggle-browser event, triggered from the prefs renderer and + * updates the enabled/disabled status of the checked/unchecked browser. Then + * sends updated browsers array back to renderers. + * @param {string} browserName + * @param {boolean} enabled */ ipcMain.on('toggle-browser', (event, { browserName, enabled }) => { const browsers = store.get('browsers') @@ -207,21 +222,22 @@ ipcMain.on('toggle-browser', (event, { browserName, enabled }) => { }) /** - * Sort browser event + * Event: Sort Browser * - * Listens for the sort-browser event, triggered from the preferences renderer - * when a browser is dragged to a new position. - * @param {Number} oldIndex index of browser being moved from. - * @param {Number} newIndex index of place browser is being moved to. + * Listens for the sort-browser event, triggered from the prefs renderer when a + * browser is dragged to a new position. Then sends updated browsers array back + * to renderers. + * @param {number} oldIndex - index of browser being moved from. + * @param {number} newIndex - index of place browser is being moved to. */ ipcMain.on('sort-browser', (event, { oldIndex, newIndex }) => { const browsers = arrayMove(store.get('browsers'), oldIndex, newIndex) store.set('browsers', browsers) - pickerWindow.webContents.send('incomingBrowsers', browsers) + sendBrowsersToRenderers(browsers) }) /** - * App on ready + * Event: App Ready * * Run once electron has loaded and the app is considered _ready for use_. */ @@ -230,65 +246,80 @@ app.on('ready', () => { app.setAsDefaultProtocolClient('http') createTrayIcon() - createPreferencesWindow() + createPrefsWindow() createPickerWindow(() => { - pickerWindow.once('ready-to-show', () => { - findInstalledBrowsers(whiteListedBrowsers) - .then(installedBrowsers => { - const storedBrowsers = store.get('browsers') - - // remove unistalled browsers from user config - const storedBrowsersPruned = storedBrowsers - .map(browser => { - if (installedBrowsers.indexOf(browser.name) === -1) { - return null - } - return browser - }) - .filter(x => x) - - const installedBrowsersWithDetails = installedBrowsers.map(name => ({ - name, - key: whiteListedBrowsers[name].key, - alias: whiteListedBrowsers[name].alias || null, - enabled: true - })) - - const mergedBrowsers = unionBy( - storedBrowsersPruned, - installedBrowsersWithDetails, - 'name' - ) - - sendBrowsersToRenderers(mergedBrowsers) - store.set('browsers', mergedBrowsers) + pickerWindow.once('ready-to-show', async () => { + // get all apps on system + const installedApps = await scanForApps() + + // filter the apps to just the browsers on system + const installedBrowsers = Object.keys(whiteListedBrowsers) + .map(name => { + for (let i = 0; i < installedApps.length; i++) { + if (name === installedApps[i]) { + return name + } + } }) - .catch(err => console.error(err)) + .filter(x => x) // remove empties - if (global.URLToOpen) { - sendUrlToPicker(global.URLToOpen) - global.URLToOpen = null - } - appIsReady = true + // get browsers in store + const storedBrowsers = store.get('browsers') + + // convert each installed browser string to object with keyboard shortcut, alias name, and enabled status details. + const installedBrowsersWithDetails = installedBrowsers.map(name => ({ + name, + key: whiteListedBrowsers[name].key, + alias: whiteListedBrowsers[name].alias || null, + enabled: true + })) + + // remove unistalled browsers from stored config + const storedBrowsersPruned = storedBrowsers + .map(browser => { + if (installedBrowsers.indexOf(browser.name) === -1) { + return null + } + return browser + }) + .filter(x => x) // remove nulls + + // merge the stored with installed browsers, this will add new browsers where necessary, keeping the stored config if present. + const mergedBrowsers = unionBy( + storedBrowsersPruned, + installedBrowsersWithDetails, + 'name' + ) + + sendBrowsersToRenderers(mergedBrowsers) + store.set('browsers', mergedBrowsers) }) + + if (global.URLToOpen) { + // if Browserosaurus was opened with a link, this will now be sent on to the picker window + pickerWindow.webContents.send('incomingURL', global.URLToOpen) + global.URLToOpen = null + } + appIsReady = true }) }) -// Hide dock icon. Also prevents Browserosaurus from appearing in cmd-tab. -app.dock.hide() - /** - * App on open URL + * Event: Open URL * * When a URL is sent to Browserosaurus (as it is default browser), send it to * the picker. + * @param {string} url */ app.on('open-url', (event, url) => { event.preventDefault() if (appIsReady) { - sendUrlToPicker(url) + pickerWindow.webContents.send('incomingURL', url) } else { - // this will be handled later in the createWindow callback + // app not ready yet, this will be handled later in the createWindow callback global.URLToOpen = url } }) + +// Hide dock icon. Also prevents Browserosaurus from appearing in cmd-tab. +app.dock.hide() diff --git a/src/picker/pickerRenderer.js b/src/picker/pickerRenderer.js index b6a534c3..73b494e6 100644 --- a/src/picker/pickerRenderer.js +++ b/src/picker/pickerRenderer.js @@ -8,20 +8,22 @@ class PickerWindow extends Window { constructor() { super() this.url = null - this.urlField = document.getElementById('url') /** * Event: Listen for URL + * * Update URL global var and show window + * @param {string} incomingURL */ electron.ipcRenderer.on('incomingURL', (event, incomingURL) => { - this.urlField.innerText = incomingURL - this.url = incomingURL + this.setURL(incomingURL) + this.window.center() // moves window to current screen this.window.show() }) /** * Event: Escape key + * * Hide picker window */ Mousetrap.bind('esc', () => { @@ -29,35 +31,43 @@ class PickerWindow extends Window { }) } - onReceiveBrowsers(browsers) { - this.populatePicker(browsers) + /** + * Set URL Field + * + * @param {string} value + */ + setURL(value) { + const urlField = document.getElementById('url') + urlField.innerText = value + this.url = value } /** * Hide Window - * Hides the window, resetting the URL text and global var + * + * Hides this window, resetting the URL text. */ hideWindow() { // remove url from field - this.urlField.innerText = '' + this.setURL(null) setTimeout(() => { // if not paused, escape causes an audible error (beep). Presumably there's some sort of race condition here. Anyway, the timeout seems to solve it. this.window.hide() - this.url = null }, 0) } /** * Open Browser - * Sends the URL to the chosen browser and tells OS to open it. - * @param {String} appName name of browser as recognised by macOS + * + * Tells the OS to open chosen browser with this.url. + * @param {string} appName name of browser as recognised by macOS */ openBrowser(appName) { opn(this.url, { app: appName, wait: false }) .then(() => this.hideWindow()) .catch(() => { alert( - `Oh no! An error just occurred, please report this as a GitHub issue. Opened URL was ${ + `Oh no! An error just occurred, please report this as a GitHub issue. Opened URL was ${ this.url }` ) @@ -66,11 +76,12 @@ class PickerWindow extends Window { } /** - * Populate picker + * On Receive Browsers (see Window class) + * * Injects all present and enabled browsers as list items of picker. - * @param {Array} browsers + * @param {array} browsers - array of objects */ - populatePicker(browsers) { + onReceiveBrowsers(browsers) { if (browsers.length > 0) { // Populate installedBrowsers diff --git a/src/preferences/preferences.html b/src/prefs/prefs.html similarity index 95% rename from src/preferences/preferences.html rename to src/prefs/prefs.html index 41836b2f..a5c5accc 100644 --- a/src/preferences/preferences.html +++ b/src/prefs/prefs.html @@ -32,7 +32,7 @@

    \ No newline at end of file diff --git a/src/preferences/preferencesRenderer.js b/src/prefs/prefsRenderer.js similarity index 77% rename from src/preferences/preferencesRenderer.js rename to src/prefs/prefsRenderer.js index c5d6a7dd..d30740ac 100644 --- a/src/preferences/preferencesRenderer.js +++ b/src/prefs/prefsRenderer.js @@ -1,41 +1,51 @@ import sortable from 'sortablejs' -import electron from 'electron' +import electron, { remote } from 'electron' import Window from '../shared/Window' -class PreferencesWindow extends Window { +class PrefsWindow extends Window { constructor() { super() this.browserTabWindowHeight = 0 - - electron.ipcRenderer.on('updateAvailable', (event, updateUrl) => { - const updateStatus = document.getElementById('updateStatus') - if (updateUrl) { - updateStatus.innerText = updateUrl - } else { - updateStatus.innerText = updateUrl - } - }) + this.currentTab = 'browsers-tab' /** - * Incoming browsers event + * Event: Update Available * - * Listens for installed browsers from main.js, repopulating the window. + * After update checked in main, it is then sent here where About tab is + * updated. + * @param {string} updateURL */ - electron.ipcRenderer.on('incomingBrowsers', (event, browsers) => { - this.populatePreferences(browsers) + electron.ipcRenderer.on('updateAvailable', (event, updateURL) => { + const updateStatus = document.getElementById('updateStatus') + if (updateURL) { + updateStatus.innerText = updateURL + } else { + updateStatus.innerText = updateURL + } }) + // Setup this.attachNavClickEvents() this.populateVersion() } + /** + * Populate Version + * + * Get the current version and place it in the About tab + */ populateVersion() { const bVersion = document.getElementById('browserosaurusVersion') - const localVersion = electron.remote.app.getVersion() + const localVersion = remote.app.getVersion() bVersion.innerText = localVersion } + /** + * Attach Nav Click Events + * + * Make tab buttons clickable. + */ attachNavClickEvents() { const navbar = document.getElementById('navbar') const navItems = navbar.querySelectorAll('li') @@ -46,10 +56,17 @@ class PreferencesWindow extends Window { ) } + /** + * Switch Tab + * + * Move to selected tab. + * @param {string} tabId + */ switchTab(tabId) { const browsersTab = document.getElementById('browsers-tab') const aboutTab = document.getElementById('about-tab') const about = document.getElementById('about') + this.currentTab = tabId switch (tabId) { case 'browsers-tab': about.classList.remove('is-active') @@ -76,34 +93,39 @@ class PreferencesWindow extends Window { /** * Toggle browser * - * Sends the toggle-browser event to main.js. This enable/disables the browser. - * @param {String} browserName - * @param {Bool} enabled + * Sends the toggle-browser event to main.js. This enable/disables the + * browser. + * @param {string} browserName + * @param {boolean} enabled */ toggleBrowser(browserName, enabled) { electron.ipcRenderer.send('toggle-browser', { browserName, enabled }) } /** - * Sort browser + * Sort Browser * * Sends the sort-browser event to main.js. This allows browsers to be * reordered. - * @param {Number} oldIndex index of browser being moved from. - * @param {Number} newIndex index of place browser is being moved to. + * @param {number} oldIndex index of browser being moved from. + * @param {number} newIndex index of place browser is being moved to. */ sortBrowser(oldIndex, newIndex) { electron.ipcRenderer.send('sort-browser', { oldIndex, newIndex }) } /** - * Populate preferences + * On Receive Browsers (see Window class) * - * Injects all browsers as list items of preferences. + * Injects all browsers as list items in browsers tab. + * @param {array} browsers - array of objects */ - populatePreferences(browsers) { + onReceiveBrowsers(browsers) { this.browserTabWindowHeight = browsers.length * 64 + 97 - this.window.setSize(400, this.browserTabWindowHeight) + + if (this.currentTab === 'browsers-tab') { + this.window.setSize(400, this.browserTabWindowHeight) + } var browserListFrag = document.createDocumentFragment() @@ -177,4 +199,4 @@ class PreferencesWindow extends Window { } } -new PreferencesWindow() +new PrefsWindow() diff --git a/src/shared/Window.js b/src/shared/Window.js index 254f4fd0..2243aff3 100644 --- a/src/shared/Window.js +++ b/src/shared/Window.js @@ -6,13 +6,20 @@ class Window { this.browserList = document.getElementById('browserList') /** - * Event: Update browsers + * Event: Receive browsers array from main + * + * @param {array} browsers */ electron.ipcRenderer.on('incomingBrowsers', (event, browsers) => this.onReceiveBrowsers(browsers) ) } + /** + * On Receive Browsers + * + * public method + */ onReceiveBrowsers() { return false } diff --git a/src/utils/findInstalledBrowsers.js b/src/utils/scanForApps.js similarity index 53% rename from src/utils/findInstalledBrowsers.js rename to src/utils/scanForApps.js index 622a1607..aa71deaf 100644 --- a/src/utils/findInstalledBrowsers.js +++ b/src/utils/scanForApps.js @@ -3,13 +3,13 @@ import jp from 'jsonpath' import parser from 'xml2json' /** - * Find installed browsers + * Scan For Apps * - * Scans the system for all known browsers (white-listed in browsers.js file). - * @returns {Promise} returns array of browser names (Strings) if resolved, and + * Scans the system for all installed apps. + * @returns {promise} - returns array of app names (Strings) if resolved, and * string if rejected. */ -function findInstalledBrowsers(whiteListedBrowsers) { +function scanForApps() { return new Promise((fulfill, reject) => { const sp = spawn('system_profiler', ['-xml', 'SPApplicationsDataType']) @@ -29,18 +29,9 @@ function findInstalledBrowsers(whiteListedBrowsers) { profile, 'plist.array.dict.array[1].dict[*].string[0]' ) - const installedBrowsers = Object.keys(whiteListedBrowsers) - .map(name => { - for (let i = 0; i < installedApps.length; i++) { - if (name === installedApps[i]) { - return name - } - } - }) - .filter(x => x) // remove empties - fulfill(installedBrowsers) + fulfill(installedApps) }) }) } -export default findInstalledBrowsers +export default scanForApps From 7cf6664b9daac21b9cdf564f9e637dfee1411006 Mon Sep 17 00:00:00 2001 From: Will Stone Date: Sun, 14 Jan 2018 17:25:27 +0000 Subject: [PATCH 15/19] better prefs tab logic. Update button. --- src/main.js | 3 +- src/main.scss | 48 +++++++++++--------- src/prefs/prefs.html | 12 ++--- src/prefs/prefsRenderer.js | 89 +++++++++++++++++++------------------- 4 files changed, 80 insertions(+), 72 deletions(-) diff --git a/src/main.js b/src/main.js index ec55b5fb..1f585b6f 100644 --- a/src/main.js +++ b/src/main.js @@ -159,7 +159,8 @@ function checkForUpdate() { ) { return { update: false, - message: 'API rate limit exceeded, please try again later' + message: + "API rate limit exceeded, please try again later or visit Browserosaurus's website to check for an update" } } else if (semver.gt(response.tag_name, app.getVersion())) { return { diff --git a/src/main.scss b/src/main.scss index 0266c8da..e06544e0 100644 --- a/src/main.scss +++ b/src/main.scss @@ -89,30 +89,29 @@ body { line-height: 2rem; } -#preferences { - #navbar { - display: flex; - justify-content: space-around; - height: 64px; - margin: 0; - padding: 0; - list-style: none; - -webkit-app-region: drag; - border-bottom: 1px solid #111111; - line-height: 64px; - - li { - cursor: default; - -webkit-app-region: no-drag; - opacity: 0.5; +.navbar { + display: flex; + justify-content: space-around; + height: 64px; + margin: 0; + padding: 0; + list-style: none; + -webkit-app-region: drag; + border-bottom: 1px solid #111111; + line-height: 64px; - &.is-active, - &:hover { - opacity: 1; - } + li { + cursor: default; + -webkit-app-region: no-drag; + opacity: 0.5; + + &:hover { + opacity: 1; } } +} +#preferences { #browserList { > li { cursor: default; @@ -130,7 +129,13 @@ body { } } -.tab-pane { +.tab-button { + &.is-active { + opacity: 1; + } +} + +.tab { display: none; &.is-active { @@ -140,6 +145,7 @@ body { .button { padding: 1rem; + text-decoration: none; color: white; border: 0; background-color: #126edb; diff --git a/src/prefs/prefs.html b/src/prefs/prefs.html index a5c5accc..b7c5b1ca 100644 --- a/src/prefs/prefs.html +++ b/src/prefs/prefs.html @@ -11,22 +11,22 @@
    Preferences
    -