aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Stefan Malzner <stefan@adlk.io>2018-11-22 11:36:11 +0100
committerLibravatar Stefan Malzner <stefan@adlk.io>2018-11-22 11:36:11 +0100
commitfd7954fef99d59ca0aa9f2b468afea3463ef2202 (patch)
treedd83cc2efd0e8ec54679b4db80ed1b5dd086feb6 /src
parentFix isFullScreen typo (diff)
downloadferdium-app-fd7954fef99d59ca0aa9f2b468afea3463ef2202.tar.gz
ferdium-app-fd7954fef99d59ca0aa9f2b468afea3463ef2202.tar.zst
ferdium-app-fd7954fef99d59ca0aa9f2b468afea3463ef2202.zip
feat(App): Add option to enable dark mode for supported services
Diffstat (limited to 'src')
-rw-r--r--src/components/settings/services/EditServiceForm.js1
-rw-r--r--src/containers/settings/EditServiceScreen.js11
-rw-r--r--src/i18n/locales/en-US.json1
-rw-r--r--src/models/Recipe.js6
-rw-r--r--src/models/Service.js3
-rw-r--r--src/stores/ServicesStore.js8
-rw-r--r--src/webview/darkmode.js28
-rw-r--r--src/webview/plugin.js33
8 files changed, 88 insertions, 3 deletions
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js
index 7aa632f29..f9afe4c8d 100644
--- a/src/components/settings/services/EditServiceForm.js
+++ b/src/components/settings/services/EditServiceForm.js
@@ -304,6 +304,7 @@ export default @observer class EditServiceForm extends Component {
304 304
305 <div className="settings__settings-group"> 305 <div className="settings__settings-group">
306 <h3>{intl.formatMessage(messages.headlineGeneral)}</h3> 306 <h3>{intl.formatMessage(messages.headlineGeneral)}</h3>
307 <Toggle field={form.$('isDarkModeEnabled')} />
307 <Toggle field={form.$('isEnabled')} /> 308 <Toggle field={form.$('isEnabled')} />
308 </div> 309 </div>
309 </div> 310 </div>
diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js
index 1fc6bc85e..e69c2c2a8 100644
--- a/src/containers/settings/EditServiceScreen.js
+++ b/src/containers/settings/EditServiceScreen.js
@@ -6,6 +6,7 @@ import { defineMessages, intlShape } from 'react-intl';
6import UserStore from '../../stores/UserStore'; 6import UserStore from '../../stores/UserStore';
7import RecipesStore from '../../stores/RecipesStore'; 7import RecipesStore from '../../stores/RecipesStore';
8import ServicesStore from '../../stores/ServicesStore'; 8import ServicesStore from '../../stores/ServicesStore';
9import SettingsStore from '../../stores/SettingsStore';
9import Form from '../../lib/Form'; 10import Form from '../../lib/Form';
10import { gaPage } from '../../lib/analytics'; 11import { gaPage } from '../../lib/analytics';
11 12
@@ -50,6 +51,10 @@ const messages = defineMessages({
50 id: 'settings.service.form.icon', 51 id: 'settings.service.form.icon',
51 defaultMessage: '!!!Custom icon', 52 defaultMessage: '!!!Custom icon',
52 }, 53 },
54 enableDarkMode: {
55 id: 'settings.service.form.enableDarkMode',
56 defaultMessage: '!!!Enable Dark Mode',
57 },
53}); 58});
54 59
55export default @inject('stores', 'actions') @observer class EditServiceScreen extends Component { 60export default @inject('stores', 'actions') @observer class EditServiceScreen extends Component {
@@ -111,6 +116,11 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
111 default: null, 116 default: null,
112 type: 'file', 117 type: 'file',
113 }, 118 },
119 isDarkModeEnabled: {
120 label: intl.formatMessage(messages.enableDarkMode),
121 value: service.isDarkModeEnabled,
122 default: this.props.stores.settings.all.app.darkMode,
123 },
114 }, 124 },
115 }; 125 };
116 126
@@ -238,6 +248,7 @@ EditServiceScreen.wrappedComponent.propTypes = {
238 user: PropTypes.instanceOf(UserStore).isRequired, 248 user: PropTypes.instanceOf(UserStore).isRequired,
239 recipes: PropTypes.instanceOf(RecipesStore).isRequired, 249 recipes: PropTypes.instanceOf(RecipesStore).isRequired,
240 services: PropTypes.instanceOf(ServicesStore).isRequired, 250 services: PropTypes.instanceOf(ServicesStore).isRequired,
251 settings: PropTypes.instanceOf(SettingsStore).isRequired,
241 }).isRequired, 252 }).isRequired,
242 router: PropTypes.shape({ 253 router: PropTypes.shape({
243 params: PropTypes.shape({ 254 params: PropTypes.shape({
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 8b2f763b5..66ec5af84 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -130,6 +130,7 @@
130 "settings.service.form.icon": "Custom icon", 130 "settings.service.form.icon": "Custom icon",
131 "settings.service.form.iconDelete": "Delete", 131 "settings.service.form.iconDelete": "Delete",
132 "settings.service.form.iconUpload": "Drop your image, or click here", 132 "settings.service.form.iconUpload": "Drop your image, or click here",
133 "settings.service.form.enableDarkMode": "Enable Dark Mode",
133 "settings.service.error.headline": "Error", 134 "settings.service.error.headline": "Error",
134 "settings.service.error.goBack": "Back to services", 135 "settings.service.error.goBack": "Back to services",
135 "settings.service.error.message": "Could not load service recipe.", 136 "settings.service.error.message": "Could not load service recipe.",
diff --git a/src/models/Recipe.js b/src/models/Recipe.js
index 032a9aa19..43c44514c 100644
--- a/src/models/Recipe.js
+++ b/src/models/Recipe.js
@@ -1,5 +1,7 @@
1import emailParser from 'address-rfc2822'; 1import emailParser from 'address-rfc2822';
2import semver from 'semver'; 2import semver from 'semver';
3import fs from 'fs-extra';
4import path from 'path';
3 5
4export default class Recipe { 6export default class Recipe {
5 id = ''; 7 id = '';
@@ -73,4 +75,8 @@ export default class Recipe {
73 75
74 return []; 76 return [];
75 } 77 }
78
79 get hasDarkMode() {
80 return fs.pathExistsSync(path.join(this.path, 'darkmode.css'));
81 }
76} 82}
diff --git a/src/models/Service.js b/src/models/Service.js
index 4f8767dbe..1bab8bd68 100644
--- a/src/models/Service.js
+++ b/src/models/Service.js
@@ -28,6 +28,7 @@ export default class Service {
28 @observable iconUrl = ''; 28 @observable iconUrl = '';
29 @observable hasCustomUploadedIcon = false; 29 @observable hasCustomUploadedIcon = false;
30 @observable hasCrashed = false; 30 @observable hasCrashed = false;
31 @observable isDarkModeEnabled = false;
31 32
32 constructor(data, recipe) { 33 constructor(data, recipe) {
33 if (!data) { 34 if (!data) {
@@ -64,6 +65,8 @@ export default class Service {
64 65
65 this.isMuted = data.isMuted !== undefined ? data.isMuted : this.isMuted; 66 this.isMuted = data.isMuted !== undefined ? data.isMuted : this.isMuted;
66 67
68 this.isDarkModeEnabled = data.isDarkModeEnabled !== undefined ? data.isDarkModeEnabled : this.isDarkModeEnabled;
69
67 this.hasCustomUploadedIcon = data.hasCustomIcon !== undefined ? data.hasCustomIcon : this.hasCustomUploadedIcon; 70 this.hasCustomUploadedIcon = data.hasCustomIcon !== undefined ? data.hasCustomIcon : this.hasCustomUploadedIcon;
68 71
69 this.recipe = recipe; 72 this.recipe = recipe;
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index c5822968a..cdb2db142 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -214,6 +214,14 @@ export default class ServicesStore extends Store {
214 await request._promise; 214 await request._promise;
215 this.actionStatus = request.result.status; 215 this.actionStatus = request.result.status;
216 216
217 if (service.isEnabled) {
218 this._sendIPCMessage({
219 serviceId,
220 channel: 'service-settings-update',
221 args: newData,
222 });
223 }
224
217 if (redirect) { 225 if (redirect) {
218 this.stores.router.push('/settings/services'); 226 this.stores.router.push('/settings/services');
219 gaEvent('Service', 'update', service.recipe.id); 227 gaEvent('Service', 'update', service.recipe.id);
diff --git a/src/webview/darkmode.js b/src/webview/darkmode.js
new file mode 100644
index 000000000..9830ef33c
--- /dev/null
+++ b/src/webview/darkmode.js
@@ -0,0 +1,28 @@
1import path from 'path';
2import fs from 'fs-extra';
3
4const ID = 'franz-theme-dark-mode';
5
6export function injectDarkModeStyle(recipePath) {
7 const darkModeStyle = path.join(recipePath, 'darkmode.css');
8 if (fs.pathExistsSync(darkModeStyle)) {
9 const data = fs.readFileSync(darkModeStyle);
10 const styles = document.createElement('style');
11 styles.id = ID;
12 styles.innerHTML = data.toString();
13
14 document.querySelector('head').appendChild(styles);
15 }
16}
17
18export function removeDarkModeStyle() {
19 const style = document.querySelector(`#${ID}`);
20
21 if (style) {
22 style.remove();
23 }
24}
25
26export function isDarkModeStyleInjected() {
27 return !!document.querySelector(`#${ID}`);
28}
diff --git a/src/webview/plugin.js b/src/webview/plugin.js
index ee8cedbab..e6fdc4efd 100644
--- a/src/webview/plugin.js
+++ b/src/webview/plugin.js
@@ -6,10 +6,14 @@ import { isDevMode } from '../environment';
6import RecipeWebview from './lib/RecipeWebview'; 6import RecipeWebview from './lib/RecipeWebview';
7 7
8import Spellchecker from './spellchecker'; 8import Spellchecker from './spellchecker';
9import { injectDarkModeStyle, isDarkModeStyleInjected, removeDarkModeStyle } from './darkmode';
9import './notifications'; 10import './notifications';
10 11
11const debug = require('debug')('Franz:Plugin'); 12const debug = require('debug')('Franz:Plugin');
12 13
14window.franzSettings = {};
15let serviceData;
16
13ipcRenderer.on('initializeRecipe', (e, data) => { 17ipcRenderer.on('initializeRecipe', (e, data) => {
14 const modulePath = path.join(data.recipe.path, 'webview.js'); 18 const modulePath = path.join(data.recipe.path, 'webview.js');
15 // Delete module from cache 19 // Delete module from cache
@@ -17,7 +21,14 @@ ipcRenderer.on('initializeRecipe', (e, data) => {
17 try { 21 try {
18 // eslint-disable-next-line 22 // eslint-disable-next-line
19 require(modulePath)(new RecipeWebview(), data); 23 require(modulePath)(new RecipeWebview(), data);
20 debug('Initialize Recipe'); 24 debug('Initialize Recipe', data);
25
26 serviceData = data;
27
28 if (data.isDarkModeEnabled) {
29 injectDarkModeStyle(data.recipe.path);
30 debug('Add dark theme styles');
31 }
21 } catch (err) { 32 } catch (err) {
22 debug('Recipe initialization failed', err); 33 debug('Recipe initialization failed', err);
23 } 34 }
@@ -33,11 +44,27 @@ new ContextMenuListener((info) => { // eslint-disable-line
33}); 44});
34 45
35ipcRenderer.on('settings-update', (e, data) => { 46ipcRenderer.on('settings-update', (e, data) => {
36 spellchecker.toggleSpellchecker(data.enableSpellchecking);
37 debug('Settings update received', data); 47 debug('Settings update received', data);
48
49 spellchecker.toggleSpellchecker(data.enableSpellchecking);
50 window.franzSettings = data;
51});
52
53ipcRenderer.on('service-settings-update', (e, data) => {
54 debug('Service settings update received', data);
55
56 if (data.isDarkModeEnabled && !isDarkModeStyleInjected()) {
57 injectDarkModeStyle(serviceData.recipe.path);
58
59 debug('Enable service dark mode');
60 } else if (!data.isDarkModeEnabled && isDarkModeStyleInjected()) {
61 removeDarkModeStyle();
62
63 debug('Disable service dark mode');
64 }
38}); 65});
39 66
40// initSpellche 67// initSpellchecker
41 68
42document.addEventListener('DOMContentLoaded', () => { 69document.addEventListener('DOMContentLoaded', () => {
43 ipcRenderer.sendToHost('hello'); 70 ipcRenderer.sendToHost('hello');