diff --git a/catalog/README.md b/catalog/README.md index 33d303ae..a81f841b 100644 --- a/catalog/README.md +++ b/catalog/README.md @@ -25,7 +25,6 @@ The yml file requires just a few fields: name: Gmail url: 'https://gmail.com' category: Productivity -mailtoHandler: 'https://mail.google.com/mail/?extsrc=mailto&url=%s' ``` The human then opens a PR. Tests pass, the PR gets merged. Yay! @@ -69,7 +68,6 @@ apps - Social Networking - Utilities - Video -- `mailtoHandler` is not required, specifies the URL pattern to handle `mailto` links. See [Navigator.registerProtocolHandler() Web API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler). Example: `https://mail.google.com/mail/?extsrc=mailto&url=%s`. ### Icons diff --git a/catalog/apps/fastmail/fastmail.yml b/catalog/apps/fastmail/fastmail.yml index ad00fa99..3b1e44bf 100644 --- a/catalog/apps/fastmail/fastmail.yml +++ b/catalog/apps/fastmail/fastmail.yml @@ -1,5 +1,3 @@ name: FastMail url: 'https://fastmail.com' category: Productivity -mailtoHandler: 'http://www.fastmail.fm/action/compose/?mailto=%s' -featured: true \ No newline at end of file diff --git a/catalog/apps/gmail/gmail.yml b/catalog/apps/gmail/gmail.yml index d9cb2e17..93fcc813 100644 --- a/catalog/apps/gmail/gmail.yml +++ b/catalog/apps/gmail/gmail.yml @@ -1,5 +1,3 @@ name: Gmail url: 'https://gmail.com' category: Productivity -mailtoHandler: 'https://mail.google.com/mail/?extsrc=mailto&url=%s' -featured: true \ No newline at end of file diff --git a/catalog/apps/google-voice/google-voice.yml b/catalog/apps/google-voice/google-voice.yml index 85e3085d..79ac99d3 100644 --- a/catalog/apps/google-voice/google-voice.yml +++ b/catalog/apps/google-voice/google-voice.yml @@ -1,4 +1,3 @@ name: 'Google Voice' category: 'Social Networking' url: 'https://voice.google.com' -featured: true \ No newline at end of file diff --git a/catalog/apps/messages/messages.yml b/catalog/apps/messages/messages.yml index 4464fed2..ebbe8b5c 100644 --- a/catalog/apps/messages/messages.yml +++ b/catalog/apps/messages/messages.yml @@ -1,4 +1,3 @@ name: Messages category: 'Social Networking' url: 'https://messages.android.com/' -featured: true \ No newline at end of file diff --git a/catalog/apps/messenger/messenger.yml b/catalog/apps/messenger/messenger.yml index eebced1b..e4211d02 100644 --- a/catalog/apps/messenger/messenger.yml +++ b/catalog/apps/messenger/messenger.yml @@ -1,4 +1,3 @@ name: Messenger category: 'Social Networking' url: 'https://messenger.com' -featured: true \ No newline at end of file diff --git a/catalog/apps/microsoft-outlook/microsoft-outlook.yml b/catalog/apps/microsoft-outlook/microsoft-outlook.yml index aa2b8f2f..3acd77f5 100644 --- a/catalog/apps/microsoft-outlook/microsoft-outlook.yml +++ b/catalog/apps/microsoft-outlook/microsoft-outlook.yml @@ -1,5 +1,3 @@ name: 'Microsoft Outlook' url: 'https://outlook.live.com/owa/' category: Productivity -mailtoHandler: 'https://outlook.live.com/owa/?path=/mail/action/compose&to=%s' -featured: true \ No newline at end of file diff --git a/catalog/apps/twitter/twitter.yml b/catalog/apps/twitter/twitter.yml index d7babb79..d706ef49 100644 --- a/catalog/apps/twitter/twitter.yml +++ b/catalog/apps/twitter/twitter.yml @@ -1,4 +1,3 @@ name: Twitter category: 'Social Networking' url: 'https://twitter.com/' -featured: true \ No newline at end of file diff --git a/catalog/apps/whatsapp/whatsapp.yml b/catalog/apps/whatsapp/whatsapp.yml index 404e0f46..b5a1ef06 100644 --- a/catalog/apps/whatsapp/whatsapp.yml +++ b/catalog/apps/whatsapp/whatsapp.yml @@ -1,4 +1,3 @@ name: WhatsApp url: 'https://web.whatsapp.com' category: 'Social Networking' -featured: true \ No newline at end of file diff --git a/catalog/apps/yahoo-mail/yahoo-mail.yml b/catalog/apps/yahoo-mail/yahoo-mail.yml index 68496a57..04c8232d 100644 --- a/catalog/apps/yahoo-mail/yahoo-mail.yml +++ b/catalog/apps/yahoo-mail/yahoo-mail.yml @@ -1,4 +1,3 @@ name: 'Yahoo Mail' url: 'https://mail.yahoo.com' -category: Productivity -mailtoHandler: 'https://compose.mail.yahoo.com/?To=%s' \ No newline at end of file +category: Productivity \ No newline at end of file diff --git a/catalog/apps/zoho-mail/zoho-mail.yml b/catalog/apps/zoho-mail/zoho-mail.yml index 789ba187..69ae2639 100644 --- a/catalog/apps/zoho-mail/zoho-mail.yml +++ b/catalog/apps/zoho-mail/zoho-mail.yml @@ -1,4 +1,3 @@ name: 'Zoho Mail' url: 'https://mail.zoho.com' -category: Productivity -mailtoHandler: 'https://mail.zoho.com/mail/compose.do?extsrc=mailto&mode=compose&tp=zb&ct=%s' \ No newline at end of file +category: Productivity \ No newline at end of file diff --git a/public/constants/mailto-urls.js b/public/constants/mailto-urls.js new file mode 100644 index 00000000..44f1e951 --- /dev/null +++ b/public/constants/mailto-urls.js @@ -0,0 +1,34 @@ +// specifies the URL pattern to handle `mailto` links. +// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler + +const rawMailtoUrls = [ + { + hostnames: ['fastmail.com'], + mailtoUrl: 'http://www.fastmail.fm/action/compose/?mailto=%s', + }, + { + hostnames: ['gmail.com', 'mail.google.com', 'googlemail.com'], + mailtoUrl: 'https://mail.google.com/mail/?extsrc=mailto&url=%s', + }, + { + hostnames: ['outlook.live.com', 'outlook.com', 'hotmail.com'], + mailtoUrl: 'https://outlook.live.com/owa/?path=/mail/action/compose&to=%s', + }, + { + hostnames: ['mail.yahoo.com', 'yahoomail.com'], + mailtoUrl: 'https://compose.mail.yahoo.com/?To=%s', + }, + { + hostnames: ['mail.zoho.com'], + mailtoUrl: 'https://mail.zoho.com/mail/compose.do?extsrc=mailto&mode=compose&tp=zb&ct=%s', + }, +]; + +const MAILTO_URLS = {}; +rawMailtoUrls.forEach((item) => { + item.hostnames.forEach((hostname) => { + MAILTO_URLS[hostname] = item.mailtoUrl; + }); +}); + +module.exports = MAILTO_URLS; diff --git a/public/constants/index.js b/public/constants/paths.js similarity index 100% rename from public/constants/index.js rename to public/constants/paths.js diff --git a/public/electron.js b/public/electron.js index 31915faa..89e944ff 100755 --- a/public/electron.js +++ b/public/electron.js @@ -11,6 +11,9 @@ const createMenu = require('./libs/create-menu'); const { addView } = require('./libs/views'); const { getPreference } = require('./libs/preferences'); const { getWorkspaces } = require('./libs/workspaces'); +const extractHostname = require('./libs/extract-hostname'); + +const MAILTO_URLS = require('./constants/mailto-urls'); require('./libs/updater'); @@ -54,6 +57,7 @@ if (!gotTheLock) { app.on('ready', () => { global.attachToMenubar = getPreference('attachToMenubar'); global.showNavigationBar = getPreference('navigationBar'); + global.MAILTO_URLS = MAILTO_URLS; commonInit(); }); @@ -86,6 +90,36 @@ if (!gotTheLock) { app.on('open-url', (e, url) => { e.preventDefault(); + const workspaces = Object.values(getWorkspaces()); + if (workspaces.length < 1) return; + + // handle mailto: + if (url.startsWith('mailto:')) { + const mailtoWorkspaces = workspaces + .filter((workspace) => extractHostname(workspace.homeUrl) in MAILTO_URLS); + // pick automically if there's only one choice + if (mailtoWorkspaces.length === 0) { + ipcMain.emit( + 'request-show-message-box', null, + 'None of your workspaces supports composing email messages.', + 'error', + ); + } else if (mailtoWorkspaces.length === 1) { + const mailtoUrl = MAILTO_URLS[extractHostname(mailtoWorkspaces[0].homeUrl)]; + const u = mailtoUrl.replace('%s', url); + ipcMain.emit('request-load-url', null, u, mailtoWorkspaces[0].id); + } else { + app.whenReady() + .then(() => openUrlWithWindow.show(url)); + } + return; + } + + // handle https/http + // pick automically if there's only one choice + if (workspaces.length === 1) { + ipcMain.emit('request-load-url', null, url, workspaces[0].id); + } app.whenReady() .then(() => openUrlWithWindow.show(url)); }); diff --git a/public/libs/extract-hostname.js b/public/libs/extract-hostname.js new file mode 100644 index 00000000..9a88fb68 --- /dev/null +++ b/public/libs/extract-hostname.js @@ -0,0 +1,27 @@ +/* eslint-disable prefer-destructuring */ +const extractHostname = (url) => { + try { + let hostname; + + // find & remove protocol (http, ftp, etc.) and get hostname + if (url.indexOf('://') > -1) { + hostname = url.split('/')[2]; + } else { + hostname = url.split('/')[0]; + } + + // find & remove port number + hostname = hostname.split(':')[0]; + // find & remove "?" + hostname = hostname.split('?')[0]; + + // find & remove "www" + hostname = hostname.replace('www.', ''); + + return hostname.trim(); + } catch (_) { + return null; + } +}; + +module.exports = extractHostname; diff --git a/public/libs/workspaces-views.js b/public/libs/workspaces-views.js index 6366813c..db0e1e95 100644 --- a/public/libs/workspaces-views.js +++ b/public/libs/workspaces-views.js @@ -19,8 +19,8 @@ const { const mainWindow = require('../windows/main'); -const createWorkspaceView = (name, homeUrl, picture, mailtoHandler) => { - const newWorkspace = createWorkspace(name, homeUrl, picture, mailtoHandler); +const createWorkspaceView = (name, homeUrl, picture) => { + const newWorkspace = createWorkspace(name, homeUrl, picture); setActiveWorkspace(newWorkspace.id); addView(mainWindow.get(), getWorkspace(newWorkspace.id)); diff --git a/public/libs/workspaces.js b/public/libs/workspaces.js index 102dc848..9f4e0a29 100644 --- a/public/libs/workspaces.js +++ b/public/libs/workspaces.js @@ -157,7 +157,7 @@ const removeWorkspace = (id) => { settings.delete(`workspaces.${v}.${id}`); }; -const createWorkspace = (name, homeUrl, picture, mailtoHandler) => { +const createWorkspace = (name, homeUrl) => { const newId = uuidv1(); // find largest order @@ -173,7 +173,6 @@ const createWorkspace = (name, homeUrl, picture, mailtoHandler) => { id: newId, name, homeUrl, - mailtoHandler, order: max + 1, active: false, }; diff --git a/public/listeners/index.js b/public/listeners/index.js index a6672168..0d4db3d6 100755 --- a/public/listeners/index.js +++ b/public/listeners/index.js @@ -173,8 +173,8 @@ const loadListeners = () => { e.returnValue = workspaces; }); - ipcMain.on('request-create-workspace', (e, name, homeUrl, picture, mailtoHandler) => { - createWorkspaceView(name, homeUrl, picture, mailtoHandler); + ipcMain.on('request-create-workspace', (e, name, homeUrl, picture) => { + createWorkspaceView(name, homeUrl, picture); createMenu(); }); diff --git a/public/preload/view.js b/public/preload/view.js index 7dcd601a..72da9dac 100644 --- a/public/preload/view.js +++ b/public/preload/view.js @@ -16,9 +16,20 @@ window.global = {}; window.ipcRenderer = ipcRenderer; window.onload = () => { - window.close = () => { - ipcRenderer.send('request-go-home'); - }; + // overwrite gmail email discard button + if (window.location.href.startsWith('https://mail.google.com') && window.location.href.includes('source=mailto')) { + const checkExist = setInterval(() => { + if (document.getElementById(':qz')) { + const discardButton = document.getElementById(':qz'); + // https://stackoverflow.com/a/46986927 + discardButton.addEventListener('click', (e) => { + e.stopPropagation(); + ipcRenderer.send('request-go-home'); + }, true); + clearInterval(checkExist); + } + }, 100); // check every 100ms + } const jsCodeInjection = ipcRenderer.sendSync('get-preference', 'jsCodeInjection'); const cssCodeInjection = ipcRenderer.sendSync('get-preference', 'cssCodeInjection'); diff --git a/public/windows/about.js b/public/windows/about.js index 10b0e327..dc08e8f7 100644 --- a/public/windows/about.js +++ b/public/windows/about.js @@ -1,7 +1,7 @@ const { BrowserWindow } = require('electron'); const path = require('path'); -const { REACT_PATH } = require('../constants'); +const { REACT_PATH } = require('../constants/paths'); const { getPreference } = require('../libs/preferences'); const mainWindow = require('./main'); diff --git a/public/windows/add-workspace.js b/public/windows/add-workspace.js index 0697e4e6..34a03945 100644 --- a/public/windows/add-workspace.js +++ b/public/windows/add-workspace.js @@ -1,7 +1,7 @@ const { BrowserWindow } = require('electron'); const path = require('path'); -const { REACT_PATH } = require('../constants'); +const { REACT_PATH } = require('../constants/paths'); const { getPreference } = require('../libs/preferences'); const mainWindow = require('./main'); diff --git a/public/windows/auth.js b/public/windows/auth.js index 882ee0aa..6f8baf77 100644 --- a/public/windows/auth.js +++ b/public/windows/auth.js @@ -1,7 +1,7 @@ const { BrowserWindow, ipcMain } = require('electron'); const path = require('path'); -const { REACT_PATH } = require('../constants'); +const { REACT_PATH } = require('../constants/paths'); const { getPreference } = require('../libs/preferences'); const mainWindow = require('./main'); diff --git a/public/windows/code-injection.js b/public/windows/code-injection.js index 7bfd7561..2612db60 100644 --- a/public/windows/code-injection.js +++ b/public/windows/code-injection.js @@ -1,7 +1,7 @@ const { BrowserWindow } = require('electron'); const path = require('path'); -const { REACT_PATH } = require('../constants'); +const { REACT_PATH } = require('../constants/paths'); const { getPreference } = require('../libs/preferences'); const mainWindow = require('./main'); diff --git a/public/windows/edit-workspace.js b/public/windows/edit-workspace.js index af75d64b..c16cfc19 100644 --- a/public/windows/edit-workspace.js +++ b/public/windows/edit-workspace.js @@ -1,7 +1,7 @@ const { BrowserWindow } = require('electron'); const path = require('path'); -const { REACT_PATH } = require('../constants'); +const { REACT_PATH } = require('../constants/paths'); const { getPreference } = require('../libs/preferences'); const mainWindow = require('./main'); diff --git a/public/windows/license-registration.js b/public/windows/license-registration.js index fc0d6caf..be0d50c0 100644 --- a/public/windows/license-registration.js +++ b/public/windows/license-registration.js @@ -1,7 +1,7 @@ const { BrowserWindow } = require('electron'); const path = require('path'); -const { REACT_PATH } = require('../constants'); +const { REACT_PATH } = require('../constants/paths'); const { getPreference } = require('../libs/preferences'); const mainWindow = require('./main'); diff --git a/public/windows/main.js b/public/windows/main.js index 2bde48ac..2eef393d 100644 --- a/public/windows/main.js +++ b/public/windows/main.js @@ -9,7 +9,7 @@ const windowStateKeeper = require('electron-window-state'); const { menubar } = require('menubar'); const path = require('path'); -const { REACT_PATH } = require('../constants'); +const { REACT_PATH } = require('../constants/paths'); const { getPreference } = require('../libs/preferences'); let win; diff --git a/public/windows/open-url-with.js b/public/windows/open-url-with.js index b550c918..bdae9ff2 100644 --- a/public/windows/open-url-with.js +++ b/public/windows/open-url-with.js @@ -1,7 +1,7 @@ const { BrowserWindow } = require('electron'); const path = require('path'); -const { REACT_PATH } = require('../constants'); +const { REACT_PATH } = require('../constants/paths'); const { getPreference } = require('../libs/preferences'); const mainWindow = require('./main'); diff --git a/public/windows/preferences.js b/public/windows/preferences.js index cac78c03..b2e74171 100644 --- a/public/windows/preferences.js +++ b/public/windows/preferences.js @@ -1,7 +1,7 @@ const { BrowserWindow } = require('electron'); const path = require('path'); -const { REACT_PATH } = require('../constants'); +const { REACT_PATH } = require('../constants/paths'); const mainWindow = require('./main'); const { getPreference } = require('../libs/preferences'); diff --git a/src/components/add-workspace/add-custom-app-card.js b/src/components/add-workspace/add-custom-app-card.js index 03e42fde..d9442ab9 100755 --- a/src/components/add-workspace/add-custom-app-card.js +++ b/src/components/add-workspace/add-custom-app-card.js @@ -63,7 +63,7 @@ const AddCustomAppCard = (props) => {