From c5b44c0259387030f9e3d31ae1db8903a95b6b67 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Mon, 11 Dec 2017 12:25:49 +0100 Subject: always initialize ContextMenuBuilder but not spellchecker --- gulpfile.babel.js | 6 +++++- src/webview/plugin.js | 31 +++++++++++++++++------------ src/webview/spellchecker.js | 48 ++++++++++++++++++++++++++++----------------- 3 files changed, 54 insertions(+), 31 deletions(-) diff --git a/gulpfile.babel.js b/gulpfile.babel.js index d947974b3..b50001b2d 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -110,7 +110,11 @@ export function watch() { } export function webserver() { - gulp.src(paths.dest) + gulp.src([ + paths.dest, + `!${paths.dest}/electron/**`, + `!${paths.dest}/webview/**`, + ]) .pipe(server({ livereload: true, })); diff --git a/src/webview/plugin.js b/src/webview/plugin.js index c877132b1..610dffc8e 100644 --- a/src/webview/plugin.js +++ b/src/webview/plugin.js @@ -1,14 +1,14 @@ import { ipcRenderer } from 'electron'; +import { ContextMenuListener, ContextMenuBuilder } from 'electron-spellchecker'; import path from 'path'; +import { isDevMode } from '../environment'; import RecipeWebview from './lib/RecipeWebview'; import Spellchecker from './spellchecker.js'; import './notifications.js'; import './ime.js'; -const spellchecker = new Spellchecker(); - ipcRenderer.on('initializeRecipe', (e, data) => { const modulePath = path.join(data.recipe.path, 'webview.js'); // Delete module from cache @@ -21,20 +21,27 @@ ipcRenderer.on('initializeRecipe', (e, data) => { } }); +let contextMenuBuilder = new ContextMenuBuilder(null, null, isDevMode); +const spellchecker = new Spellchecker(); + +new ContextMenuListener((info) => { // eslint-disable-line + contextMenuBuilder.showPopupMenu(info); +}); + ipcRenderer.on('settings-update', (e, data) => { - if (data.enableSpellchecking) { - if (!spellchecker.isEnabled) { - spellchecker.enable(); - - // TODO: this does not work yet, needs more testing - // if (data.spellcheckingLanguage !== 'auto') { - // console.log('set spellchecking language to', data.spellcheckingLanguage); - // spellchecker.switchLanguage(data.spellcheckingLanguage); - // } - } + if (data.enableSpellchecking && !spellchecker.isInitialized) { + spellchecker.initialize(); + + contextMenuBuilder = new ContextMenuBuilder(spellchecker.handler, null, isDevMode); + + new ContextMenuListener((info) => { // eslint-disable-line + contextMenuBuilder.showPopupMenu(info); + }); } }); +// initSpellche + document.addEventListener('DOMContentLoaded', () => { ipcRenderer.sendToHost('hello'); }, false); diff --git a/src/webview/spellchecker.js b/src/webview/spellchecker.js index 5beb77e03..5704779d7 100644 --- a/src/webview/spellchecker.js +++ b/src/webview/spellchecker.js @@ -1,30 +1,42 @@ -import { SpellCheckHandler, ContextMenuListener, ContextMenuBuilder } from 'electron-spellchecker'; +import { SpellCheckHandler } from 'electron-spellchecker'; import { isMac } from '../environment'; export default class Spellchecker { - isEnabled = false; - spellchecker = null; + isInitialized = false; + handler = null; + initRetries = 0; + + initialize() { + this.handler = new SpellCheckHandler(); - enable() { - this.spellchecker = new SpellCheckHandler(); if (!isMac) { - this.spellchecker.attachToInput(); - this.spellchecker.switchLanguage(navigator.language); + this.attach(); + } else { + this.isInitialized = true; } + } - const contextMenuBuilder = new ContextMenuBuilder(this.spellchecker); + attach() { + let initFailed = false; - new ContextMenuListener((info) => { // eslint-disable-line - contextMenuBuilder.showPopupMenu(info); - }); - } + if (this.initRetries > 3) { + console.error('Could not initialize spellchecker'); + return; + } - // TODO: this does not work yet, needs more testing - // switchLanguage(language) { - // if (language !== 'auto') { - // this.spellchecker.switchLanguage(language); - // } - // } + try { + this.handler.attachToInput(); + this.handler.switchLanguage(navigator.language); + } catch (err) { + initFailed = true; + this.initRetries = +1; + setTimeout(() => { this.attach(); console.warn('Spellchecker init failed, trying again in 5s'); }, 5000); + } + + if (!initFailed) { + this.isInitialized = true; + } + } } -- cgit v1.2.3-54-g00ecf From fcbf398c9a5473cd20f6560c7012bb4dc43f90de Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 13 Dec 2017 11:18:13 +0100 Subject: fix mobx issue with settings model --- src/api/server/LocalApi.js | 4 +--- src/models/Settings.js | 6 +++++- src/stores/SettingsStore.js | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/api/server/LocalApi.js b/src/api/server/LocalApi.js index eba236f16..79ac6e12f 100644 --- a/src/api/server/LocalApi.js +++ b/src/api/server/LocalApi.js @@ -1,5 +1,3 @@ -import SettingsModel from '../../models/Settings'; - export default class LocalApi { // App async updateAppSettings(data) { @@ -15,7 +13,7 @@ export default class LocalApi { async getAppSettings() { const settingsString = localStorage.getItem('app'); try { - const settings = new SettingsModel(JSON.parse(settingsString) || {}); + const settings = JSON.parse(settingsString) || {}; console.debug('LocalApi::getAppSettings resolves', settings); return settings; diff --git a/src/models/Settings.js b/src/models/Settings.js index 35bfe0d05..ca44da258 100644 --- a/src/models/Settings.js +++ b/src/models/Settings.js @@ -1,4 +1,4 @@ -import { observable } from 'mobx'; +import { observable, extendObservable } from 'mobx'; import { DEFAULT_APP_SETTINGS } from '../config'; export default class Settings { @@ -17,4 +17,8 @@ export default class Settings { constructor(data) { Object.assign(this, data); } + + update(data) { + extendObservable(this, data); + } } diff --git a/src/stores/SettingsStore.js b/src/stores/SettingsStore.js index 33473f16d..da99a720f 100644 --- a/src/stores/SettingsStore.js +++ b/src/stores/SettingsStore.js @@ -26,7 +26,7 @@ export default class SettingsStore extends Store { } @computed get all() { - return this.allSettingsRequest.result || new SettingsModel(); + return new SettingsModel(this.allSettingsRequest.result); } @action async _update({ settings }) { -- cgit v1.2.3-54-g00ecf From fc49f232d9ac116f2a3bd7667fdd3e553df7516f Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 13 Dec 2017 11:20:13 +0100 Subject: override spellchecker attribute on inputs to enable/disable spellchecker --- src/webview/plugin.js | 15 +++++---------- src/webview/spellchecker.js | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/webview/plugin.js b/src/webview/plugin.js index 610dffc8e..cf38169d3 100644 --- a/src/webview/plugin.js +++ b/src/webview/plugin.js @@ -21,23 +21,18 @@ ipcRenderer.on('initializeRecipe', (e, data) => { } }); -let contextMenuBuilder = new ContextMenuBuilder(null, null, isDevMode); const spellchecker = new Spellchecker(); +spellchecker.initialize(); + +const contextMenuBuilder = new ContextMenuBuilder(spellchecker.handler, null, isDevMode); new ContextMenuListener((info) => { // eslint-disable-line contextMenuBuilder.showPopupMenu(info); }); ipcRenderer.on('settings-update', (e, data) => { - if (data.enableSpellchecking && !spellchecker.isInitialized) { - spellchecker.initialize(); - - contextMenuBuilder = new ContextMenuBuilder(spellchecker.handler, null, isDevMode); - - new ContextMenuListener((info) => { // eslint-disable-line - contextMenuBuilder.showPopupMenu(info); - }); - } + console.log('settings-update', data); + spellchecker.toggleSpellchecker(data.enableSpellchecking); }); // initSpellche diff --git a/src/webview/spellchecker.js b/src/webview/spellchecker.js index 5704779d7..a504a4039 100644 --- a/src/webview/spellchecker.js +++ b/src/webview/spellchecker.js @@ -6,6 +6,11 @@ export default class Spellchecker { isInitialized = false; handler = null; initRetries = 0; + DOMCheckInterval = null; + + get inputs() { + return document.querySelectorAll('input[type="text"], [contenteditable="true"], textarea'); + } initialize() { this.handler = new SpellCheckHandler(); @@ -38,5 +43,21 @@ export default class Spellchecker { this.isInitialized = true; } } + + toggleSpellchecker(enable = false) { + this.inputs.forEach((input) => { + input.setAttribute('spellcheck', enable); + }); + + this.intervalHandler(enable); + } + + intervalHandler(enable) { + clearInterval(this.DOMCheckInterval); + + if (enable) { + this.DOMCheckInterval = setInterval(() => this.toggleSpellchecker(enable), 30000); + } + } } -- cgit v1.2.3-54-g00ecf From 53fde0c7b31b5c79a81702c4338d1e9beb47e44f Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 13 Dec 2017 11:21:05 +0100 Subject: fix(Notification): Remove notification sound when app is muted --- src/stores/ServicesStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index b04aafd78..113df6d55 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js @@ -297,7 +297,7 @@ export default class ServicesStore extends Store { }); } else if (channel === 'notification') { const options = args[0].options; - if (service.recipe.hasNotificationSound || service.isMuted) { + if (service.recipe.hasNotificationSound || service.isMuted || this.stores.settings.all.isAppMuted) { Object.assign(options, { silent: true, }); -- cgit v1.2.3-54-g00ecf From 965fdf2581c7822e3d995be12a9315f8008d2e75 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 13 Dec 2017 12:28:43 +0100 Subject: fix(Spell checker): Fixed issues with spell checker --- src/components/settings/settings/EditSettingsForm.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index 878e46d6d..ff398aa33 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js @@ -64,10 +64,6 @@ const messages = defineMessages({ id: 'settings.app.currentVersion', defaultMessage: '!!!Current version:', }, - restartRequired: { - id: 'settings.app.restartRequired', - defaultMessage: '!!!Changes require restart', - }, }); @observer @@ -158,7 +154,6 @@ export default class EditSettingsForm extends Component { {/* Advanced */}

{intl.formatMessage(messages.headlineAdvanced)}

-

{intl.formatMessage(messages.restartRequired)}

{/* + )} {recipe.hasCustomUrl && ( -- cgit v1.2.3-54-g00ecf From 03610f2dd6833b3b6358790d044b852c154b4bf3 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Fri, 15 Dec 2017 14:00:17 +0100 Subject: feat(Recipes): Add `hasHostedOption` to enable hosted & self hosted services --- src/components/settings/services/EditServiceForm.js | 19 +++++++++++++++++-- src/containers/settings/EditServiceScreen.js | 11 +++++++++++ src/models/Recipe.js | 2 ++ src/styles/content-tabs.scss | 12 +++++++----- src/styles/input.scss | 4 ++++ 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js index a7d296460..f689dd5c4 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.js @@ -47,6 +47,10 @@ const messages = defineMessages({ id: 'settings.service.form.tabOnPremise', defaultMessage: '!!!Self hosted ⭐️', }, + useHostedService: { + id: 'settings.service.form.useHostedService', + defaultMessage: '!!!Use the hosted {name} service.', + }, customUrlValidationError: { id: 'settings.service.form.customUrlValidationError', defaultMessage: '!!!Could not validate custom {name} server.', @@ -108,7 +112,6 @@ export default class EditServiceForm extends Component { this.props.form.submit({ onSuccess: async (form) => { const values = form.values(); - let isValid = true; if (recipe.validateUrl && values.customUrl) { @@ -166,6 +169,13 @@ export default class EditServiceForm extends Component { /> ); + let activeTabIndex = 0; + if (recipe.hasHostedOption && service.team) { + activeTabIndex = 1; + } else if (recipe.hasHostedOption && service.customUrl) { + activeTabIndex = 2; + } + return (
@@ -198,8 +208,13 @@ export default class EditServiceForm extends Component { {(recipe.hasTeamId || recipe.hasCustomUrl) && ( + {recipe.hasHostedOption && ( + + {intl.formatMessage(messages.useHostedService, { name: recipe.name })} + + )} {recipe.hasTeamId && ( Date: Fri, 15 Dec 2017 14:02:37 +0100 Subject: Add missing translation strings --- src/i18n/locales/en-US.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 48b408e59..9efb36450 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -110,6 +110,7 @@ "settings.service.form.editServiceHeadline": "Edit {name}", "settings.service.form.tabHosted": "Hosted", "settings.service.form.tabOnPremise": "Self hosted ⭐️", + "settings.service.form.useHostedService": "Use the hosted {name} service.", "settings.service.form.customUrlValidationError": "Could not validate custom {name} server.", "settings.service.form.customUrlPremiumInfo": "To add self hosted services, you need a Franz Premium Supporter Account.", "settings.service.form.customUrlUpgradeAccount": "Upgrade your account", -- cgit v1.2.3-54-g00ecf From 5826dc36797b3d1d96ec986437003e5fc2626e91 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Fri, 15 Dec 2017 14:42:45 +0100 Subject: feat(Recipe): Add semver version validation --- src/models/Recipe.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/models/Recipe.js b/src/models/Recipe.js index 036d6d216..1fc23ac89 100644 --- a/src/models/Recipe.js +++ b/src/models/Recipe.js @@ -1,10 +1,11 @@ import emailParser from 'address-rfc2822'; +import semver from 'semver'; export default class Recipe { id = ''; name = ''; description = ''; - version = '1.0'; + version = ''; path = ''; serviceURL = ''; @@ -31,6 +32,10 @@ export default class Recipe { throw Error(`Recipe '${data.name}' requires Id`); } + if (!semver.valid(data.version)) { + throw Error(`Version ${data.version} of recipe '${data.name}' is not a valid semver version`); + } + this.id = data.id || this.id; this.name = data.name || this.name; this.rawAuthor = data.author || this.author; -- cgit v1.2.3-54-g00ecf