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) => {
- Add Custom App + Add Custom Workspace Make it your own! diff --git a/src/components/add-workspace/app-card.js b/src/components/add-workspace/app-card.js index 247a95c9..f9e7fffe 100755 --- a/src/components/add-workspace/app-card.js +++ b/src/components/add-workspace/app-card.js @@ -73,7 +73,6 @@ const AppCard = (props) => { classes, icon, icon128, - mailtoHandler, name, onUpdateForm, onUpdateMode, @@ -110,7 +109,7 @@ const AppCard = (props) => { { onUpdateForm({ - name, homeUrl: url, picturePath: icon, mailtoHandler, + name, homeUrl: url, picturePath: icon, }); onUpdateMode('custom'); }} @@ -125,7 +124,7 @@ const AppCard = (props) => { size="medium" variant="contained" onClick={() => { - requestCreateWorkspace(name, url, icon128, mailtoHandler); + requestCreateWorkspace(name, url, icon128); remote.getCurrentWindow().close(); }} > @@ -138,7 +137,6 @@ const AppCard = (props) => { }; AppCard.defaultProps = { - mailtoHandler: null, icon128: null, }; @@ -146,7 +144,6 @@ AppCard.propTypes = { classes: PropTypes.object.isRequired, icon128: PropTypes.string, icon: PropTypes.string.isRequired, - mailtoHandler: PropTypes.string, name: PropTypes.string.isRequired, onUpdateForm: PropTypes.func.isRequired, onUpdateMode: PropTypes.func.isRequired, diff --git a/src/components/add-workspace/form.js b/src/components/add-workspace/form.js index 58543f24..3aa5a852 100644 --- a/src/components/add-workspace/form.js +++ b/src/components/add-workspace/form.js @@ -5,6 +5,8 @@ import Button from '@material-ui/core/Button'; import TextField from '@material-ui/core/TextField'; import connectComponent from '../../helpers/connect-component'; +import isUrl from '../../helpers/is-url'; +import getMailtoUrl from '../../helpers/get-mailto-url'; import { updateForm, save } from '../../state/add-workspace/actions'; @@ -12,8 +14,6 @@ import defaultIcon from '../../images/default-icon.png'; import EnhancedDialogTitle from './enhanced-dialog-title'; -import isUrl from '../../helpers/is-url'; - const styles = (theme) => ({ root: { background: theme.palette.background.paper, @@ -79,6 +79,7 @@ const AddWorkspaceCustom = ({ classes, homeUrl, homeUrlError, + isMailApp, name, nameError, onSave, @@ -87,7 +88,7 @@ const AddWorkspaceCustom = ({ }) => (
- Add Custom App + Add Custom Workspace
onUpdateForm({ homeUrl: e.target.value })} + helperText={!homeUrlError && isMailApp && 'Email app detected.'} />
@@ -178,6 +180,7 @@ AddWorkspaceCustom.propTypes = { classes: PropTypes.object.isRequired, homeUrl: PropTypes.string, homeUrlError: PropTypes.string, + isMailApp: PropTypes.bool.isRequired, name: PropTypes.string, nameError: PropTypes.string, onSave: PropTypes.func.isRequired, @@ -188,6 +191,7 @@ AddWorkspaceCustom.propTypes = { const mapStateToProps = (state) => ({ homeUrl: state.addWorkspace.form.homeUrl, homeUrlError: state.addWorkspace.form.homeUrlError, + isMailApp: Boolean(getMailtoUrl(state.addWorkspace.form.homeUrl)), name: state.addWorkspace.form.name, nameError: state.addWorkspace.form.nameError, picturePath: state.addWorkspace.form.picturePath, diff --git a/src/components/add-workspace/index.js b/src/components/add-workspace/index.js index 503ac2cc..5c732a19 100644 --- a/src/components/add-workspace/index.js +++ b/src/components/add-workspace/index.js @@ -126,7 +126,6 @@ class AddWorkspace extends React.Component { url={app.url} icon={app.icon} icon128={app.icon128} - mailtoHandler={app.mailtoHandler} /> ))} {!isGetting && } @@ -183,7 +182,7 @@ class AddWorkspace extends React.Component { className={classes.bottomNavigation} > } /> - } /> + } />
); diff --git a/src/components/edit-workspace/index.js b/src/components/edit-workspace/index.js index 7fece168..10e9b903 100644 --- a/src/components/edit-workspace/index.js +++ b/src/components/edit-workspace/index.js @@ -5,11 +5,13 @@ import Button from '@material-ui/core/Button'; import TextField from '@material-ui/core/TextField'; import connectComponent from '../../helpers/connect-component'; +import getMailtoUrl from '../../helpers/get-mailto-url'; import defaultIcon from '../../images/default-icon.png'; import { updateForm, save } from '../../state/edit-workspace/actions'; + const styles = (theme) => ({ root: { background: theme.palette.background.paper, @@ -67,6 +69,7 @@ const EditWorkspace = ({ classes, homeUrl, homeUrlError, + isMailApp, name, nameError, onSave, @@ -104,6 +107,7 @@ const EditWorkspace = ({ }} value={homeUrl} onChange={(e) => onUpdateForm({ homeUrl: e.target.value })} + helperText={!homeUrlError && isMailApp && 'Email app detected.'} />
@@ -161,6 +165,7 @@ EditWorkspace.propTypes = { classes: PropTypes.object.isRequired, homeUrl: PropTypes.string.isRequired, homeUrlError: PropTypes.string, + isMailApp: PropTypes.bool.isRequired, name: PropTypes.string.isRequired, nameError: PropTypes.string, onSave: PropTypes.func.isRequired, @@ -171,6 +176,7 @@ EditWorkspace.propTypes = { const mapStateToProps = (state) => ({ homeUrl: state.editWorkspace.form.homeUrl, homeUrlError: state.editWorkspace.form.homeUrlError, + isMailApp: Boolean(getMailtoUrl(state.editWorkspace.form.homeUrl)), id: state.editWorkspace.form.id, name: state.editWorkspace.form.name, nameError: state.editWorkspace.form.nameError, diff --git a/src/components/open-url-with/index.js b/src/components/open-url-with/index.js index 8a5c95cb..f72ad0fd 100644 --- a/src/components/open-url-with/index.js +++ b/src/components/open-url-with/index.js @@ -9,34 +9,43 @@ import ChevronRightIcon from '@material-ui/icons/ChevronRight'; import connectComponent from '../../helpers/connect-component'; import getWorkspacesAsList from '../../helpers/get-workspaces-as-list'; +import getMailtoUrl from '../../helpers/get-mailto-url'; import { requestLoadURL } from '../../senders'; const { remote } = window.require('electron'); -const OpenUrlWith = ({ workspaces }) => ( - - {getWorkspacesAsList(workspaces).map((workspace) => workspace.mailtoHandler && ( +const OpenUrlWith = ({ workspaces }) => { + const incomingUrl = remote.getGlobal('incomingUrl'); + const isMailtoUrl = incomingUrl.startsWith('mailto:'); + + const renderWorkspace = (workspace, i) => { + if (isMailtoUrl && !getMailtoUrl(workspace.homeUrl)) return null; + return ( { - const incomingUrl = remote.getGlobal('incomingUrl'); - - const u = incomingUrl.startsWith('mailto:') ? workspace.mailtoHandler.replace('%s', incomingUrl) : incomingUrl; + const u = isMailtoUrl ? getMailtoUrl(workspace.homeUrl).replace('%s', incomingUrl) : incomingUrl; requestLoadURL(u, workspace.id); remote.getCurrentWindow().close(); }} > - ))} - -); + ); + }; + + return ( + + {getWorkspacesAsList(workspaces).map(renderWorkspace)} + + ); +}; OpenUrlWith.propTypes = { workspaces: PropTypes.object.isRequired, diff --git a/src/components/preferences/index.js b/src/components/preferences/index.js index 5f30ea19..d3abd069 100644 --- a/src/components/preferences/index.js +++ b/src/components/preferences/index.js @@ -17,7 +17,7 @@ import connectComponent from '../../helpers/connect-component'; import StatedMenu from '../shared/stated-menu'; -import { updateIsDefaultMailClient } from '../../state/general/actions'; +import { updateIsDefaultMailClient, updateIsDefaultWebBrowser } from '../../state/general/actions'; import { requestOpenInBrowser, @@ -51,7 +51,7 @@ const styles = (theme) => ({ const getThemeString = (theme) => { if (theme === 'light') return 'Light'; if (theme === 'dark') return 'Dark'; - return 'Automatic'; + return 'System default'; }; const getOpenAtLoginString = (openAtLogin) => { @@ -67,9 +67,11 @@ const Preferences = ({ cssCodeInjection, downloadPath, isDefaultMailClient, + isDefaultWebBrowser, jsCodeInjection, navigationBar, onUpdateIsDefaultMailClient, + onUpdateIsDefaultWebBrowser, openAtLogin, rememberLastPageVisited, shareWorkspaceBrowsingData, @@ -93,7 +95,7 @@ const Preferences = ({ )} > - requestSetPreference('theme', 'automatic')}>Automatic + requestSetPreference('theme', 'automatic')}>System default requestSetPreference('theme', 'light')}>Light requestSetPreference('theme', 'dark')}>Dark @@ -288,7 +290,7 @@ const Preferences = ({ - Default Email Client + Default App @@ -313,6 +315,29 @@ const Preferences = ({ )} + + {isDefaultWebBrowser ? ( + + + + ) : ( + + + + + )} @@ -379,9 +404,11 @@ Preferences.propTypes = { cssCodeInjection: PropTypes.string, downloadPath: PropTypes.string.isRequired, isDefaultMailClient: PropTypes.bool.isRequired, + isDefaultWebBrowser: PropTypes.bool.isRequired, jsCodeInjection: PropTypes.string, navigationBar: PropTypes.bool.isRequired, onUpdateIsDefaultMailClient: PropTypes.func.isRequired, + onUpdateIsDefaultWebBrowser: PropTypes.func.isRequired, openAtLogin: PropTypes.oneOf(['yes', 'yes-hidden', 'no']).isRequired, rememberLastPageVisited: PropTypes.bool.isRequired, shareWorkspaceBrowsingData: PropTypes.bool.isRequired, @@ -410,6 +437,7 @@ const mapStateToProps = (state) => ({ const actionCreators = { updateIsDefaultMailClient, + updateIsDefaultWebBrowser, }; export default connectComponent( diff --git a/src/constants/actions.js b/src/constants/actions.js index bc6b07f7..651232af 100644 --- a/src/constants/actions.js +++ b/src/constants/actions.js @@ -17,6 +17,7 @@ export const UPDATE_DID_FAIL_LOAD = 'UPDATE_DID_FAIL_LOAD'; export const UPDATE_IS_DARK_MODE = 'UPDATE_IS_DARK_MODE'; export const UPDATE_IS_FULL_SCREEN = 'UPDATE_IS_FULL_SCREEN'; export const UPDATE_IS_DEFAULT_MAIL_CLIENT = 'UPDATE_IS_DEFAULT_MAIL_CLIENT'; +export const UPDATE_IS_DEFAULT_WEB_BROWSER = 'UPDATE_IS_DEFAULT_WEB_BROWSER'; export const UPDATE_IS_LOADING = 'UPDATE_IS_LOADING'; // Find In Page diff --git a/src/helpers/extract-hostname.js b/src/helpers/extract-hostname.js index cbef7ba1..180311bc 100644 --- a/src/helpers/extract-hostname.js +++ b/src/helpers/extract-hostname.js @@ -1,23 +1,27 @@ /* eslint-disable prefer-destructuring */ const extractHostname = (url) => { - let hostname; + 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 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; } - - // find & remove port number - hostname = hostname.split(':')[0]; - // find & remove "?" - hostname = hostname.split('?')[0]; - - // find & remove "www" - hostname = hostname.replace('www.', ''); - - return hostname; }; export default extractHostname; diff --git a/src/helpers/get-mailto-url.js b/src/helpers/get-mailto-url.js new file mode 100644 index 00000000..aa68565f --- /dev/null +++ b/src/helpers/get-mailto-url.js @@ -0,0 +1,18 @@ +import extractHostname from './extract-hostname'; + +let MAILTO_URLS; + +const getMailtoUrl = (url) => { + if (!MAILTO_URLS) { + MAILTO_URLS = window.require('electron').remote.getGlobal('MAILTO_URLS'); + } + + const extractedHostname = extractHostname(url); + if (extractedHostname in MAILTO_URLS) { + return MAILTO_URLS[extractedHostname]; + } + + return null; +}; + +export default getMailtoUrl; diff --git a/src/senders/index.js b/src/senders/index.js index 2bb9c13b..c41acc16 100644 --- a/src/senders/index.js +++ b/src/senders/index.js @@ -16,7 +16,6 @@ export const requestShowAddWorkspaceWindow = () => ipcRenderer.send('request-sho export const requestShowCodeInjectionWindow = (type) => ipcRenderer.send('request-show-code-injection-window', type); export const requestShowLicenseRegistrationWindow = (type) => ipcRenderer.send('request-show-license-registration-window', type); - // Preferences export const getPreference = (name) => ipcRenderer.sendSync('get-preference', name); export const getPreferences = () => ipcRenderer.sendSync('get-preferences'); @@ -32,7 +31,7 @@ export const requestSetSystemPreference = (name, value) => ipcRenderer.send('req // Workspace export const getWorkspace = (id) => ipcRenderer.sendSync('get-workspace', id); export const getWorkspaces = () => ipcRenderer.sendSync('get-workspaces'); -export const requestCreateWorkspace = (name, homeUrl, picture, mailtoHandler) => ipcRenderer.send('request-create-workspace', name, homeUrl, picture, mailtoHandler); +export const requestCreateWorkspace = (name, homeUrl, picture) => ipcRenderer.send('request-create-workspace', name, homeUrl, picture); export const requestSetWorkspace = (id, opts) => ipcRenderer.send('request-set-workspace', id, opts); export const requestSetWorkspacePicture = (id, picturePath) => ipcRenderer.send('request-set-workspace-picture', id, picturePath); export const requestRemoveWorkspacePicture = (id) => ipcRenderer.send('request-remove-workspace-picture', id); diff --git a/src/state/add-workspace/actions.js b/src/state/add-workspace/actions.js index 05b5ba18..460d4fd3 100755 --- a/src/state/add-workspace/actions.js +++ b/src/state/add-workspace/actions.js @@ -108,7 +108,7 @@ export const save = () => (dispatch, getState) => { return dispatch(updateForm(validatedChanges)); } - requestCreateWorkspace(form.name, form.homeUrl, form.picturePath, form.mailtoHandler); + requestCreateWorkspace(form.name, form.homeUrl.trim(), form.picturePath); remote.getCurrentWindow().close(); return null; }; diff --git a/src/state/edit-workspace/actions.js b/src/state/edit-workspace/actions.js index 915d246e..698bd758 100644 --- a/src/state/edit-workspace/actions.js +++ b/src/state/edit-workspace/actions.js @@ -43,7 +43,7 @@ export const save = () => (dispatch, getState) => { id, { name: form.name, - homeUrl: form.homeUrl, + homeUrl: form.homeUrl.trim(), }, ); diff --git a/src/state/general/actions.js b/src/state/general/actions.js index f4f0c9cd..3033198a 100644 --- a/src/state/general/actions.js +++ b/src/state/general/actions.js @@ -4,6 +4,7 @@ import { UPDATE_DID_FAIL_LOAD, UPDATE_IS_DARK_MODE, UPDATE_IS_DEFAULT_MAIL_CLIENT, + UPDATE_IS_DEFAULT_WEB_BROWSER, UPDATE_IS_FULL_SCREEN, UPDATE_IS_LOADING, } from '../../constants/actions'; @@ -44,6 +45,13 @@ export const updateIsDefaultMailClient = (isDefaultMailClient) => (dispatch) => }); }; +export const updateIsDefaultWebBrowser = (isDefaultWebBrowser) => (dispatch) => { + dispatch({ + type: UPDATE_IS_DEFAULT_WEB_BROWSER, + isDefaultWebBrowser, + }); +}; + export const updateIsDarkMode = (isDarkMode) => (dispatch) => { dispatch({ type: UPDATE_IS_DARK_MODE, diff --git a/src/state/general/reducers.js b/src/state/general/reducers.js index 50ef280d..d371ecdd 100644 --- a/src/state/general/reducers.js +++ b/src/state/general/reducers.js @@ -6,6 +6,7 @@ import { UPDATE_DID_FAIL_LOAD, UPDATE_IS_DARK_MODE, UPDATE_IS_DEFAULT_MAIL_CLIENT, + UPDATE_IS_DEFAULT_WEB_BROWSER, UPDATE_IS_FULL_SCREEN, UPDATE_IS_LOADING, } from '../../constants/actions'; @@ -40,6 +41,14 @@ const isDefaultMailClient = (state = remote.app.isDefaultProtocolClient('mailto' } }; +const isDefaultWebBrowser = (state = remote.app.isDefaultProtocolClient('http'), action) => { + switch (action.type) { + case UPDATE_IS_DEFAULT_WEB_BROWSER: return action.isDefaultWebBrowser; + default: return state; + } +}; + + const isDarkMode = (state = remote.systemPreferences.isDarkMode(), action) => { switch (action.type) { case UPDATE_IS_DARK_MODE: return action.isDarkMode; @@ -67,6 +76,7 @@ export default combineReducers({ didFailLoad, isDarkMode, isDefaultMailClient, + isDefaultWebBrowser, isFullScreen, isLoading, });