aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Stefan Malzner <stefan@adlk.io>2018-11-22 14:14:25 +0100
committerLibravatar Stefan Malzner <stefan@adlk.io>2018-11-22 14:14:25 +0100
commit46b8c8c4b3a5b80e0187b284abc84566a7e784db (patch)
tree7fd378bcdd18e78c42dfeb61a15f89fd10106046 /src
parentADD features loading spinner (diff)
parentfeat(App): Add option to enable dark mode for supported services (diff)
downloadferdium-app-46b8c8c4b3a5b80e0187b284abc84566a7e784db.tar.gz
ferdium-app-46b8c8c4b3a5b80e0187b284abc84566a7e784db.tar.zst
ferdium-app-46b8c8c4b3a5b80e0187b284abc84566a7e784db.zip
Merge branch 'develop' into feature/features-api
Diffstat (limited to 'src')
-rw-r--r--src/I18n.js12
-rw-r--r--src/actions/settings.js4
-rw-r--r--src/api/FeaturesApi.js4
-rw-r--r--src/api/LocalApi.js8
-rw-r--r--src/api/server/LocalApi.js27
-rw-r--r--src/api/server/ServerApi.js68
-rw-r--r--src/app.js2
-rw-r--r--src/components/auth/AuthLayout.js97
-rw-r--r--src/components/auth/Import.js3
-rw-r--r--src/components/auth/Invite.js3
-rw-r--r--src/components/auth/Login.js3
-rw-r--r--src/components/auth/Password.js3
-rw-r--r--src/components/auth/Pricing.js3
-rw-r--r--src/components/auth/Signup.js7
-rw-r--r--src/components/auth/Welcome.js3
-rw-r--r--src/components/layout/AppLayout.js131
-rw-r--r--src/components/layout/Sidebar.js3
-rw-r--r--src/components/services/content/ServiceDisabled.js3
-rw-r--r--src/components/services/content/ServiceWebview.js3
-rw-r--r--src/components/services/content/Services.js3
-rw-r--r--src/components/services/content/WebviewCrashHandler.js3
-rw-r--r--src/components/services/tabs/TabItem.js18
-rw-r--r--src/components/services/tabs/Tabbar.js3
-rw-r--r--src/components/settings/SettingsLayout.js3
-rw-r--r--src/components/settings/account/AccountDashboard.js3
-rw-r--r--src/components/settings/recipes/RecipeItem.js3
-rw-r--r--src/components/settings/recipes/RecipesDashboard.js3
-rw-r--r--src/components/settings/services/EditServiceForm.js7
-rw-r--r--src/components/settings/services/ServiceError.js3
-rw-r--r--src/components/settings/services/ServiceItem.js3
-rw-r--r--src/components/settings/services/ServicesDashboard.js3
-rw-r--r--src/components/settings/settings/EditSettingsForm.js10
-rw-r--r--src/components/settings/user/EditUserForm.js3
-rw-r--r--src/components/subscription/SubscriptionForm.js3
-rw-r--r--src/components/subscription/SubscriptionPopup.js3
-rw-r--r--src/components/ui/Button.js3
-rw-r--r--src/components/ui/ImageUpload.js4
-rw-r--r--src/components/ui/InfoBar.js3
-rw-r--r--src/components/ui/Infobox.js3
-rw-r--r--src/components/ui/Input.js3
-rw-r--r--src/components/ui/Link.js4
-rw-r--r--src/components/ui/Radio.js3
-rw-r--r--src/components/ui/SearchInput.js3
-rw-r--r--src/components/ui/Select.js3
-rw-r--r--src/components/ui/StatusBarTargetUrl.js3
-rw-r--r--src/components/ui/Tabs/Tabs.js3
-rw-r--r--src/components/ui/Toggle.js3
-rw-r--r--src/config.js17
-rw-r--r--src/containers/auth/AuthLayoutContainer.js19
-rw-r--r--src/containers/auth/ImportScreen.js3
-rw-r--r--src/containers/auth/InviteScreen.js3
-rw-r--r--src/containers/auth/LoginScreen.js3
-rw-r--r--src/containers/auth/PasswordScreen.js3
-rw-r--r--src/containers/auth/PricingScreen.js3
-rw-r--r--src/containers/auth/SignupScreen.js3
-rw-r--r--src/containers/auth/WelcomeScreen.js3
-rw-r--r--src/containers/layout/AppLayoutContainer.js13
-rw-r--r--src/containers/settings/AccountScreen.js3
-rw-r--r--src/containers/settings/EditServiceScreen.js14
-rw-r--r--src/containers/settings/EditSettingsScreen.js65
-rw-r--r--src/containers/settings/EditUserScreen.js3
-rw-r--r--src/containers/settings/InviteScreen.js3
-rw-r--r--src/containers/settings/RecipesScreen.js3
-rw-r--r--src/containers/settings/ServicesScreen.js3
-rw-r--r--src/containers/settings/SettingsWindow.js3
-rw-r--r--src/containers/subscription/SubscriptionFormScreen.js3
-rw-r--r--src/containers/subscription/SubscriptionPopupScreen.js3
-rw-r--r--src/electron/Settings.js45
-rw-r--r--src/electron/ipc-api/settings.js6
-rw-r--r--src/environment.js12
-rw-r--r--src/helpers/validation-helpers.js35
-rw-r--r--src/i18n/locales/ca.json48
-rw-r--r--src/i18n/locales/cs.json48
-rw-r--r--src/i18n/locales/de.json54
-rw-r--r--src/i18n/locales/el.json48
-rw-r--r--src/i18n/locales/en-US.json52
-rw-r--r--src/i18n/locales/es.json60
-rw-r--r--src/i18n/locales/fr.json98
-rw-r--r--src/i18n/locales/ga.json50
-rw-r--r--src/i18n/locales/hr.json70
-rw-r--r--src/i18n/locales/hu.json48
-rw-r--r--src/i18n/locales/id.json48
-rw-r--r--src/i18n/locales/it.json66
-rw-r--r--src/i18n/locales/ja.json48
-rw-r--r--src/i18n/locales/ka.json48
-rw-r--r--src/i18n/locales/nl-BE.json48
-rw-r--r--src/i18n/locales/nl.json50
-rw-r--r--src/i18n/locales/pl.json80
-rw-r--r--src/i18n/locales/pt-BR.json60
-rw-r--r--src/i18n/locales/pt.json216
-rw-r--r--src/i18n/locales/ru.json62
-rw-r--r--src/i18n/locales/sk.json48
-rw-r--r--src/i18n/locales/sr.json48
-rw-r--r--src/i18n/locales/tr.json58
-rw-r--r--src/i18n/locales/uk.json74
-rw-r--r--src/i18n/locales/zh-TW.json52
-rw-r--r--src/index.html13
-rw-r--r--src/index.js35
-rw-r--r--src/lib/Menu.js598
-rw-r--r--src/lib/analytics.js6
-rw-r--r--src/models/News.js8
-rw-r--r--src/models/Plan.js4
-rw-r--r--src/models/Recipe.js14
-rw-r--r--src/models/RecipePreview.js8
-rw-r--r--src/models/Service.js5
-rw-r--r--src/models/Settings.js34
-rw-r--r--src/models/User.js19
-rw-r--r--src/stores/AppStore.js72
-rw-r--r--src/stores/FeaturesStore.js6
-rw-r--r--src/stores/RecipesStore.js8
-rw-r--r--src/stores/RequestStore.js4
-rw-r--r--src/stores/ServicesStore.js58
-rw-r--r--src/stores/SettingsStore.js121
-rw-r--r--src/stores/UIStore.js4
-rw-r--r--src/stores/UserStore.js24
-rw-r--r--src/styles/animations.scss54
-rw-r--r--src/styles/auth.scss121
-rw-r--r--src/styles/badge.scss19
-rw-r--r--src/styles/button.scss97
-rw-r--r--src/styles/colors.scss48
-rw-r--r--src/styles/content-tabs.scss34
-rw-r--r--src/styles/image-upload.scss93
-rw-r--r--src/styles/info-bar.scss49
-rw-r--r--src/styles/infobox.scss34
-rw-r--r--src/styles/input.scss95
-rw-r--r--src/styles/invite.scss11
-rw-r--r--src/styles/layout.scss160
-rw-r--r--src/styles/main.scss2
-rw-r--r--src/styles/mixins.scss2
-rw-r--r--src/styles/radio.scss35
-rw-r--r--src/styles/recipes.scss56
-rw-r--r--src/styles/reset.scss67
-rw-r--r--src/styles/searchInput.scss30
-rw-r--r--src/styles/select.scss30
-rw-r--r--src/styles/service-table.scss39
-rw-r--r--src/styles/services.scss60
-rw-r--r--src/styles/settings.scss354
-rw-r--r--src/styles/status-bar-target-url.scss14
-rw-r--r--src/styles/subscription-popup.scss13
-rw-r--r--src/styles/subscription.scss56
-rw-r--r--src/styles/tabs.scss98
-rw-r--r--src/styles/title-bar.scss80
-rw-r--r--src/styles/toggle.scss72
-rw-r--r--src/styles/tooltip.scss2
-rw-r--r--src/styles/type.scss48
-rw-r--r--src/styles/util.scss10
-rw-r--r--src/styles/welcome.scss138
-rw-r--r--src/webview/darkmode.js28
-rw-r--r--src/webview/plugin.js38
149 files changed, 3753 insertions, 1727 deletions
diff --git a/src/I18n.js b/src/I18n.js
index ae3ba2fa9..e33141576 100644
--- a/src/I18n.js
+++ b/src/I18n.js
@@ -7,13 +7,19 @@ import { oneOrManyChildElements } from './prop-types';
7import translations from './i18n/translations'; 7import translations from './i18n/translations';
8import UserStore from './stores/UserStore'; 8import UserStore from './stores/UserStore';
9 9
10@inject('stores') @observer 10export default @inject('stores') @observer class I18N extends Component {
11export default class I18N extends Component { 11 componentDidUpdate() {
12 window.franz.menu.rebuild();
13 }
14
12 render() { 15 render() {
13 const { stores, children } = this.props; 16 const { stores, children } = this.props;
14 const { locale } = stores.app; 17 const { locale } = stores.app;
15 return ( 18 return (
16 <IntlProvider {...{ locale, key: locale, messages: translations[locale] }}> 19 <IntlProvider
20 {...{ locale, key: locale, messages: translations[locale] }}
21 ref={(intlProvider) => { window.franz.intl = intlProvider ? intlProvider.getChildContext().intl : null; }}
22 >
17 {children} 23 {children}
18 </IntlProvider> 24 </IntlProvider>
19 ); 25 );
diff --git a/src/actions/settings.js b/src/actions/settings.js
index 3d53cd674..fd29b798b 100644
--- a/src/actions/settings.js
+++ b/src/actions/settings.js
@@ -2,9 +2,11 @@ import PropTypes from 'prop-types';
2 2
3export default { 3export default {
4 update: { 4 update: {
5 settings: PropTypes.object.isRequired, 5 type: PropTypes.string.isRequired,
6 data: PropTypes.object.isRequired,
6 }, 7 },
7 remove: { 8 remove: {
9 type: PropTypes.string.isRequired,
8 key: PropTypes.string.isRequired, 10 key: PropTypes.string.isRequired,
9 }, 11 },
10}; 12};
diff --git a/src/api/FeaturesApi.js b/src/api/FeaturesApi.js
index 292dc80cc..c66f28f5b 100644
--- a/src/api/FeaturesApi.js
+++ b/src/api/FeaturesApi.js
@@ -3,8 +3,8 @@ export default class FeaturesApi {
3 this.server = server; 3 this.server = server;
4 } 4 }
5 5
6 base() { 6 default() {
7 return this.server.getBaseFeatures(); 7 return this.server.getDefaultFeatures();
8 } 8 }
9 9
10 features() { 10 features() {
diff --git a/src/api/LocalApi.js b/src/api/LocalApi.js
index 59d7d8fa2..741917104 100644
--- a/src/api/LocalApi.js
+++ b/src/api/LocalApi.js
@@ -4,6 +4,14 @@ export default class LocalApi {
4 this.local = local; 4 this.local = local;
5 } 5 }
6 6
7 getAppSettings() {
8 return this.local.getAppSettings();
9 }
10
11 updateAppSettings(data) {
12 return this.local.updateAppSettings(data);
13 }
14
7 getAppCacheSize() { 15 getAppCacheSize() {
8 return this.local.getAppCacheSize(); 16 return this.local.getAppCacheSize();
9 } 17 }
diff --git a/src/api/server/LocalApi.js b/src/api/server/LocalApi.js
index aa3a7d655..4814bba66 100644
--- a/src/api/server/LocalApi.js
+++ b/src/api/server/LocalApi.js
@@ -1,11 +1,30 @@
1import { remote } from 'electron'; 1import { ipcRenderer, remote } from 'electron';
2import du from 'du'; 2import du from 'du';
3 3
4import { getServicePartitionsDirectory } from '../../helpers/service-helpers.js'; 4import { getServicePartitionsDirectory } from '../../helpers/service-helpers.js';
5 5
6const debug = require('debug')('Franz:LocalApi');
7
6const { session } = remote; 8const { session } = remote;
7 9
8export default class LocalApi { 10export default class LocalApi {
11 // Settings
12 getAppSettings() {
13 return new Promise((resolve) => {
14 ipcRenderer.once('appSettings', (event, data) => {
15 debug('LocalApi::getAppSettings resolves', data);
16 resolve(data);
17 });
18
19 ipcRenderer.send('getAppSettings');
20 });
21 }
22
23 async updateAppSettings(data) {
24 debug('LocalApi::updateAppSettings resolves', data);
25 ipcRenderer.send('updateAppSettings', data);
26 }
27
9 // Services 28 // Services
10 async getAppCacheSize() { 29 async getAppCacheSize() {
11 const partitionsDir = getServicePartitionsDirectory(); 30 const partitionsDir = getServicePartitionsDirectory();
@@ -13,7 +32,7 @@ export default class LocalApi {
13 du(partitionsDir, (err, size) => { 32 du(partitionsDir, (err, size) => {
14 if (err) reject(err); 33 if (err) reject(err);
15 34
16 console.debug('LocalApi::getAppCacheSize resolves', size); 35 debug('LocalApi::getAppCacheSize resolves', size);
17 resolve(size); 36 resolve(size);
18 }); 37 });
19 }); 38 });
@@ -22,14 +41,14 @@ export default class LocalApi {
22 async clearCache(serviceId) { 41 async clearCache(serviceId) {
23 const s = session.fromPartition(`persist:service-${serviceId}`); 42 const s = session.fromPartition(`persist:service-${serviceId}`);
24 43
25 console.debug('LocalApi::clearCache resolves', serviceId); 44 debug('LocalApi::clearCache resolves', serviceId);
26 return new Promise(resolve => s.clearCache(resolve)); 45 return new Promise(resolve => s.clearCache(resolve));
27 } 46 }
28 47
29 async clearAppCache() { 48 async clearAppCache() {
30 const s = session.defaultSession; 49 const s = session.defaultSession;
31 50
32 console.debug('LocalApi::clearCache clearAppCache'); 51 debug('LocalApi::clearCache clearAppCache');
33 return new Promise(resolve => s.clearCache(resolve)); 52 return new Promise(resolve => s.clearCache(resolve));
34 } 53 }
35} 54}
diff --git a/src/api/server/ServerApi.js b/src/api/server/ServerApi.js
index ad7988bb2..164419951 100644
--- a/src/api/server/ServerApi.js
+++ b/src/api/server/ServerApi.js
@@ -27,13 +27,15 @@ import {
27 removeServicePartitionDirectory, 27 removeServicePartitionDirectory,
28} from '../../helpers/service-helpers.js'; 28} from '../../helpers/service-helpers.js';
29 29
30const debug = require('debug')('Franz:ServerApi');
31
30module.paths.unshift( 32module.paths.unshift(
31 getDevRecipeDirectory(), 33 getDevRecipeDirectory(),
32 getRecipeDirectory(), 34 getRecipeDirectory(),
33); 35);
34 36
35const { app } = remote; 37const { app } = remote;
36const fetch = remote.require('electron-fetch'); 38const { default: fetch } = remote.require('electron-fetch');
37 39
38const SERVER_URL = API; 40const SERVER_URL = API;
39const API_VERSION = 'v1'; 41const API_VERSION = 'v1';
@@ -55,7 +57,7 @@ export default class ServerApi {
55 } 57 }
56 const u = await request.json(); 58 const u = await request.json();
57 59
58 console.debug('ServerApi::login resolves', u); 60 debug('ServerApi::login resolves', u);
59 return u.token; 61 return u.token;
60 } 62 }
61 63
@@ -69,7 +71,7 @@ export default class ServerApi {
69 } 71 }
70 const u = await request.json(); 72 const u = await request.json();
71 73
72 console.debug('ServerApi::signup resolves', u); 74 debug('ServerApi::signup resolves', u);
73 return u.token; 75 return u.token;
74 } 76 }
75 77
@@ -82,7 +84,7 @@ export default class ServerApi {
82 throw request; 84 throw request;
83 } 85 }
84 86
85 console.debug('ServerApi::inviteUser'); 87 debug('ServerApi::inviteUser');
86 return true; 88 return true;
87 } 89 }
88 90
@@ -98,7 +100,7 @@ export default class ServerApi {
98 } 100 }
99 const r = await request.json(); 101 const r = await request.json();
100 102
101 console.debug('ServerApi::retrievePassword'); 103 debug('ServerApi::retrievePassword');
102 return r; 104 return r;
103 } 105 }
104 106
@@ -112,7 +114,7 @@ export default class ServerApi {
112 const data = await request.json(); 114 const data = await request.json();
113 115
114 const user = new UserModel(data); 116 const user = new UserModel(data);
115 console.debug('ServerApi::userInfo resolves', user); 117 debug('ServerApi::userInfo resolves', user);
116 118
117 return user; 119 return user;
118 } 120 }
@@ -128,7 +130,7 @@ export default class ServerApi {
128 const updatedData = await request.json(); 130 const updatedData = await request.json();
129 131
130 const user = Object.assign(updatedData, { data: new UserModel(updatedData.data) }); 132 const user = Object.assign(updatedData, { data: new UserModel(updatedData.data) });
131 console.debug('ServerApi::updateUserInfo resolves', user); 133 debug('ServerApi::updateUserInfo resolves', user);
132 return user; 134 return user;
133 } 135 }
134 136
@@ -141,7 +143,7 @@ export default class ServerApi {
141 } 143 }
142 const data = await request.json(); 144 const data = await request.json();
143 145
144 console.debug('ServerApi::deleteAccount resolves', data); 146 debug('ServerApi::deleteAccount resolves', data);
145 return data; 147 return data;
146 } 148 }
147 149
@@ -157,7 +159,7 @@ export default class ServerApi {
157 159
158 let services = await this._mapServiceModels(data); 160 let services = await this._mapServiceModels(data);
159 services = services.filter(service => service !== null); 161 services = services.filter(service => service !== null);
160 console.debug('ServerApi::getServices resolves', services); 162 debug('ServerApi::getServices resolves', services);
161 return services; 163 return services;
162 } 164 }
163 165
@@ -181,7 +183,7 @@ export default class ServerApi {
181 183
182 const service = Object.assign(serviceData, { data: await this._prepareServiceModel(serviceData.data) }); 184 const service = Object.assign(serviceData, { data: await this._prepareServiceModel(serviceData.data) });
183 185
184 console.debug('ServerApi::createService resolves', service); 186 debug('ServerApi::createService resolves', service);
185 return service; 187 return service;
186 } 188 }
187 189
@@ -205,7 +207,7 @@ export default class ServerApi {
205 207
206 const service = Object.assign(serviceData, { data: await this._prepareServiceModel(serviceData.data) }); 208 const service = Object.assign(serviceData, { data: await this._prepareServiceModel(serviceData.data) });
207 209
208 console.debug('ServerApi::updateService resolves', service); 210 debug('ServerApi::updateService resolves', service);
209 return service; 211 return service;
210 } 212 }
211 213
@@ -240,7 +242,7 @@ export default class ServerApi {
240 throw request; 242 throw request;
241 } 243 }
242 const serviceData = await request.json(); 244 const serviceData = await request.json();
243 console.debug('ServerApi::reorderService resolves', serviceData); 245 debug('ServerApi::reorderService resolves', serviceData);
244 return serviceData; 246 return serviceData;
245 } 247 }
246 248
@@ -255,13 +257,13 @@ export default class ServerApi {
255 257
256 removeServicePartitionDirectory(id, true); 258 removeServicePartitionDirectory(id, true);
257 259
258 console.debug('ServerApi::deleteService resolves', data); 260 debug('ServerApi::deleteService resolves', data);
259 return data; 261 return data;
260 } 262 }
261 263
262 // Features 264 // Features
263 async getBaseFeatures() { 265 async getDefaultFeatures() {
264 const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/features/base`, this._prepareAuthRequest({ 266 const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/features/default`, this._prepareAuthRequest({
265 method: 'GET', 267 method: 'GET',
266 })); 268 }));
267 if (!request.ok) { 269 if (!request.ok) {
@@ -270,7 +272,7 @@ export default class ServerApi {
270 const data = await request.json(); 272 const data = await request.json();
271 273
272 const features = data; 274 const features = data;
273 console.debug('ServerApi::getBaseFeatures resolves', features); 275 console.debug('ServerApi::getDefaultFeatures resolves', features);
274 return features; 276 return features;
275 } 277 }
276 278
@@ -306,7 +308,7 @@ export default class ServerApi {
306 308
307 this.recipes = this.recipes.concat(this._getDevRecipes()); 309 this.recipes = this.recipes.concat(this._getDevRecipes());
308 310
309 console.debug('StubServerApi::getInstalledRecipes resolves', this.recipes); 311 debug('StubServerApi::getInstalledRecipes resolves', this.recipes);
310 return this.recipes; 312 return this.recipes;
311 } 313 }
312 314
@@ -319,7 +321,7 @@ export default class ServerApi {
319 throw request; 321 throw request;
320 } 322 }
321 const recipes = await request.json(); 323 const recipes = await request.json();
322 console.debug('ServerApi::getRecipeUpdates resolves', recipes); 324 debug('ServerApi::getRecipeUpdates resolves', recipes);
323 return recipes; 325 return recipes;
324 } 326 }
325 327
@@ -334,7 +336,7 @@ export default class ServerApi {
334 const data = await request.json(); 336 const data = await request.json();
335 337
336 const recipePreviews = this._mapRecipePreviewModel(data); 338 const recipePreviews = this._mapRecipePreviewModel(data);
337 console.debug('ServerApi::getRecipes resolves', recipePreviews); 339 debug('ServerApi::getRecipes resolves', recipePreviews);
338 340
339 return recipePreviews; 341 return recipePreviews;
340 } 342 }
@@ -351,7 +353,7 @@ export default class ServerApi {
351 // data = this._addLocalRecipesToPreviews(data); 353 // data = this._addLocalRecipesToPreviews(data);
352 354
353 const recipePreviews = this._mapRecipePreviewModel(data); 355 const recipePreviews = this._mapRecipePreviewModel(data);
354 console.debug('ServerApi::getFeaturedRecipes resolves', recipePreviews); 356 debug('ServerApi::getFeaturedRecipes resolves', recipePreviews);
355 return recipePreviews; 357 return recipePreviews;
356 } 358 }
357 359
@@ -365,7 +367,7 @@ export default class ServerApi {
365 const data = await request.json(); 367 const data = await request.json();
366 368
367 const recipePreviews = this._mapRecipePreviewModel(data); 369 const recipePreviews = this._mapRecipePreviewModel(data);
368 console.debug('ServerApi::searchRecipePreviews resolves', recipePreviews); 370 debug('ServerApi::searchRecipePreviews resolves', recipePreviews);
369 return recipePreviews; 371 return recipePreviews;
370 } 372 }
371 373
@@ -379,7 +381,7 @@ export default class ServerApi {
379 381
380 fs.ensureDirSync(recipeTempDirectory); 382 fs.ensureDirSync(recipeTempDirectory);
381 const res = await fetch(packageUrl); 383 const res = await fetch(packageUrl);
382 console.debug('Recipe downloaded', recipeId); 384 debug('Recipe downloaded', recipeId);
383 const buffer = await res.buffer(); 385 const buffer = await res.buffer();
384 fs.writeFileSync(archivePath, buffer); 386 fs.writeFileSync(archivePath, buffer);
385 387
@@ -421,7 +423,7 @@ export default class ServerApi {
421 const data = await request.json(); 423 const data = await request.json();
422 424
423 const plan = new PlanModel(data); 425 const plan = new PlanModel(data);
424 console.debug('ServerApi::getPlans resolves', plan); 426 debug('ServerApi::getPlans resolves', plan);
425 return plan; 427 return plan;
426 } 428 }
427 429
@@ -437,7 +439,7 @@ export default class ServerApi {
437 } 439 }
438 const data = await request.json(); 440 const data = await request.json();
439 441
440 console.debug('ServerApi::getHostedPage resolves', data); 442 debug('ServerApi::getHostedPage resolves', data);
441 return data; 443 return data;
442 } 444 }
443 445
@@ -450,7 +452,7 @@ export default class ServerApi {
450 } 452 }
451 const data = await request.json(); 453 const data = await request.json();
452 454
453 console.debug('ServerApi::getPaymentDashboardUrl resolves', data); 455 debug('ServerApi::getPaymentDashboardUrl resolves', data);
454 return data; 456 return data;
455 } 457 }
456 458
@@ -463,7 +465,7 @@ export default class ServerApi {
463 } 465 }
464 const data = await request.json(); 466 const data = await request.json();
465 const orders = this._mapOrderModels(data); 467 const orders = this._mapOrderModels(data);
466 console.debug('ServerApi::getSubscriptionOrders resolves', orders); 468 debug('ServerApi::getSubscriptionOrders resolves', orders);
467 return orders; 469 return orders;
468 } 470 }
469 471
@@ -480,7 +482,7 @@ export default class ServerApi {
480 } 482 }
481 const data = await request.json(); 483 const data = await request.json();
482 const news = this._mapNewsModels(data); 484 const news = this._mapNewsModels(data);
483 console.debug('ServerApi::getLatestNews resolves', news); 485 debug('ServerApi::getLatestNews resolves', news);
484 return news; 486 return news;
485 } 487 }
486 488
@@ -494,7 +496,7 @@ export default class ServerApi {
494 throw request; 496 throw request;
495 } 497 }
496 498
497 console.debug('ServerApi::hideNews resolves', id); 499 debug('ServerApi::hideNews resolves', id);
498 } 500 }
499 501
500 // Health Check 502 // Health Check
@@ -505,7 +507,7 @@ export default class ServerApi {
505 if (!request.ok) { 507 if (!request.ok) {
506 throw request; 508 throw request;
507 } 509 }
508 console.debug('ServerApi::healthCheck resolves'); 510 debug('ServerApi::healthCheck resolves');
509 } 511 }
510 512
511 async getLegacyServices() { 513 async getLegacyServices() {
@@ -531,7 +533,7 @@ export default class ServerApi {
531 return service; 533 return service;
532 })); 534 }));
533 535
534 console.debug('ServerApi::getLegacyServices resolves', services); 536 debug('ServerApi::getLegacyServices resolves', services);
535 return services; 537 return services;
536 } 538 }
537 } catch (err) { 539 } catch (err) {
@@ -564,7 +566,7 @@ export default class ServerApi {
564 566
565 return new ServiceModel(service, recipe); 567 return new ServiceModel(service, recipe);
566 } catch (e) { 568 } catch (e) {
567 console.debug(e); 569 debug(e);
568 return null; 570 return null;
569 } 571 }
570 } 572 }
@@ -582,7 +584,7 @@ export default class ServerApi {
582 584
583 await this.getRecipePackage(recipeId); 585 await this.getRecipePackage(recipeId);
584 586
585 console.debug('Rerun ServerAPI::getInstalledRecipes'); 587 debug('Rerun ServerAPI::getInstalledRecipes');
586 await this.getInstalledRecipes(); 588 await this.getInstalledRecipes();
587 589
588 recipe = this.recipes.find(r => r.id === recipeId); 590 recipe = this.recipes.find(r => r.id === recipeId);
@@ -682,7 +684,7 @@ export default class ServerApi {
682 684
683 return recipes; 685 return recipes;
684 } catch (err) { 686 } catch (err) {
685 console.debug('Could not load dev recipes'); 687 debug('Could not load dev recipes');
686 return false; 688 return false;
687 } 689 }
688 } 690 }
diff --git a/src/app.js b/src/app.js
index 814bfacf1..43d0cf018 100644
--- a/src/app.js
+++ b/src/app.js
@@ -6,7 +6,7 @@ import { Provider } from 'mobx-react';
6import { syncHistoryWithStore, RouterStore } from 'mobx-react-router'; 6import { syncHistoryWithStore, RouterStore } from 'mobx-react-router';
7import { Router, Route, hashHistory, IndexRedirect } from 'react-router'; 7import { Router, Route, hashHistory, IndexRedirect } from 'react-router';
8 8
9import 'babel-polyfill'; 9import '@babel/polyfill';
10import smoothScroll from 'smoothscroll-polyfill'; 10import smoothScroll from 'smoothscroll-polyfill';
11 11
12import ServerApi from './api/server/ServerApi'; 12import ServerApi from './api/server/ServerApi';
diff --git a/src/components/auth/AuthLayout.js b/src/components/auth/AuthLayout.js
index 2741b8a15..4fb0e6a59 100644
--- a/src/components/auth/AuthLayout.js
+++ b/src/components/auth/AuthLayout.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import { RouteTransition } from 'react-router-transition'; 4import { RouteTransition } from 'react-router-transition';
5import { intlShape } from 'react-intl'; 5import { intlShape } from 'react-intl';
6import { TitleBar } from 'electron-react-titlebar';
6 7
7import Link from '../ui/Link'; 8import Link from '../ui/Link';
8import InfoBar from '../ui/InfoBar'; 9import InfoBar from '../ui/InfoBar';
@@ -10,8 +11,9 @@ import InfoBar from '../ui/InfoBar';
10import { oneOrManyChildElements, globalError as globalErrorPropType } from '../../prop-types'; 11import { oneOrManyChildElements, globalError as globalErrorPropType } from '../../prop-types';
11import globalMessages from '../../i18n/globalMessages'; 12import globalMessages from '../../i18n/globalMessages';
12 13
13@observer 14import { isWindows } from '../../environment';
14export default class AuthLayout extends Component { 15
16export default @observer class AuthLayout extends Component {
15 static propTypes = { 17 static propTypes = {
16 children: oneOrManyChildElements.isRequired, 18 children: oneOrManyChildElements.isRequired,
17 pathname: PropTypes.string.isRequired, 19 pathname: PropTypes.string.isRequired,
@@ -20,6 +22,8 @@ export default class AuthLayout extends Component {
20 isAPIHealthy: PropTypes.bool.isRequired, 22 isAPIHealthy: PropTypes.bool.isRequired,
21 retryHealthCheck: PropTypes.func.isRequired, 23 retryHealthCheck: PropTypes.func.isRequired,
22 isHealthCheckLoading: PropTypes.bool.isRequired, 24 isHealthCheckLoading: PropTypes.bool.isRequired,
25 isFullScreen: PropTypes.bool.isRequired,
26 darkMode: PropTypes.bool.isRequired,
23 }; 27 };
24 28
25 static contextTypes = { 29 static contextTypes = {
@@ -35,53 +39,58 @@ export default class AuthLayout extends Component {
35 isAPIHealthy, 39 isAPIHealthy,
36 retryHealthCheck, 40 retryHealthCheck,
37 isHealthCheckLoading, 41 isHealthCheckLoading,
42 isFullScreen,
43 darkMode,
38 } = this.props; 44 } = this.props;
39 const { intl } = this.context; 45 const { intl } = this.context;
40 46
41 return ( 47 return (
42 <div className="auth"> 48 <div className={darkMode ? 'theme__dark' : ''}>
43 {!isOnline && ( 49 {isWindows && !isFullScreen && <TitleBar menu={window.franz.menu.template} icon={'assets/images/logo.svg'} />}
44 <InfoBar 50 <div className={'auth'}>
45 type="warning" 51 {!isOnline && (
46 > 52 <InfoBar
47 <span className="mdi mdi-flash" /> 53 type="warning"
48 {intl.formatMessage(globalMessages.notConnectedToTheInternet)} 54 >
49 </InfoBar> 55 <span className="mdi mdi-flash" />
50 )} 56 {intl.formatMessage(globalMessages.notConnectedToTheInternet)}
51 {isOnline && !isAPIHealthy && ( 57 </InfoBar>
52 <InfoBar 58 )}
53 type="danger" 59 {isOnline && !isAPIHealthy && (
54 ctaLabel="Try again" 60 <InfoBar
55 ctaLoading={isHealthCheckLoading} 61 type="danger"
56 sticky 62 ctaLabel="Try again"
57 onClick={retryHealthCheck} 63 ctaLoading={isHealthCheckLoading}
58 > 64 sticky
59 <span className="mdi mdi-flash" /> 65 onClick={retryHealthCheck}
60 {intl.formatMessage(globalMessages.APIUnhealthy)} 66 >
61 </InfoBar> 67 <span className="mdi mdi-flash" />
62 )} 68 {intl.formatMessage(globalMessages.APIUnhealthy)}
63 <div className="auth__layout"> 69 </InfoBar>
64 <RouteTransition 70 )}
65 pathname={pathname} 71 <div className="auth__layout">
66 atEnter={{ opacity: 0 }} 72 <RouteTransition
67 atLeave={{ opacity: 0 }} 73 pathname={pathname}
68 atActive={{ opacity: 1 }} 74 atEnter={{ opacity: 0 }}
69 mapStyles={styles => ({ 75 atLeave={{ opacity: 0 }}
70 transform: `translateX(${styles.translateX}%)`, 76 atActive={{ opacity: 1 }}
71 opacity: styles.opacity, 77 mapStyles={styles => ({
72 })} 78 transform: `translateX(${styles.translateX}%)`,
73 component="span" 79 opacity: styles.opacity,
74 > 80 })}
75 {/* Inject globalError into children */} 81 component="span"
76 {React.cloneElement(children, { 82 >
77 error, 83 {/* Inject globalError into children */}
78 })} 84 {React.cloneElement(children, {
79 </RouteTransition> 85 error,
86 })}
87 </RouteTransition>
88 </div>
89 {/* </div> */}
90 <Link to="https://adlk.io" className="auth__adlk" target="_blank">
91 <img src="./assets/images/adlk.svg" alt="" />
92 </Link>
80 </div> 93 </div>
81 {/* </div> */}
82 <Link to="https://adlk.io" className="auth__adlk" target="_blank">
83 <img src="./assets/images/adlk.svg" alt="" />
84 </Link>
85 </div> 94 </div>
86 ); 95 );
87 } 96 }
diff --git a/src/components/auth/Import.js b/src/components/auth/Import.js
index 9ba14e768..0d5feb274 100644
--- a/src/components/auth/Import.js
+++ b/src/components/auth/Import.js
@@ -28,8 +28,7 @@ const messages = defineMessages({
28 }, 28 },
29}); 29});
30 30
31@observer 31export default @observer class Import extends Component {
32export default class Import extends Component {
33 static propTypes = { 32 static propTypes = {
34 services: MobxPropTypes.arrayOrObservableArray.isRequired, 33 services: MobxPropTypes.arrayOrObservableArray.isRequired,
35 onSubmit: PropTypes.func.isRequired, 34 onSubmit: PropTypes.func.isRequired,
diff --git a/src/components/auth/Invite.js b/src/components/auth/Invite.js
index f1c16986b..96821a61a 100644
--- a/src/components/auth/Invite.js
+++ b/src/components/auth/Invite.js
@@ -43,8 +43,7 @@ const messages = defineMessages({
43 }, 43 },
44}); 44});
45 45
46@observer 46export default @observer class Invite extends Component {
47export default class Invite extends Component {
48 static propTypes = { 47 static propTypes = {
49 onSubmit: PropTypes.func.isRequired, 48 onSubmit: PropTypes.func.isRequired,
50 embed: PropTypes.bool, 49 embed: PropTypes.bool,
diff --git a/src/components/auth/Login.js b/src/components/auth/Login.js
index 4a3cd6776..f465b35a5 100644
--- a/src/components/auth/Login.js
+++ b/src/components/auth/Login.js
@@ -55,8 +55,7 @@ const messages = defineMessages({
55 }, 55 },
56}); 56});
57 57
58@observer 58export default @observer class Login extends Component {
59export default class Login extends Component {
60 static propTypes = { 59 static propTypes = {
61 onSubmit: PropTypes.func.isRequired, 60 onSubmit: PropTypes.func.isRequired,
62 isSubmitting: PropTypes.bool.isRequired, 61 isSubmitting: PropTypes.bool.isRequired,
diff --git a/src/components/auth/Password.js b/src/components/auth/Password.js
index 5bcc80b6e..ad34e39af 100644
--- a/src/components/auth/Password.js
+++ b/src/components/auth/Password.js
@@ -41,8 +41,7 @@ const messages = defineMessages({
41 }, 41 },
42}); 42});
43 43
44@observer 44export default @observer class Password extends Component {
45export default class Password extends Component {
46 static propTypes = { 45 static propTypes = {
47 onSubmit: PropTypes.func.isRequired, 46 onSubmit: PropTypes.func.isRequired,
48 isSubmitting: PropTypes.bool.isRequired, 47 isSubmitting: PropTypes.bool.isRequired,
diff --git a/src/components/auth/Pricing.js b/src/components/auth/Pricing.js
index 3cc8d5f6b..f08129568 100644
--- a/src/components/auth/Pricing.js
+++ b/src/components/auth/Pricing.js
@@ -28,8 +28,7 @@ const messages = defineMessages({
28 }, 28 },
29}); 29});
30 30
31@observer 31export default @observer class Signup extends Component {
32export default class Signup extends Component {
33 static propTypes = { 32 static propTypes = {
34 donor: MobxPropTypes.objectOrObservableObject.isRequired, 33 donor: MobxPropTypes.objectOrObservableObject.isRequired,
35 isLoading: PropTypes.bool.isRequired, 34 isLoading: PropTypes.bool.isRequired,
diff --git a/src/components/auth/Signup.js b/src/components/auth/Signup.js
index 219948274..bbcad8b67 100644
--- a/src/components/auth/Signup.js
+++ b/src/components/auth/Signup.js
@@ -65,8 +65,7 @@ const messages = defineMessages({
65 }, 65 },
66}); 66});
67 67
68@observer 68export default @observer class Signup extends Component {
69export default class Signup extends Component {
70 static propTypes = { 69 static propTypes = {
71 onSubmit: PropTypes.func.isRequired, 70 onSubmit: PropTypes.func.isRequired,
72 isSubmitting: PropTypes.bool.isRequired, 71 isSubmitting: PropTypes.bool.isRequired,
@@ -187,7 +186,7 @@ export default class Signup extends Component {
187 {intl.formatMessage(messages.legalInfo)} 186 {intl.formatMessage(messages.legalInfo)}
188 <br /> 187 <br />
189 <Link 188 <Link
190 to="http://meetfranz.com/terms" 189 to="https://meetfranz.com/terms"
191 target="_blank" 190 target="_blank"
192 className="link" 191 className="link"
193 > 192 >
@@ -195,7 +194,7 @@ export default class Signup extends Component {
195 </Link> 194 </Link>
196 &nbsp;&amp;&nbsp; 195 &nbsp;&amp;&nbsp;
197 <Link 196 <Link
198 to="http://meetfranz.com/privacy" 197 to="https://meetfranz.com/privacy"
199 target="_blank" 198 target="_blank"
200 className="link" 199 className="link"
201 > 200 >
diff --git a/src/components/auth/Welcome.js b/src/components/auth/Welcome.js
index 9e1c762a5..f6d77f70f 100644
--- a/src/components/auth/Welcome.js
+++ b/src/components/auth/Welcome.js
@@ -16,8 +16,7 @@ const messages = defineMessages({
16 }, 16 },
17}); 17});
18 18
19@observer 19export default @observer class Login extends Component {
20export default class Login extends Component {
21 static propTypes = { 20 static propTypes = {
22 loginRoute: PropTypes.string.isRequired, 21 loginRoute: PropTypes.string.isRequired,
23 signupRoute: PropTypes.string.isRequired, 22 signupRoute: PropTypes.string.isRequired,
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js
index 20dc2f764..a4003ef8e 100644
--- a/src/components/layout/AppLayout.js
+++ b/src/components/layout/AppLayout.js
@@ -2,10 +2,13 @@ import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; 3import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 4import { defineMessages, intlShape } from 'react-intl';
5import { TitleBar } from 'electron-react-titlebar';
5 6
6import InfoBar from '../ui/InfoBar'; 7import InfoBar from '../ui/InfoBar';
7import globalMessages from '../../i18n/globalMessages'; 8import globalMessages from '../../i18n/globalMessages';
8 9
10import { isWindows } from '../../environment';
11
9function createMarkup(HTMLString) { 12function createMarkup(HTMLString) {
10 return { __html: HTMLString }; 13 return { __html: HTMLString };
11} 14}
@@ -37,9 +40,9 @@ const messages = defineMessages({
37 }, 40 },
38}); 41});
39 42
40@observer 43export default @observer class AppLayout extends Component {
41export default class AppLayout extends Component {
42 static propTypes = { 44 static propTypes = {
45 isFullScreen: PropTypes.bool.isRequired,
43 sidebar: PropTypes.element.isRequired, 46 sidebar: PropTypes.element.isRequired,
44 services: PropTypes.element.isRequired, 47 services: PropTypes.element.isRequired,
45 children: PropTypes.element, 48 children: PropTypes.element,
@@ -54,6 +57,7 @@ export default class AppLayout extends Component {
54 areRequiredRequestsSuccessful: PropTypes.bool.isRequired, 57 areRequiredRequestsSuccessful: PropTypes.bool.isRequired,
55 retryRequiredRequests: PropTypes.func.isRequired, 58 retryRequiredRequests: PropTypes.func.isRequired,
56 areRequiredRequestsLoading: PropTypes.bool.isRequired, 59 areRequiredRequestsLoading: PropTypes.bool.isRequired,
60 darkMode: PropTypes.bool.isRequired,
57 }; 61 };
58 62
59 static defaultProps = { 63 static defaultProps = {
@@ -66,6 +70,7 @@ export default class AppLayout extends Component {
66 70
67 render() { 71 render() {
68 const { 72 const {
73 isFullScreen,
69 sidebar, 74 sidebar,
70 services, 75 services,
71 children, 76 children,
@@ -80,71 +85,75 @@ export default class AppLayout extends Component {
80 areRequiredRequestsSuccessful, 85 areRequiredRequestsSuccessful,
81 retryRequiredRequests, 86 retryRequiredRequests,
82 areRequiredRequestsLoading, 87 areRequiredRequestsLoading,
88 darkMode,
83 } = this.props; 89 } = this.props;
84 90
85 const { intl } = this.context; 91 const { intl } = this.context;
86 92
87 return ( 93 return (
88 <div> 94 <div className={(darkMode ? 'theme__dark' : '')}>
89 <div className="app"> 95 <div className="app">
90 {sidebar} 96 {isWindows && !isFullScreen && <TitleBar menu={window.franz.menu.template} icon={'assets/images/logo.svg'} />}
91 <div className="app__service"> 97 <div className="app__content">
92 {news.length > 0 && news.map(item => ( 98 {sidebar}
93 <InfoBar 99 <div className="app__service">
94 key={item.id} 100 {news.length > 0 && news.map(item => (
95 position="top" 101 <InfoBar
96 type={item.type} 102 key={item.id}
97 sticky={item.sticky} 103 position="top"
98 onHide={() => removeNewsItem({ newsId: item.id })} 104 type={item.type}
99 > 105 sticky={item.sticky}
100 <span dangerouslySetInnerHTML={createMarkup(item.message)} /> 106 onHide={() => removeNewsItem({ newsId: item.id })}
101 </InfoBar> 107 >
102 ))} 108 <span dangerouslySetInnerHTML={createMarkup(item.message)} />
103 {!isOnline && ( 109 </InfoBar>
104 <InfoBar 110 ))}
105 type="danger" 111 {!isOnline && (
106 > 112 <InfoBar
107 <span className="mdi mdi-flash" /> 113 type="danger"
108 {intl.formatMessage(globalMessages.notConnectedToTheInternet)} 114 >
109 </InfoBar> 115 <span className="mdi mdi-flash" />
110 )} 116 {intl.formatMessage(globalMessages.notConnectedToTheInternet)}
111 {!areRequiredRequestsSuccessful && showRequiredRequestsError && ( 117 </InfoBar>
112 <InfoBar 118 )}
113 type="danger" 119 {!areRequiredRequestsSuccessful && showRequiredRequestsError && (
114 ctaLabel="Try again" 120 <InfoBar
115 ctaLoading={areRequiredRequestsLoading} 121 type="danger"
116 sticky 122 ctaLabel="Try again"
117 onClick={retryRequiredRequests} 123 ctaLoading={areRequiredRequestsLoading}
118 > 124 sticky
119 <span className="mdi mdi-flash" /> 125 onClick={retryRequiredRequests}
120 {intl.formatMessage(messages.requiredRequestsFailed)} 126 >
121 </InfoBar> 127 <span className="mdi mdi-flash" />
122 )} 128 {intl.formatMessage(messages.requiredRequestsFailed)}
123 {showServicesUpdatedInfoBar && ( 129 </InfoBar>
124 <InfoBar 130 )}
125 type="primary" 131 {showServicesUpdatedInfoBar && (
126 ctaLabel={intl.formatMessage(messages.buttonReloadServices)} 132 <InfoBar
127 onClick={reloadServicesAfterUpdate} 133 type="primary"
128 sticky 134 ctaLabel={intl.formatMessage(messages.buttonReloadServices)}
129 > 135 onClick={reloadServicesAfterUpdate}
130 <span className="mdi mdi-power-plug" /> 136 sticky
131 {intl.formatMessage(messages.servicesUpdated)} 137 >
132 </InfoBar> 138 <span className="mdi mdi-power-plug" />
133 )} 139 {intl.formatMessage(messages.servicesUpdated)}
134 {appUpdateIsDownloaded && ( 140 </InfoBar>
135 <InfoBar 141 )}
136 type="primary" 142 {appUpdateIsDownloaded && (
137 ctaLabel={intl.formatMessage(messages.buttonInstallUpdate)} 143 <InfoBar
138 onClick={installAppUpdate} 144 type="primary"
139 sticky 145 ctaLabel={intl.formatMessage(messages.buttonInstallUpdate)}
140 > 146 onClick={installAppUpdate}
141 <span className="mdi mdi-information" /> 147 sticky
142 {intl.formatMessage(messages.updateAvailable)} <a href="https://meetfranz.com/changelog" target="_blank"> 148 >
143 <u>{intl.formatMessage(messages.changelog)}</u> 149 <span className="mdi mdi-information" />
144 </a> 150 {intl.formatMessage(messages.updateAvailable)} <a href="https://meetfranz.com/changelog" target="_blank">
145 </InfoBar> 151 <u>{intl.formatMessage(messages.changelog)}</u>
146 )} 152 </a>
147 {services} 153 </InfoBar>
154 )}
155 {services}
156 </div>
148 </div> 157 </div>
149 </div> 158 </div>
150 {children} 159 {children}
diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js
index fa269f216..6ea95bf88 100644
--- a/src/components/layout/Sidebar.js
+++ b/src/components/layout/Sidebar.js
@@ -26,8 +26,7 @@ const messages = defineMessages({
26 }, 26 },
27}); 27});
28 28
29@observer 29export default @observer class Sidebar extends Component {
30export default class Sidebar extends Component {
31 static propTypes = { 30 static propTypes = {
32 openSettings: PropTypes.func.isRequired, 31 openSettings: PropTypes.func.isRequired,
33 toggleMuteApp: PropTypes.func.isRequired, 32 toggleMuteApp: PropTypes.func.isRequired,
diff --git a/src/components/services/content/ServiceDisabled.js b/src/components/services/content/ServiceDisabled.js
index b5af3743d..58fb38d8c 100644
--- a/src/components/services/content/ServiceDisabled.js
+++ b/src/components/services/content/ServiceDisabled.js
@@ -16,8 +16,7 @@ const messages = defineMessages({
16 }, 16 },
17}); 17});
18 18
19@observer 19export default @observer class ServiceDisabled extends Component {
20export default class ServiceDisabled extends Component {
21 static propTypes = { 20 static propTypes = {
22 name: PropTypes.string.isRequired, 21 name: PropTypes.string.isRequired,
23 enable: PropTypes.func.isRequired, 22 enable: PropTypes.func.isRequired,
diff --git a/src/components/services/content/ServiceWebview.js b/src/components/services/content/ServiceWebview.js
index c146abf4e..7163209ee 100644
--- a/src/components/services/content/ServiceWebview.js
+++ b/src/components/services/content/ServiceWebview.js
@@ -10,8 +10,7 @@ import StatusBarTargetUrl from '../../ui/StatusBarTargetUrl';
10import WebviewCrashHandler from './WebviewCrashHandler'; 10import WebviewCrashHandler from './WebviewCrashHandler';
11import ServiceDisabled from './ServiceDisabled'; 11import ServiceDisabled from './ServiceDisabled';
12 12
13@observer 13export default @observer class ServiceWebview extends Component {
14export default class ServiceWebview extends Component {
15 static propTypes = { 14 static propTypes = {
16 service: PropTypes.instanceOf(ServiceModel).isRequired, 15 service: PropTypes.instanceOf(ServiceModel).isRequired,
17 setWebviewReference: PropTypes.func.isRequired, 16 setWebviewReference: PropTypes.func.isRequired,
diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js
index b1322afc2..4cbd51043 100644
--- a/src/components/services/content/Services.js
+++ b/src/components/services/content/Services.js
@@ -18,8 +18,7 @@ const messages = defineMessages({
18 }, 18 },
19}); 19});
20 20
21@observer 21export default @observer class Services extends Component {
22export default class Services extends Component {
23 static propTypes = { 22 static propTypes = {
24 services: MobxPropTypes.arrayOrObservableArray.isRequired, 23 services: MobxPropTypes.arrayOrObservableArray.isRequired,
25 setWebviewReference: PropTypes.func.isRequired, 24 setWebviewReference: PropTypes.func.isRequired,
diff --git a/src/components/services/content/WebviewCrashHandler.js b/src/components/services/content/WebviewCrashHandler.js
index d3e6951f3..3be1fccf4 100644
--- a/src/components/services/content/WebviewCrashHandler.js
+++ b/src/components/services/content/WebviewCrashHandler.js
@@ -24,8 +24,7 @@ const messages = defineMessages({
24 }, 24 },
25}); 25});
26 26
27@observer 27export default @observer class WebviewCrashHandler extends Component {
28export default class WebviewCrashHandler extends Component {
29 static propTypes = { 28 static propTypes = {
30 name: PropTypes.string.isRequired, 29 name: PropTypes.string.isRequired,
31 reload: PropTypes.func.isRequired, 30 reload: PropTypes.func.isRequired,
diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js
index 7aed8fda7..8de7dc438 100644
--- a/src/components/services/tabs/TabItem.js
+++ b/src/components/services/tabs/TabItem.js
@@ -7,7 +7,7 @@ import classnames from 'classnames';
7import { SortableElement } from 'react-sortable-hoc'; 7import { SortableElement } from 'react-sortable-hoc';
8 8
9import ServiceModel from '../../../models/Service'; 9import ServiceModel from '../../../models/Service';
10import { ctrlKey } from '../../../environment'; 10import { isDevMode, ctrlKey } from '../../../environment';
11 11
12const { Menu } = remote; 12const { Menu } = remote;
13 13
@@ -119,10 +119,14 @@ class TabItem extends Component {
119 click: () => (service.isEnabled ? disableService() : enableService()), 119 click: () => (service.isEnabled ? disableService() : enableService()),
120 }, { 120 }, {
121 type: 'separator', 121 type: 'separator',
122 }, {
123 label: intl.formatMessage(messages.deleteService),
124 click: () => deleteService(),
125 }]; 122 }];
123
124 if (isDevMode) {
125 menuTemplate.push({
126 label: intl.formatMessage(messages.deleteService),
127 click: () => deleteService(),
128 });
129 }
126 const menu = Menu.buildFromTemplate(menuTemplate); 130 const menu = Menu.buildFromTemplate(menuTemplate);
127 131
128 let notificationBadge = null; 132 let notificationBadge = null;
@@ -137,10 +141,10 @@ class TabItem extends Component {
137 {service.unreadIndirectMessageCount > 0 141 {service.unreadIndirectMessageCount > 0
138 && service.unreadDirectMessageCount === 0 142 && service.unreadDirectMessageCount === 0
139 && service.isIndirectMessageBadgeEnabled && ( 143 && service.isIndirectMessageBadgeEnabled && (
140 <span className="tab-item__message-count is-indirect"> 144 <span className="tab-item__message-count is-indirect">
141 • 145 •
142 </span> 146 </span>
143 )} 147 )}
144 </span> 148 </span>
145 ); 149 );
146 } 150 }
diff --git a/src/components/services/tabs/Tabbar.js b/src/components/services/tabs/Tabbar.js
index ceb88c51c..dd5c2140f 100644
--- a/src/components/services/tabs/Tabbar.js
+++ b/src/components/services/tabs/Tabbar.js
@@ -4,8 +4,7 @@ import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
4 4
5import TabBarSortableList from './TabBarSortableList'; 5import TabBarSortableList from './TabBarSortableList';
6 6
7@observer 7export default @observer class TabBar extends Component {
8export default class TabBar extends Component {
9 static propTypes = { 8 static propTypes = {
10 services: MobxPropTypes.arrayOrObservableArray.isRequired, 9 services: MobxPropTypes.arrayOrObservableArray.isRequired,
11 setActive: PropTypes.func.isRequired, 10 setActive: PropTypes.func.isRequired,
diff --git a/src/components/settings/SettingsLayout.js b/src/components/settings/SettingsLayout.js
index d5392ddba..3cb08feb1 100644
--- a/src/components/settings/SettingsLayout.js
+++ b/src/components/settings/SettingsLayout.js
@@ -5,8 +5,7 @@ import { observer } from 'mobx-react';
5import { oneOrManyChildElements } from '../../prop-types'; 5import { oneOrManyChildElements } from '../../prop-types';
6import Appear from '../ui/effects/Appear'; 6import Appear from '../ui/effects/Appear';
7 7
8@observer 8export default @observer class SettingsLayout extends Component {
9export default class SettingsLayout extends Component {
10 static propTypes = { 9 static propTypes = {
11 navigation: PropTypes.element.isRequired, 10 navigation: PropTypes.element.isRequired,
12 children: oneOrManyChildElements.isRequired, 11 children: oneOrManyChildElements.isRequired,
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js
index e6ccdaac7..ede519fd6 100644
--- a/src/components/settings/account/AccountDashboard.js
+++ b/src/components/settings/account/AccountDashboard.js
@@ -78,8 +78,7 @@ const messages = defineMessages({
78 }, 78 },
79}); 79});
80 80
81@observer 81export default @observer class AccountDashboard extends Component {
82export default class AccountDashboard extends Component {
83 static propTypes = { 82 static propTypes = {
84 user: MobxPropTypes.observableObject.isRequired, 83 user: MobxPropTypes.observableObject.isRequired,
85 orders: MobxPropTypes.arrayOrObservableArray.isRequired, 84 orders: MobxPropTypes.arrayOrObservableArray.isRequired,
diff --git a/src/components/settings/recipes/RecipeItem.js b/src/components/settings/recipes/RecipeItem.js
index 7b2f64d26..dae8891b3 100644
--- a/src/components/settings/recipes/RecipeItem.js
+++ b/src/components/settings/recipes/RecipeItem.js
@@ -4,8 +4,7 @@ import { observer } from 'mobx-react';
4 4
5import RecipePreviewModel from '../../../models/RecipePreview'; 5import RecipePreviewModel from '../../../models/RecipePreview';
6 6
7@observer 7export default @observer class RecipeItem extends Component {
8export default class RecipeItem extends Component {
9 static propTypes = { 8 static propTypes = {
10 recipe: PropTypes.instanceOf(RecipePreviewModel).isRequired, 9 recipe: PropTypes.instanceOf(RecipePreviewModel).isRequired,
11 onClick: PropTypes.func.isRequired, 10 onClick: PropTypes.func.isRequired,
diff --git a/src/components/settings/recipes/RecipesDashboard.js b/src/components/settings/recipes/RecipesDashboard.js
index 4610c69a5..cd783200f 100644
--- a/src/components/settings/recipes/RecipesDashboard.js
+++ b/src/components/settings/recipes/RecipesDashboard.js
@@ -46,8 +46,7 @@ const messages = defineMessages({
46 }, 46 },
47}); 47});
48 48
49@observer 49export default @observer class RecipesDashboard extends Component {
50export default class RecipesDashboard extends Component {
51 static propTypes = { 50 static propTypes = {
52 recipes: MobxPropTypes.arrayOrObservableArray.isRequired, 51 recipes: MobxPropTypes.arrayOrObservableArray.isRequired,
53 isLoading: PropTypes.bool.isRequired, 52 isLoading: PropTypes.bool.isRequired,
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js
index 3ffca99a7..777a95fcf 100644
--- a/src/components/settings/services/EditServiceForm.js
+++ b/src/components/settings/services/EditServiceForm.js
@@ -94,8 +94,7 @@ const messages = defineMessages({
94 }, 94 },
95}); 95});
96 96
97@observer 97export default @observer class EditServiceForm extends Component {
98export default class EditServiceForm extends Component {
99 static propTypes = { 98 static propTypes = {
100 recipe: PropTypes.instanceOf(Recipe).isRequired, 99 recipe: PropTypes.instanceOf(Recipe).isRequired,
101 service(props, propName) { 100 service(props, propName) {
@@ -204,6 +203,8 @@ export default class EditServiceForm extends Component {
204 activeTabIndex = 2; 203 activeTabIndex = 2;
205 } 204 }
206 205
206 const requiresUserInput = !recipe.hasHostedOption && (recipe.hasTeamId || recipe.hasCustomUrl);
207
207 return ( 208 return (
208 <div className="settings__main"> 209 <div className="settings__main">
209 <div className="settings__header"> 210 <div className="settings__header">
@@ -305,6 +306,7 @@ export default class EditServiceForm extends Component {
305 306
306 <div className="settings__settings-group"> 307 <div className="settings__settings-group">
307 <h3>{intl.formatMessage(messages.headlineGeneral)}</h3> 308 <h3>{intl.formatMessage(messages.headlineGeneral)}</h3>
309 <Toggle field={form.$('isDarkModeEnabled')} />
308 <Toggle field={form.$('isEnabled')} /> 310 <Toggle field={form.$('isEnabled')} />
309 </div> 311 </div>
310 </div> 312 </div>
@@ -342,6 +344,7 @@ export default class EditServiceForm extends Component {
342 type="submit" 344 type="submit"
343 label={intl.formatMessage(messages.saveService)} 345 label={intl.formatMessage(messages.saveService)}
344 htmlForm="form" 346 htmlForm="form"
347 disabled={action !== 'edit' && form.isPristine && requiresUserInput}
345 /> 348 />
346 )} 349 )}
347 </div> 350 </div>
diff --git a/src/components/settings/services/ServiceError.js b/src/components/settings/services/ServiceError.js
index 1f1512927..3cfc080d6 100644
--- a/src/components/settings/services/ServiceError.js
+++ b/src/components/settings/services/ServiceError.js
@@ -25,8 +25,7 @@ const messages = defineMessages({
25 }, 25 },
26}); 26});
27 27
28@observer 28export default @observer class ServiceError extends Component {
29export default class ServiceError extends Component {
30 static contextTypes = { 29 static contextTypes = {
31 intl: intlShape, 30 intl: intlShape,
32 }; 31 };
diff --git a/src/components/settings/services/ServiceItem.js b/src/components/settings/services/ServiceItem.js
index 9743315b0..84080519b 100644
--- a/src/components/settings/services/ServiceItem.js
+++ b/src/components/settings/services/ServiceItem.js
@@ -22,8 +22,7 @@ const messages = defineMessages({
22 }, 22 },
23}); 23});
24 24
25@observer 25export default @observer class ServiceItem extends Component {
26export default class ServiceItem extends Component {
27 static propTypes = { 26 static propTypes = {
28 service: PropTypes.instanceOf(ServiceModel).isRequired, 27 service: PropTypes.instanceOf(ServiceModel).isRequired,
29 goToServiceForm: PropTypes.func.isRequired, 28 goToServiceForm: PropTypes.func.isRequired,
diff --git a/src/components/settings/services/ServicesDashboard.js b/src/components/settings/services/ServicesDashboard.js
index 20e451f01..e7dfaf106 100644
--- a/src/components/settings/services/ServicesDashboard.js
+++ b/src/components/settings/services/ServicesDashboard.js
@@ -49,8 +49,7 @@ const messages = defineMessages({
49 }, 49 },
50}); 50});
51 51
52@observer 52export default @observer class ServicesDashboard extends Component {
53export default class ServicesDashboard extends Component {
54 static propTypes = { 53 static propTypes = {
55 services: MobxPropTypes.arrayOrObservableArray.isRequired, 54 services: MobxPropTypes.arrayOrObservableArray.isRequired,
56 isLoading: PropTypes.bool.isRequired, 55 isLoading: PropTypes.bool.isRequired,
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js
index 72aa5a8af..b5c048ebd 100644
--- a/src/components/settings/settings/EditSettingsForm.js
+++ b/src/components/settings/settings/EditSettingsForm.js
@@ -76,10 +76,13 @@ const messages = defineMessages({
76 id: 'settings.app.currentVersion', 76 id: 'settings.app.currentVersion',
77 defaultMessage: '!!!Current version:', 77 defaultMessage: '!!!Current version:',
78 }, 78 },
79 enableGPUAccelerationInfo: {
80 id: 'settings.app.restartRequired',
81 defaultMessage: '!!!Changes require restart',
82 },
79}); 83});
80 84
81@observer 85export default @observer class EditSettingsForm extends Component {
82export default class EditSettingsForm extends Component {
83 static propTypes = { 86 static propTypes = {
84 checkForUpdates: PropTypes.func.isRequired, 87 checkForUpdates: PropTypes.func.isRequired,
85 installUpdate: PropTypes.func.isRequired, 88 installUpdate: PropTypes.func.isRequired,
@@ -157,6 +160,7 @@ export default class EditSettingsForm extends Component {
157 <h2 id="apperance">{intl.formatMessage(messages.headlineAppearance)}</h2> 160 <h2 id="apperance">{intl.formatMessage(messages.headlineAppearance)}</h2>
158 <Toggle field={form.$('showDisabledServices')} /> 161 <Toggle field={form.$('showDisabledServices')} />
159 <Toggle field={form.$('showMessageBadgeWhenMuted')} /> 162 <Toggle field={form.$('showMessageBadgeWhenMuted')} />
163 <Toggle field={form.$('darkMode')} />
160 164
161 {/* Language */} 165 {/* Language */}
162 <h2 id="language">{intl.formatMessage(messages.headlineLanguage)}</h2> 166 <h2 id="language">{intl.formatMessage(messages.headlineLanguage)}</h2>
@@ -172,6 +176,8 @@ export default class EditSettingsForm extends Component {
172 {/* Advanced */} 176 {/* Advanced */}
173 <h2 id="advanced">{intl.formatMessage(messages.headlineAdvanced)}</h2> 177 <h2 id="advanced">{intl.formatMessage(messages.headlineAdvanced)}</h2>
174 <Toggle field={form.$('enableSpellchecking')} /> 178 <Toggle field={form.$('enableSpellchecking')} />
179 <Toggle field={form.$('enableGPUAcceleration')} />
180 <p className="settings__help">{intl.formatMessage(messages.enableGPUAccelerationInfo)}</p>
175 {/* <Select field={form.$('spellcheckingLanguage')} /> */} 181 {/* <Select field={form.$('spellcheckingLanguage')} /> */}
176 <div className="settings__settings-group"> 182 <div className="settings__settings-group">
177 <h3> 183 <h3>
diff --git a/src/components/settings/user/EditUserForm.js b/src/components/settings/user/EditUserForm.js
index 1ac8be80f..b825f844a 100644
--- a/src/components/settings/user/EditUserForm.js
+++ b/src/components/settings/user/EditUserForm.js
@@ -39,8 +39,7 @@ const messages = defineMessages({
39 }, 39 },
40}); 40});
41 41
42@observer 42export default @observer class EditServiceForm extends Component {
43export default class EditServiceForm extends Component {
44 static propTypes = { 43 static propTypes = {
45 status: MobxPropTypes.observableArray.isRequired, 44 status: MobxPropTypes.observableArray.isRequired,
46 form: PropTypes.instanceOf(Form).isRequired, 45 form: PropTypes.instanceOf(Form).isRequired,
diff --git a/src/components/subscription/SubscriptionForm.js b/src/components/subscription/SubscriptionForm.js
index dd350479d..5992e4204 100644
--- a/src/components/subscription/SubscriptionForm.js
+++ b/src/components/subscription/SubscriptionForm.js
@@ -71,8 +71,7 @@ const messages = defineMessages({
71 }, 71 },
72}); 72});
73 73
74@observer 74export default @observer class SubscriptionForm extends Component {
75export default class SubscriptionForm extends Component {
76 static propTypes = { 75 static propTypes = {
77 plan: MobxPropTypes.objectOrObservableObject.isRequired, 76 plan: MobxPropTypes.objectOrObservableObject.isRequired,
78 isLoading: PropTypes.bool.isRequired, 77 isLoading: PropTypes.bool.isRequired,
diff --git a/src/components/subscription/SubscriptionPopup.js b/src/components/subscription/SubscriptionPopup.js
index 528d02907..f3c63e7ee 100644
--- a/src/components/subscription/SubscriptionPopup.js
+++ b/src/components/subscription/SubscriptionPopup.js
@@ -17,8 +17,7 @@ const messages = defineMessages({
17 }, 17 },
18}); 18});
19 19
20@observer 20export default @observer class SubscriptionPopup extends Component {
21export default class SubscriptionPopup extends Component {
22 static propTypes = { 21 static propTypes = {
23 url: PropTypes.string.isRequired, 22 url: PropTypes.string.isRequired,
24 closeWindow: PropTypes.func.isRequired, 23 closeWindow: PropTypes.func.isRequired,
diff --git a/src/components/ui/Button.js b/src/components/ui/Button.js
index 554206cb7..309e05bb4 100644
--- a/src/components/ui/Button.js
+++ b/src/components/ui/Button.js
@@ -4,8 +4,7 @@ import { observer } from 'mobx-react';
4import Loader from 'react-loader'; 4import Loader from 'react-loader';
5import classnames from 'classnames'; 5import classnames from 'classnames';
6 6
7@observer 7export default @observer class Button extends Component {
8export default class Button extends Component {
9 static propTypes = { 8 static propTypes = {
10 className: PropTypes.string, 9 className: PropTypes.string,
11 label: PropTypes.string.isRequired, 10 label: PropTypes.string.isRequired,
diff --git a/src/components/ui/ImageUpload.js b/src/components/ui/ImageUpload.js
index 81c3b8da6..76f77d631 100644
--- a/src/components/ui/ImageUpload.js
+++ b/src/components/ui/ImageUpload.js
@@ -2,12 +2,10 @@ import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import { Field } from 'mobx-react-form'; 4import { Field } from 'mobx-react-form';
5// import Loader from 'react-loader';
6import classnames from 'classnames'; 5import classnames from 'classnames';
7import Dropzone from 'react-dropzone'; 6import Dropzone from 'react-dropzone';
8 7
9@observer 8export default @observer class ImageUpload extends Component {
10export default class ImageUpload extends Component {
11 static propTypes = { 9 static propTypes = {
12 field: PropTypes.instanceOf(Field).isRequired, 10 field: PropTypes.instanceOf(Field).isRequired,
13 className: PropTypes.string, 11 className: PropTypes.string,
diff --git a/src/components/ui/InfoBar.js b/src/components/ui/InfoBar.js
index 84a5f1446..94a1ddf76 100644
--- a/src/components/ui/InfoBar.js
+++ b/src/components/ui/InfoBar.js
@@ -7,8 +7,7 @@ import Loader from 'react-loader';
7// import { oneOrManyChildElements } from '../../prop-types'; 7// import { oneOrManyChildElements } from '../../prop-types';
8import Appear from '../ui/effects/Appear'; 8import Appear from '../ui/effects/Appear';
9 9
10@observer 10export default @observer class InfoBar extends Component {
11export default class InfoBar extends Component {
12 static propTypes = { 11 static propTypes = {
13 // eslint-disable-next-line 12 // eslint-disable-next-line
14 children: PropTypes.any.isRequired, 13 children: PropTypes.any.isRequired,
diff --git a/src/components/ui/Infobox.js b/src/components/ui/Infobox.js
index 2d063c7ef..77051f567 100644
--- a/src/components/ui/Infobox.js
+++ b/src/components/ui/Infobox.js
@@ -4,8 +4,7 @@ import { observer } from 'mobx-react';
4import classnames from 'classnames'; 4import classnames from 'classnames';
5import Loader from 'react-loader'; 5import Loader from 'react-loader';
6 6
7@observer 7export default @observer class Infobox extends Component {
8export default class Infobox extends Component {
9 static propTypes = { 8 static propTypes = {
10 children: PropTypes.any.isRequired, // eslint-disable-line 9 children: PropTypes.any.isRequired, // eslint-disable-line
11 icon: PropTypes.string, 10 icon: PropTypes.string,
diff --git a/src/components/ui/Input.js b/src/components/ui/Input.js
index 69c95702b..7bf6e1b00 100644
--- a/src/components/ui/Input.js
+++ b/src/components/ui/Input.js
@@ -6,8 +6,7 @@ import classnames from 'classnames';
6 6
7import { scorePassword as scorePasswordFunc } from '../../helpers/password-helpers'; 7import { scorePassword as scorePasswordFunc } from '../../helpers/password-helpers';
8 8
9@observer 9export default @observer class Input extends Component {
10export default class Input extends Component {
11 static propTypes = { 10 static propTypes = {
12 field: PropTypes.instanceOf(Field).isRequired, 11 field: PropTypes.instanceOf(Field).isRequired,
13 className: PropTypes.string, 12 className: PropTypes.string,
diff --git a/src/components/ui/Link.js b/src/components/ui/Link.js
index f5da921fa..0602290f1 100644
--- a/src/components/ui/Link.js
+++ b/src/components/ui/Link.js
@@ -9,9 +9,7 @@ import { oneOrManyChildElements } from '../../prop-types';
9import { matchRoute } from '../../helpers/routing-helpers'; 9import { matchRoute } from '../../helpers/routing-helpers';
10 10
11// TODO: create container component for this component 11// TODO: create container component for this component
12 12export default @inject('stores') @observer class Link extends Component {
13@inject('stores') @observer
14export default class Link extends Component {
15 onClick(e) { 13 onClick(e) {
16 if (this.props.target === '_blank') { 14 if (this.props.target === '_blank') {
17 e.preventDefault(); 15 e.preventDefault();
diff --git a/src/components/ui/Radio.js b/src/components/ui/Radio.js
index b54cfc820..63ca6f9b8 100644
--- a/src/components/ui/Radio.js
+++ b/src/components/ui/Radio.js
@@ -4,8 +4,7 @@ import { observer } from 'mobx-react';
4import { Field } from 'mobx-react-form'; 4import { Field } from 'mobx-react-form';
5import classnames from 'classnames'; 5import classnames from 'classnames';
6 6
7@observer 7export default @observer class Radio extends Component {
8export default class Radio extends Component {
9 static propTypes = { 8 static propTypes = {
10 field: PropTypes.instanceOf(Field).isRequired, 9 field: PropTypes.instanceOf(Field).isRequired,
11 className: PropTypes.string, 10 className: PropTypes.string,
diff --git a/src/components/ui/SearchInput.js b/src/components/ui/SearchInput.js
index a94cde201..5a9571d27 100644
--- a/src/components/ui/SearchInput.js
+++ b/src/components/ui/SearchInput.js
@@ -5,8 +5,7 @@ import classnames from 'classnames';
5import uuidv1 from 'uuid/v1'; 5import uuidv1 from 'uuid/v1';
6import { debounce } from 'lodash'; 6import { debounce } from 'lodash';
7 7
8@observer 8export default @observer class SearchInput extends Component {
9export default class SearchInput extends Component {
10 static propTypes = { 9 static propTypes = {
11 value: PropTypes.string, 10 value: PropTypes.string,
12 placeholder: PropTypes.string, 11 placeholder: PropTypes.string,
diff --git a/src/components/ui/Select.js b/src/components/ui/Select.js
index 2a877af3e..abcad417e 100644
--- a/src/components/ui/Select.js
+++ b/src/components/ui/Select.js
@@ -4,8 +4,7 @@ import { observer } from 'mobx-react';
4import { Field } from 'mobx-react-form'; 4import { Field } from 'mobx-react-form';
5import classnames from 'classnames'; 5import classnames from 'classnames';
6 6
7@observer 7export default @observer class Select extends Component {
8export default class Select extends Component {
9 static propTypes = { 8 static propTypes = {
10 field: PropTypes.instanceOf(Field).isRequired, 9 field: PropTypes.instanceOf(Field).isRequired,
11 className: PropTypes.string, 10 className: PropTypes.string,
diff --git a/src/components/ui/StatusBarTargetUrl.js b/src/components/ui/StatusBarTargetUrl.js
index b7b198f42..4285a343c 100644
--- a/src/components/ui/StatusBarTargetUrl.js
+++ b/src/components/ui/StatusBarTargetUrl.js
@@ -5,8 +5,7 @@ import classnames from 'classnames';
5 5
6import Appear from '../ui/effects/Appear'; 6import Appear from '../ui/effects/Appear';
7 7
8@observer 8export default @observer class StatusBarTargetUrl extends Component {
9export default class StatusBarTargetUrl extends Component {
10 static propTypes = { 9 static propTypes = {
11 className: PropTypes.string, 10 className: PropTypes.string,
12 text: PropTypes.string, 11 text: PropTypes.string,
diff --git a/src/components/ui/Tabs/Tabs.js b/src/components/ui/Tabs/Tabs.js
index 50397f9bb..12f650ffd 100644
--- a/src/components/ui/Tabs/Tabs.js
+++ b/src/components/ui/Tabs/Tabs.js
@@ -5,8 +5,7 @@ import classnames from 'classnames';
5 5
6import { oneOrManyChildElements } from '../../../prop-types'; 6import { oneOrManyChildElements } from '../../../prop-types';
7 7
8@observer 8export default @observer class Tab extends Component {
9export default class Tab extends Component {
10 static propTypes = { 9 static propTypes = {
11 children: oneOrManyChildElements.isRequired, 10 children: oneOrManyChildElements.isRequired,
12 active: PropTypes.number, 11 active: PropTypes.number,
diff --git a/src/components/ui/Toggle.js b/src/components/ui/Toggle.js
index 62d46393e..f7c2ec955 100644
--- a/src/components/ui/Toggle.js
+++ b/src/components/ui/Toggle.js
@@ -4,8 +4,7 @@ import { observer } from 'mobx-react';
4import classnames from 'classnames'; 4import classnames from 'classnames';
5import { Field } from 'mobx-react-form'; 5import { Field } from 'mobx-react-form';
6 6
7@observer 7export default @observer class Toggle extends Component {
8export default class Toggle extends Component {
9 static propTypes = { 8 static propTypes = {
10 field: PropTypes.instanceOf(Field).isRequired, 9 field: PropTypes.instanceOf(Field).isRequired,
11 className: PropTypes.string, 10 className: PropTypes.string,
diff --git a/src/config.js b/src/config.js
index e66594c59..ce946f00a 100644
--- a/src/config.js
+++ b/src/config.js
@@ -1,11 +1,15 @@
1import electron from 'electron';
2import path from 'path';
3
4const app = process.type === 'renderer' ? electron.remote.app : electron.app;
5
1export const CHECK_INTERVAL = 1000 * 3600; // How often should we perform checks 6export const CHECK_INTERVAL = 1000 * 3600; // How often should we perform checks
2export const LOCAL_API = 'http://localhost:3000'; 7export const LOCAL_API = 'http://localhost:3000';
3export const DEV_API = 'https://dev.franzinfra.com'; 8export const DEV_API = 'https://dev.franzinfra.com';
4export const LIVE_API = 'https://api.franzinfra.com'; 9export const LIVE_API = 'https://api.franzinfra.com';
5export const GA_ID = 'UA-74126766-6'; 10export const GA_ID = 'UA-74126766-10';
6 11
7export const DEFAULT_APP_SETTINGS = { 12export const DEFAULT_APP_SETTINGS = {
8 autoLaunchOnStart: true,
9 autoLaunchInBackground: false, 13 autoLaunchInBackground: false,
10 runInBackground: true, 14 runInBackground: true,
11 enableSystemTray: true, 15 enableSystemTray: true,
@@ -13,12 +17,15 @@ export const DEFAULT_APP_SETTINGS = {
13 showDisabledServices: true, 17 showDisabledServices: true,
14 showMessageBadgeWhenMuted: true, 18 showMessageBadgeWhenMuted: true,
15 enableSpellchecking: true, 19 enableSpellchecking: true,
16 // spellcheckingLanguage: 'auto', 20 darkMode: false,
17 locale: '', 21 locale: '',
18 fallbackLocale: 'en-US', 22 fallbackLocale: 'en-US',
19 beta: false, 23 beta: false,
20 isAppMuted: false, 24 isAppMuted: false,
25 enableGPUAcceleration: true,
21}; 26};
22 27
23export const FRANZ_SERVICE_REQUEST = 'http://bit.ly/franz-service-request'; 28export const FRANZ_SERVICE_REQUEST = 'https://bit.ly/franz-service-request';
24export const FRANZ_TRANSLATION = 'http://bit.ly/franz-translate'; 29export const FRANZ_TRANSLATION = 'https://bit.ly/franz-translate';
30
31export const SETTINGS_PATH = path.join(app.getPath('userData'), 'config', 'settings.json');
diff --git a/src/containers/auth/AuthLayoutContainer.js b/src/containers/auth/AuthLayoutContainer.js
index f362b1800..b73598f3d 100644
--- a/src/containers/auth/AuthLayoutContainer.js
+++ b/src/containers/auth/AuthLayoutContainer.js
@@ -9,8 +9,7 @@ import AppLoader from '../../components/ui/AppLoader';
9 9
10import { oneOrManyChildElements } from '../../prop-types'; 10import { oneOrManyChildElements } from '../../prop-types';
11 11
12@inject('stores', 'actions') @observer 12export default @inject('stores', 'actions') @observer class AuthLayoutContainer extends Component {
13export default class AuthLayoutContainer extends Component {
14 static propTypes = { 13 static propTypes = {
15 children: oneOrManyChildElements.isRequired, 14 children: oneOrManyChildElements.isRequired,
16 location: PropTypes.shape({ 15 location: PropTypes.shape({
@@ -20,10 +19,10 @@ export default class AuthLayoutContainer extends Component {
20 19
21 render() { 20 render() {
22 const { stores, actions, children, location } = this.props; 21 const { stores, actions, children, location } = this.props;
23 const { features } = stores; 22 const { app, features, globalError } = stores;
24 23
25 const isLoadingBaseFeatures = features.baseFeaturesRequest.isExecuting 24 const isLoadingBaseFeatures = features.defaultFeaturesRequest.isExecuting
26 && !features.baseFeaturesRequest.wasExecuted; 25 && !features.defaultFeaturesRequest.wasExecuted;
27 26
28 if (isLoadingBaseFeatures) { 27 if (isLoadingBaseFeatures) {
29 return ( 28 return (
@@ -33,12 +32,14 @@ export default class AuthLayoutContainer extends Component {
33 32
34 return ( 33 return (
35 <AuthLayout 34 <AuthLayout
36 error={stores.globalError.response} 35 error={globalError.response}
37 pathname={location.pathname} 36 pathname={location.pathname}
38 isOnline={stores.app.isOnline} 37 isOnline={app.isOnline}
39 isAPIHealthy={!stores.app.healthCheckRequest.isError} 38 isAPIHealthy={!app.healthCheckRequest.isError}
40 retryHealthCheck={actions.app.healthCheck} 39 retryHealthCheck={actions.app.healthCheck}
41 isHealthCheckLoading={stores.app.healthCheckRequest.isExecuting} 40 isHealthCheckLoading={app.healthCheckRequest.isExecuting}
41 isFullScreen={app.isFullScreen}
42 darkMode={app.isSystemDarkModeEnabled}
42 > 43 >
43 {children} 44 {children}
44 </AuthLayout> 45 </AuthLayout>
diff --git a/src/containers/auth/ImportScreen.js b/src/containers/auth/ImportScreen.js
index ddd56ffb6..fc46f8b54 100644
--- a/src/containers/auth/ImportScreen.js
+++ b/src/containers/auth/ImportScreen.js
@@ -5,8 +5,7 @@ import Import from '../../components/auth/Import';
5import UserStore from '../../stores/UserStore'; 5import UserStore from '../../stores/UserStore';
6import { gaPage } from '../../lib/analytics'; 6import { gaPage } from '../../lib/analytics';
7 7
8@inject('stores', 'actions') @observer 8export default @inject('stores', 'actions') @observer class ImportScreen extends Component {
9export default class ImportScreen extends Component {
10 componentDidMount() { 9 componentDidMount() {
11 gaPage('Auth/Import'); 10 gaPage('Auth/Import');
12 } 11 }
diff --git a/src/containers/auth/InviteScreen.js b/src/containers/auth/InviteScreen.js
index 059888c99..26bf97038 100644
--- a/src/containers/auth/InviteScreen.js
+++ b/src/containers/auth/InviteScreen.js
@@ -4,8 +4,7 @@ import { inject, observer } from 'mobx-react';
4import Invite from '../../components/auth/Invite'; 4import Invite from '../../components/auth/Invite';
5import { gaPage } from '../../lib/analytics'; 5import { gaPage } from '../../lib/analytics';
6 6
7@inject('stores', 'actions') @observer 7export default @inject('stores', 'actions') @observer class InviteScreen extends Component {
8export default class InviteScreen extends Component {
9 componentDidMount() { 8 componentDidMount() {
10 gaPage('Auth/Invite'); 9 gaPage('Auth/Invite');
11 } 10 }
diff --git a/src/containers/auth/LoginScreen.js b/src/containers/auth/LoginScreen.js
index 9e22c5141..865bd38f8 100644
--- a/src/containers/auth/LoginScreen.js
+++ b/src/containers/auth/LoginScreen.js
@@ -7,8 +7,7 @@ import { gaPage } from '../../lib/analytics';
7 7
8import { globalError as globalErrorPropType } from '../../prop-types'; 8import { globalError as globalErrorPropType } from '../../prop-types';
9 9
10@inject('stores', 'actions') @observer 10export default @inject('stores', 'actions') @observer class LoginScreen extends Component {
11export default class LoginScreen extends Component {
12 static propTypes = { 11 static propTypes = {
13 error: globalErrorPropType.isRequired, 12 error: globalErrorPropType.isRequired,
14 }; 13 };
diff --git a/src/containers/auth/PasswordScreen.js b/src/containers/auth/PasswordScreen.js
index d88cb08e6..236fd2031 100644
--- a/src/containers/auth/PasswordScreen.js
+++ b/src/containers/auth/PasswordScreen.js
@@ -5,8 +5,7 @@ import Password from '../../components/auth/Password';
5import UserStore from '../../stores/UserStore'; 5import UserStore from '../../stores/UserStore';
6import { gaPage } from '../../lib/analytics'; 6import { gaPage } from '../../lib/analytics';
7 7
8@inject('stores', 'actions') @observer 8export default @inject('stores', 'actions') @observer class PasswordScreen extends Component {
9export default class PasswordScreen extends Component {
10 componentDidMount() { 9 componentDidMount() {
11 gaPage('Auth/Password Retrieve'); 10 gaPage('Auth/Password Retrieve');
12 } 11 }
diff --git a/src/containers/auth/PricingScreen.js b/src/containers/auth/PricingScreen.js
index 7e1586535..34b512e15 100644
--- a/src/containers/auth/PricingScreen.js
+++ b/src/containers/auth/PricingScreen.js
@@ -10,8 +10,7 @@ import { gaPage } from '../../lib/analytics';
10 10
11import { globalError as globalErrorPropType } from '../../prop-types'; 11import { globalError as globalErrorPropType } from '../../prop-types';
12 12
13@inject('stores', 'actions') @observer 13export default @inject('stores', 'actions') @observer class PricingScreen extends Component {
14export default class PricingScreen extends Component {
15 static propTypes = { 14 static propTypes = {
16 error: globalErrorPropType.isRequired, 15 error: globalErrorPropType.isRequired,
17 }; 16 };
diff --git a/src/containers/auth/SignupScreen.js b/src/containers/auth/SignupScreen.js
index 3b86ab138..caf75de90 100644
--- a/src/containers/auth/SignupScreen.js
+++ b/src/containers/auth/SignupScreen.js
@@ -8,8 +8,7 @@ import { gaPage } from '../../lib/analytics';
8 8
9import { globalError as globalErrorPropType } from '../../prop-types'; 9import { globalError as globalErrorPropType } from '../../prop-types';
10 10
11@inject('stores', 'actions') @observer 11export default @inject('stores', 'actions') @observer class SignupScreen extends Component {
12export default class SignupScreen extends Component {
13 static propTypes = { 12 static propTypes = {
14 error: globalErrorPropType.isRequired, 13 error: globalErrorPropType.isRequired,
15 }; 14 };
diff --git a/src/containers/auth/WelcomeScreen.js b/src/containers/auth/WelcomeScreen.js
index e413264a6..2c120f81c 100644
--- a/src/containers/auth/WelcomeScreen.js
+++ b/src/containers/auth/WelcomeScreen.js
@@ -7,8 +7,7 @@ import UserStore from '../../stores/UserStore';
7import RecipePreviewsStore from '../../stores/RecipePreviewsStore'; 7import RecipePreviewsStore from '../../stores/RecipePreviewsStore';
8import { gaPage } from '../../lib/analytics'; 8import { gaPage } from '../../lib/analytics';
9 9
10@inject('stores', 'actions') @observer 10export default @inject('stores', 'actions') @observer class LoginScreen extends Component {
11export default class LoginScreen extends Component {
12 componentDidMount() { 11 componentDidMount() {
13 gaPage('Auth/Welcome'); 12 gaPage('Auth/Welcome');
14 } 13 }
diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js
index 7a398ed2d..affc1a0a2 100644
--- a/src/containers/layout/AppLayoutContainer.js
+++ b/src/containers/layout/AppLayoutContainer.js
@@ -18,8 +18,7 @@ import Sidebar from '../../components/layout/Sidebar';
18import Services from '../../components/services/content/Services'; 18import Services from '../../components/services/content/Services';
19import AppLoader from '../../components/ui/AppLoader'; 19import AppLoader from '../../components/ui/AppLoader';
20 20
21@inject('stores', 'actions') @observer 21export default @inject('stores', 'actions') @observer class AppLayoutContainer extends Component {
22export default class AppLayoutContainer extends Component {
23 static defaultProps = { 22 static defaultProps = {
24 children: null, 23 children: null,
25 }; 24 };
@@ -82,7 +81,7 @@ export default class AppLayoutContainer extends Component {
82 <Sidebar 81 <Sidebar
83 services={services.allDisplayed} 82 services={services.allDisplayed}
84 setActive={setActive} 83 setActive={setActive}
85 isAppMuted={settings.all.isAppMuted} 84 isAppMuted={settings.all.app.isAppMuted}
86 openSettings={openSettings} 85 openSettings={openSettings}
87 closeSettings={closeSettings} 86 closeSettings={closeSettings}
88 reorder={reorder} 87 reorder={reorder}
@@ -92,25 +91,26 @@ export default class AppLayoutContainer extends Component {
92 deleteService={deleteService} 91 deleteService={deleteService}
93 updateService={updateService} 92 updateService={updateService}
94 toggleMuteApp={toggleMuteApp} 93 toggleMuteApp={toggleMuteApp}
95 showMessageBadgeWhenMutedSetting={settings.all.showMessageBadgeWhenMuted} 94 showMessageBadgeWhenMutedSetting={settings.all.app.showMessageBadgeWhenMuted}
96 showMessageBadgesEvenWhenMuted={ui.showMessageBadgesEvenWhenMuted} 95 showMessageBadgesEvenWhenMuted={ui.showMessageBadgesEvenWhenMuted}
97 /> 96 />
98 ); 97 );
99 98
100 const servicesContainer = ( 99 const servicesContainer = (
101 <Services 100 <Services
102 services={services.allDisplayed} 101 services={services.allDisplayedUnordered}
103 handleIPCMessage={handleIPCMessage} 102 handleIPCMessage={handleIPCMessage}
104 setWebviewReference={setWebviewReference} 103 setWebviewReference={setWebviewReference}
105 openWindow={openWindow} 104 openWindow={openWindow}
106 reload={reload} 105 reload={reload}
107 isAppMuted={settings.all.isAppMuted} 106 isAppMuted={settings.all.app.isAppMuted}
108 update={updateService} 107 update={updateService}
109 /> 108 />
110 ); 109 );
111 110
112 return ( 111 return (
113 <AppLayout 112 <AppLayout
113 isFullScreen={app.isFullScreen}
114 isOnline={app.isOnline} 114 isOnline={app.isOnline}
115 showServicesUpdatedInfoBar={ui.showServicesUpdatedInfoBar} 115 showServicesUpdatedInfoBar={ui.showServicesUpdatedInfoBar}
116 appUpdateIsDownloaded={app.updateStatus === app.updateStatusTypes.DOWNLOADED} 116 appUpdateIsDownloaded={app.updateStatus === app.updateStatusTypes.DOWNLOADED}
@@ -125,6 +125,7 @@ export default class AppLayoutContainer extends Component {
125 areRequiredRequestsSuccessful={requests.areRequiredRequestsSuccessful} 125 areRequiredRequestsSuccessful={requests.areRequiredRequestsSuccessful}
126 retryRequiredRequests={retryRequiredRequests} 126 retryRequiredRequests={retryRequiredRequests}
127 areRequiredRequestsLoading={requests.areRequiredRequestsLoading} 127 areRequiredRequestsLoading={requests.areRequiredRequestsLoading}
128 darkMode={settings.all.app.darkMode}
128 > 129 >
129 {React.Children.count(children) > 0 ? children : null} 130 {React.Children.count(children) > 0 ? children : null}
130 </AppLayout> 131 </AppLayout>
diff --git a/src/containers/settings/AccountScreen.js b/src/containers/settings/AccountScreen.js
index c5c2982b0..5818af0b1 100644
--- a/src/containers/settings/AccountScreen.js
+++ b/src/containers/settings/AccountScreen.js
@@ -12,8 +12,7 @@ import AccountDashboard from '../../components/settings/account/AccountDashboard
12 12
13const { BrowserWindow } = remote; 13const { BrowserWindow } = remote;
14 14
15@inject('stores', 'actions') @observer 15export default @inject('stores', 'actions') @observer class AccountScreen extends Component {
16export default class AccountScreen extends Component {
17 componentDidMount() { 16 componentDidMount() {
18 gaPage('Settings/Account Dashboard'); 17 gaPage('Settings/Account Dashboard');
19 } 18 }
diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js
index f2d5550e1..17d727642 100644
--- a/src/containers/settings/EditServiceScreen.js
+++ b/src/containers/settings/EditServiceScreen.js
@@ -7,6 +7,7 @@ import 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 FeaturesStore from '../../stores/FeaturesStore'; 9import FeaturesStore from '../../stores/FeaturesStore';
10import SettingsStore from '../../stores/SettingsStore';
10import Form from '../../lib/Form'; 11import Form from '../../lib/Form';
11import { gaPage } from '../../lib/analytics'; 12import { gaPage } from '../../lib/analytics';
12 13
@@ -51,10 +52,13 @@ const messages = defineMessages({
51 id: 'settings.service.form.icon', 52 id: 'settings.service.form.icon',
52 defaultMessage: '!!!Custom icon', 53 defaultMessage: '!!!Custom icon',
53 }, 54 },
55 enableDarkMode: {
56 id: 'settings.service.form.enableDarkMode',
57 defaultMessage: '!!!Enable Dark Mode',
58 },
54}); 59});
55 60
56@inject('stores', 'actions') @observer 61export default @inject('stores', 'actions') @observer class EditServiceScreen extends Component {
57export default class EditServiceScreen extends Component {
58 static contextTypes = { 62 static contextTypes = {
59 intl: intlShape, 63 intl: intlShape,
60 }; 64 };
@@ -113,6 +117,11 @@ export default class EditServiceScreen extends Component {
113 default: null, 117 default: null,
114 type: 'file', 118 type: 'file',
115 }, 119 },
120 isDarkModeEnabled: {
121 label: intl.formatMessage(messages.enableDarkMode),
122 value: service.isDarkModeEnabled,
123 default: this.props.stores.settings.all.app.darkMode,
124 },
116 }, 125 },
117 }; 126 };
118 127
@@ -245,6 +254,7 @@ EditServiceScreen.wrappedComponent.propTypes = {
245 recipes: PropTypes.instanceOf(RecipesStore).isRequired, 254 recipes: PropTypes.instanceOf(RecipesStore).isRequired,
246 services: PropTypes.instanceOf(ServicesStore).isRequired, 255 services: PropTypes.instanceOf(ServicesStore).isRequired,
247 features: PropTypes.instanceOf(FeaturesStore).isRequired, 256 features: PropTypes.instanceOf(FeaturesStore).isRequired,
257 settings: PropTypes.instanceOf(SettingsStore).isRequired,
248 }).isRequired, 258 }).isRequired,
249 router: PropTypes.shape({ 259 router: PropTypes.shape({
250 params: PropTypes.shape({ 260 params: PropTypes.shape({
diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js
index 1fa7ce8bc..df6442eb8 100644
--- a/src/containers/settings/EditSettingsScreen.js
+++ b/src/containers/settings/EditSettingsScreen.js
@@ -39,6 +39,10 @@ const messages = defineMessages({
39 id: 'settings.app.form.language', 39 id: 'settings.app.form.language',
40 defaultMessage: '!!!Language', 40 defaultMessage: '!!!Language',
41 }, 41 },
42 darkMode: {
43 id: 'settings.app.form.darkMode',
44 defaultMessage: '!!!Dark Mode',
45 },
42 showDisabledServices: { 46 showDisabledServices: {
43 id: 'settings.app.form.showDisabledServices', 47 id: 'settings.app.form.showDisabledServices',
44 defaultMessage: '!!!Display disabled services tabs', 48 defaultMessage: '!!!Display disabled services tabs',
@@ -51,22 +55,21 @@ const messages = defineMessages({
51 id: 'settings.app.form.enableSpellchecking', 55 id: 'settings.app.form.enableSpellchecking',
52 defaultMessage: '!!!Enable spell checking', 56 defaultMessage: '!!!Enable spell checking',
53 }, 57 },
58 enableGPUAcceleration: {
59 id: 'settings.app.form.enableGPUAcceleration',
60 defaultMessage: '!!!Enable GPU Acceleration',
61 },
54 spellcheckingLanguage: { 62 spellcheckingLanguage: {
55 id: 'settings.app.form.spellcheckingLanguage', 63 id: 'settings.app.form.spellcheckingLanguage',
56 defaultMessage: '!!!Language for spell checking', 64 defaultMessage: '!!!Language for spell checking',
57 }, 65 },
58 // spellcheckingAutomaticDetection: {
59 // id: 'settings.app.form.spellcheckingAutomaticDetection',
60 // defaultMessage: '!!!Detect language automatically',
61 // },
62 beta: { 66 beta: {
63 id: 'settings.app.form.beta', 67 id: 'settings.app.form.beta',
64 defaultMessage: '!!!Include beta versions', 68 defaultMessage: '!!!Include beta versions',
65 }, 69 },
66}); 70});
67 71
68@inject('stores', 'actions') @observer 72export default @inject('stores', 'actions') @observer class EditSettingsScreen extends Component {
69export default class EditSettingsScreen extends Component {
70 static contextTypes = { 73 static contextTypes = {
71 intl: intlShape, 74 intl: intlShape,
72 }; 75 };
@@ -84,22 +87,25 @@ export default class EditSettingsScreen extends Component {
84 }); 87 });
85 88
86 settings.update({ 89 settings.update({
87 settings: { 90 type: 'app',
91 data: {
88 runInBackground: settingsData.runInBackground, 92 runInBackground: settingsData.runInBackground,
89 enableSystemTray: settingsData.enableSystemTray, 93 enableSystemTray: settingsData.enableSystemTray,
90 minimizeToSystemTray: settingsData.minimizeToSystemTray, 94 minimizeToSystemTray: settingsData.minimizeToSystemTray,
95 enableGPUAcceleration: settingsData.enableGPUAcceleration,
91 showDisabledServices: settingsData.showDisabledServices, 96 showDisabledServices: settingsData.showDisabledServices,
97 darkMode: settingsData.darkMode,
92 showMessageBadgeWhenMuted: settingsData.showMessageBadgeWhenMuted, 98 showMessageBadgeWhenMuted: settingsData.showMessageBadgeWhenMuted,
93 enableSpellchecking: settingsData.enableSpellchecking, 99 enableSpellchecking: settingsData.enableSpellchecking,
94 // spellcheckingLanguage: settingsData.spellcheckingLanguage, 100 beta: settingsData.beta, // we need this info in the main process as well
95 locale: settingsData.locale, 101 locale: settingsData.locale, // we need this info in the main process as well
96 beta: settingsData.beta,
97 }, 102 },
98 }); 103 });
99 104
100 user.update({ 105 user.update({
101 userData: { 106 userData: {
102 beta: settingsData.beta, 107 beta: settingsData.beta,
108 locale: settingsData.locale,
103 }, 109 },
104 }); 110 });
105 } 111 }
@@ -116,17 +122,6 @@ export default class EditSettingsScreen extends Component {
116 }); 122 });
117 }); 123 });
118 124
119 // const spellcheckerLocales = [{
120 // value: 'auto',
121 // label: intl.formatMessage(messages.spellcheckingAutomaticDetection),
122 // }];
123 // Object.keys(SPELLCHECKER_LOCALES).forEach((key) => {
124 // spellcheckerLocales.push({
125 // value: key,
126 // label: SPELLCHECKER_LOCALES[key],
127 // });
128 // });
129
130 const config = { 125 const config = {
131 fields: { 126 fields: {
132 autoLaunchOnStart: { 127 autoLaunchOnStart: {
@@ -141,40 +136,44 @@ export default class EditSettingsScreen extends Component {
141 }, 136 },
142 runInBackground: { 137 runInBackground: {
143 label: intl.formatMessage(messages.runInBackground), 138 label: intl.formatMessage(messages.runInBackground),
144 value: settings.all.runInBackground, 139 value: settings.all.app.runInBackground,
145 default: DEFAULT_APP_SETTINGS.runInBackground, 140 default: DEFAULT_APP_SETTINGS.runInBackground,
146 }, 141 },
147 enableSystemTray: { 142 enableSystemTray: {
148 label: intl.formatMessage(messages.enableSystemTray), 143 label: intl.formatMessage(messages.enableSystemTray),
149 value: settings.all.enableSystemTray, 144 value: settings.all.app.enableSystemTray,
150 default: DEFAULT_APP_SETTINGS.enableSystemTray, 145 default: DEFAULT_APP_SETTINGS.enableSystemTray,
151 }, 146 },
152 minimizeToSystemTray: { 147 minimizeToSystemTray: {
153 label: intl.formatMessage(messages.minimizeToSystemTray), 148 label: intl.formatMessage(messages.minimizeToSystemTray),
154 value: settings.all.minimizeToSystemTray, 149 value: settings.all.app.minimizeToSystemTray,
155 default: DEFAULT_APP_SETTINGS.minimizeToSystemTray, 150 default: DEFAULT_APP_SETTINGS.minimizeToSystemTray,
156 }, 151 },
157 showDisabledServices: { 152 showDisabledServices: {
158 label: intl.formatMessage(messages.showDisabledServices), 153 label: intl.formatMessage(messages.showDisabledServices),
159 value: settings.all.showDisabledServices, 154 value: settings.all.app.showDisabledServices,
160 default: DEFAULT_APP_SETTINGS.showDisabledServices, 155 default: DEFAULT_APP_SETTINGS.showDisabledServices,
161 }, 156 },
162 showMessageBadgeWhenMuted: { 157 showMessageBadgeWhenMuted: {
163 label: intl.formatMessage(messages.showMessageBadgeWhenMuted), 158 label: intl.formatMessage(messages.showMessageBadgeWhenMuted),
164 value: settings.all.showMessageBadgeWhenMuted, 159 value: settings.all.app.showMessageBadgeWhenMuted,
165 default: DEFAULT_APP_SETTINGS.showMessageBadgeWhenMuted, 160 default: DEFAULT_APP_SETTINGS.showMessageBadgeWhenMuted,
166 }, 161 },
167 enableSpellchecking: { 162 enableSpellchecking: {
168 label: intl.formatMessage(messages.enableSpellchecking), 163 label: intl.formatMessage(messages.enableSpellchecking),
169 value: settings.all.enableSpellchecking, 164 value: settings.all.app.enableSpellchecking,
170 default: DEFAULT_APP_SETTINGS.enableSpellchecking, 165 default: DEFAULT_APP_SETTINGS.enableSpellchecking,
171 }, 166 },
172 // spellcheckingLanguage: { 167 darkMode: {
173 // label: intl.formatMessage(messages.spellcheckingLanguage), 168 label: intl.formatMessage(messages.darkMode),
174 // value: settings.all.spellcheckingLanguage, 169 value: settings.all.app.darkMode,
175 // options: spellcheckerLocales, 170 default: DEFAULT_APP_SETTINGS.darkMode,
176 // default: DEFAULT_APP_SETTINGS.spellcheckingLanguage, 171 },
177 // }, 172 enableGPUAcceleration: {
173 label: intl.formatMessage(messages.enableGPUAcceleration),
174 value: settings.all.app.enableGPUAcceleration,
175 default: DEFAULT_APP_SETTINGS.enableGPUAcceleration,
176 },
178 locale: { 177 locale: {
179 label: intl.formatMessage(messages.language), 178 label: intl.formatMessage(messages.language),
180 value: app.locale, 179 value: app.locale,
diff --git a/src/containers/settings/EditUserScreen.js b/src/containers/settings/EditUserScreen.js
index a67fa7fb5..3da3e8d2c 100644
--- a/src/containers/settings/EditUserScreen.js
+++ b/src/containers/settings/EditUserScreen.js
@@ -50,8 +50,7 @@ const messages = defineMessages({
50 }, 50 },
51}); 51});
52 52
53@inject('stores', 'actions') @observer 53export default @inject('stores', 'actions') @observer class EditUserScreen extends Component {
54export default class EditUserScreen extends Component {
55 static contextTypes = { 54 static contextTypes = {
56 intl: intlShape, 55 intl: intlShape,
57 }; 56 };
diff --git a/src/containers/settings/InviteScreen.js b/src/containers/settings/InviteScreen.js
index 5f341b1b3..38ca6ec74 100644
--- a/src/containers/settings/InviteScreen.js
+++ b/src/containers/settings/InviteScreen.js
@@ -5,8 +5,7 @@ import { inject, observer } from 'mobx-react';
5import Invite from '../../components/auth/Invite'; 5import Invite from '../../components/auth/Invite';
6import { gaPage } from '../../lib/analytics'; 6import { gaPage } from '../../lib/analytics';
7 7
8@inject('stores', 'actions') @observer 8export default @inject('stores', 'actions') @observer class InviteScreen extends Component {
9export default class InviteScreen extends Component {
10 componentDidMount() { 9 componentDidMount() {
11 gaPage('Settings/Invite'); 10 gaPage('Settings/Invite');
12 } 11 }
diff --git a/src/containers/settings/RecipesScreen.js b/src/containers/settings/RecipesScreen.js
index 65341e9e3..4efe81505 100644
--- a/src/containers/settings/RecipesScreen.js
+++ b/src/containers/settings/RecipesScreen.js
@@ -11,8 +11,7 @@ import { gaPage } from '../../lib/analytics';
11 11
12import RecipesDashboard from '../../components/settings/recipes/RecipesDashboard'; 12import RecipesDashboard from '../../components/settings/recipes/RecipesDashboard';
13 13
14@inject('stores', 'actions') @observer 14export default @inject('stores', 'actions') @observer class RecipesScreen extends Component {
15export default class RecipesScreen extends Component {
16 static propTypes = { 15 static propTypes = {
17 params: PropTypes.shape({ 16 params: PropTypes.shape({
18 filter: PropTypes.string, 17 filter: PropTypes.string,
diff --git a/src/containers/settings/ServicesScreen.js b/src/containers/settings/ServicesScreen.js
index 12db1bcd3..c1a133ef7 100644
--- a/src/containers/settings/ServicesScreen.js
+++ b/src/containers/settings/ServicesScreen.js
@@ -10,8 +10,7 @@ import { gaPage } from '../../lib/analytics';
10 10
11import ServicesDashboard from '../../components/settings/services/ServicesDashboard'; 11import ServicesDashboard from '../../components/settings/services/ServicesDashboard';
12 12
13@inject('stores', 'actions') @observer 13export default @inject('stores', 'actions') @observer class ServicesScreen extends Component {
14export default class ServicesScreen extends Component {
15 componentDidMount() { 14 componentDidMount() {
16 gaPage('Settings/Service Dashboard'); 15 gaPage('Settings/Service Dashboard');
17 } 16 }
diff --git a/src/containers/settings/SettingsWindow.js b/src/containers/settings/SettingsWindow.js
index 13ca96f72..55589d0be 100644
--- a/src/containers/settings/SettingsWindow.js
+++ b/src/containers/settings/SettingsWindow.js
@@ -7,8 +7,7 @@ import ServicesStore from '../../stores/ServicesStore';
7import Layout from '../../components/settings/SettingsLayout'; 7import Layout from '../../components/settings/SettingsLayout';
8import Navigation from '../../components/settings/navigation/SettingsNavigation'; 8import Navigation from '../../components/settings/navigation/SettingsNavigation';
9 9
10@inject('stores', 'actions') @observer 10export default @inject('stores', 'actions') @observer class SettingsContainer extends Component {
11export default class SettingsContainer extends Component {
12 render() { 11 render() {
13 const { children, stores } = this.props; 12 const { children, stores } = this.props;
14 const { closeSettings } = this.props.actions.ui; 13 const { closeSettings } = this.props.actions.ui;
diff --git a/src/containers/subscription/SubscriptionFormScreen.js b/src/containers/subscription/SubscriptionFormScreen.js
index fc6e3c4be..9f7571bda 100644
--- a/src/containers/subscription/SubscriptionFormScreen.js
+++ b/src/containers/subscription/SubscriptionFormScreen.js
@@ -9,8 +9,7 @@ import SubscriptionForm from '../../components/subscription/SubscriptionForm';
9 9
10const { BrowserWindow } = remote; 10const { BrowserWindow } = remote;
11 11
12@inject('stores', 'actions') @observer 12export default @inject('stores', 'actions') @observer class SubscriptionFormScreen extends Component {
13export default class SubscriptionFormScreen extends Component {
14 static propTypes = { 13 static propTypes = {
15 onCloseWindow: PropTypes.func, 14 onCloseWindow: PropTypes.func,
16 content: PropTypes.oneOrManyChildElements, 15 content: PropTypes.oneOrManyChildElements,
diff --git a/src/containers/subscription/SubscriptionPopupScreen.js b/src/containers/subscription/SubscriptionPopupScreen.js
index bb0603170..6641f236d 100644
--- a/src/containers/subscription/SubscriptionPopupScreen.js
+++ b/src/containers/subscription/SubscriptionPopupScreen.js
@@ -5,8 +5,7 @@ import { inject, observer } from 'mobx-react';
5import SubscriptionPopup from '../../components/subscription/SubscriptionPopup'; 5import SubscriptionPopup from '../../components/subscription/SubscriptionPopup';
6 6
7 7
8@inject('stores', 'actions') @observer 8export default @inject('stores', 'actions') @observer class SubscriptionPopupScreen extends Component {
9export default class SubscriptionPopupScreen extends Component {
10 state = { 9 state = {
11 complete: false, 10 complete: false,
12 }; 11 };
diff --git a/src/electron/Settings.js b/src/electron/Settings.js
index 824b4c20c..7b04406a2 100644
--- a/src/electron/Settings.js
+++ b/src/electron/Settings.js
@@ -1,27 +1,46 @@
1import { observable } from 'mobx'; 1import { observable, toJS } from 'mobx';
2import { pathExistsSync, outputJsonSync, readJsonSync } from 'fs-extra';
2 3
3import { DEFAULT_APP_SETTINGS } from '../config'; 4import { SETTINGS_PATH, DEFAULT_APP_SETTINGS } from '../config';
5
6const debug = require('debug')('Franz:Settings');
4 7
5export default class Settings { 8export default class Settings {
6 @observable store = { 9 @observable store = DEFAULT_APP_SETTINGS;
7 autoLaunchOnStart: DEFAULT_APP_SETTINGS.autoLaunchOnStart, 10
8 autoLaunchInBackground: DEFAULT_APP_SETTINGS.autoLaunchInBackground, 11 constructor() {
9 runInBackground: DEFAULT_APP_SETTINGS.runInBackground, 12 if (!pathExistsSync(SETTINGS_PATH)) {
10 enableSystemTray: DEFAULT_APP_SETTINGS.enableSystemTray, 13 this._writeFile();
11 minimizeToSystemTray: DEFAULT_APP_SETTINGS.minimizeToSystemTray, 14 } else {
12 locale: DEFAULT_APP_SETTINGS.locale, 15 this._hydrate();
13 beta: DEFAULT_APP_SETTINGS.beta, 16 }
14 }; 17 }
15 18
16 set(settings) { 19 set(settings) {
17 this.store = Object.assign(this.store, settings); 20 this.store = this._merge(settings);
21
22 this._writeFile();
18 } 23 }
19 24
20 all() { 25 get all() {
21 return this.store; 26 return this.store;
22 } 27 }
23 28
24 get(key) { 29 get(key) {
25 return this.store[key]; 30 return this.store[key];
26 } 31 }
32
33 _merge(settings) {
34 return Object.assign(DEFAULT_APP_SETTINGS, this.store, settings);
35 }
36
37 _hydrate() {
38 this.store = this._merge(readJsonSync(SETTINGS_PATH));
39 debug('Hydrate store', toJS(this.store));
40 }
41
42 _writeFile() {
43 outputJsonSync(SETTINGS_PATH, this.store);
44 debug('Write settings file', toJS(this.store));
45 }
27} 46}
diff --git a/src/electron/ipc-api/settings.js b/src/electron/ipc-api/settings.js
index 995b28fbd..3eab68a91 100644
--- a/src/electron/ipc-api/settings.js
+++ b/src/electron/ipc-api/settings.js
@@ -1,7 +1,11 @@
1import { ipcMain } from 'electron'; 1import { ipcMain } from 'electron';
2 2
3export default (params) => { 3export default (params) => {
4 ipcMain.on('settings', (event, args) => { 4 ipcMain.on('getAppSettings', () => {
5 params.mainWindow.webContents.send('appSettings', params.settings.all);
6 });
7
8 ipcMain.on('updateAppSettings', (event, args) => {
5 params.settings.set(args); 9 params.settings.set(args);
6 }); 10 });
7}; 11};
diff --git a/src/environment.js b/src/environment.js
index e185120c0..e1762129b 100644
--- a/src/environment.js
+++ b/src/environment.js
@@ -4,11 +4,17 @@ export const isDevMode = Boolean(process.execPath.match(/[\\/]electron/));
4export const useLiveAPI = process.env.LIVE_API; 4export const useLiveAPI = process.env.LIVE_API;
5export const useLocalAPI = process.env.LOCAL_API; 5export const useLocalAPI = process.env.LOCAL_API;
6 6
7export const isMac = process.platform === 'darwin'; 7let platform = process.platform;
8export const isWindows = process.platform === 'win32'; 8if (process.env.OS_PLATFORM) {
9export const isLinux = process.platform === 'linux'; 9 platform = process.env.OS_PLATFORM;
10}
11
12export const isMac = platform === 'darwin';
13export const isWindows = platform === 'win32';
14export const isLinux = platform === 'linux';
10 15
11export const ctrlKey = isMac ? '⌘' : 'Ctrl'; 16export const ctrlKey = isMac ? '⌘' : 'Ctrl';
17export const cmdKey = isMac ? 'Cmd' : 'Ctrl';
12 18
13let api; 19let api;
14if (!isDevMode || (isDevMode && useLiveAPI)) { 20if (!isDevMode || (isDevMode && useLiveAPI)) {
diff --git a/src/helpers/validation-helpers.js b/src/helpers/validation-helpers.js
index a8a242d54..2f762437d 100644
--- a/src/helpers/validation-helpers.js
+++ b/src/helpers/validation-helpers.js
@@ -1,6 +1,31 @@
1import { defineMessages } from 'react-intl';
2
3const messages = defineMessages({
4 required: {
5 id: 'validation.required',
6 defaultMessage: '!!!Field is required',
7 },
8 email: {
9 id: 'validation.email',
10 defaultMessage: '!!!Email not valid',
11 },
12 url: {
13 id: 'validation.url',
14 defaultMessage: '!!!Not a valid URL',
15 },
16 minLength: {
17 id: 'validation.minLength',
18 defaultMessage: '!!!Too few characters',
19 },
20 oneRequired: {
21 id: 'validation.oneRequired',
22 defaultMessage: '!!!At least one is required',
23 },
24});
25
1export function required({ field }) { 26export function required({ field }) {
2 const isValid = (field.value.trim() !== ''); 27 const isValid = (field.value.trim() !== '');
3 return [isValid, `${field.label} is required`]; 28 return [isValid, window.franz.intl.formatMessage(messages.required, { field: field.label })];
4} 29}
5 30
6export function email({ field }) { 31export function email({ field }) {
@@ -13,7 +38,7 @@ export function email({ field }) {
13 isValid = true; 38 isValid = true;
14 } 39 }
15 40
16 return [isValid, `${field.label} not valid`]; 41 return [isValid, window.franz.intl.formatMessage(messages.email, { field: field.label })];
17} 42}
18 43
19export function url({ field }) { 44export function url({ field }) {
@@ -27,7 +52,7 @@ export function url({ field }) {
27 isValid = true; 52 isValid = true;
28 } 53 }
29 54
30 return [isValid, `${field.label} is not a valid url`]; 55 return [isValid, window.franz.intl.formatMessage(messages.url, { field: field.label })];
31} 56}
32 57
33export function minLength(length) { 58export function minLength(length) {
@@ -36,13 +61,13 @@ export function minLength(length) {
36 if (field.touched) { 61 if (field.touched) {
37 isValid = field.value.length >= length; 62 isValid = field.value.length >= length;
38 } 63 }
39 return [isValid, `${field.label} should be at least ${length} characters long.`]; 64 return [isValid, window.franz.intl.formatMessage(messages.minLength, { field: field.label, length })];
40 }; 65 };
41} 66}
42 67
43export function oneRequired(targets) { 68export function oneRequired(targets) {
44 return ({ field, form }) => { 69 return ({ field, form }) => {
45 const invalidFields = targets.filter(target => form.$(target).value === ''); 70 const invalidFields = targets.filter(target => form.$(target).value === '');
46 return [targets.length !== invalidFields.length, `${field.label} is required`]; 71 return [targets.length !== invalidFields.length, window.franz.intl.formatMessage(messages.required, { field: field.label })];
47 }; 72 };
48} 73}
diff --git a/src/i18n/locales/ca.json b/src/i18n/locales/ca.json
index 524dfe0fb..4ea3eb1a7 100644
--- a/src/i18n/locales/ca.json
+++ b/src/i18n/locales/ca.json
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "La teva sessió ha caducat, torna a iniciar la sessió.", 26 "login.serverLogout" : "La teva sessió ha caducat, torna a iniciar la sessió.",
27 "login.submit.label" : "Accedir", 27 "login.submit.label" : "Accedir",
28 "login.tokenExpired" : "La teva sessió ha caducat, torna a iniciar la sessió.", 28 "login.tokenExpired" : "La teva sessió ha caducat, torna a iniciar la sessió.",
29 "menu.app.about" : "Sobre Franz",
30 "menu.app.hide" : "Amaga",
31 "menu.app.hideOthers" : "Amaga altres",
32 "menu.app.quit" : "Surt",
33 "menu.app.settings" : "Configuració",
34 "menu.app.unhide" : "Mostra",
35 "menu.edit" : "Edita",
36 "menu.edit.copy" : "Copia",
37 "menu.edit.cut" : "Retalla",
38 "menu.edit.delete" : "Suprimeix",
39 "menu.edit.emojiSymbols" : "Emoji i Símbols",
40 "menu.edit.paste" : "Enganxa",
41 "menu.edit.pasteAndMatchStyle" : "Enganxa amb els estils",
42 "menu.edit.redo" : "Refés",
43 "menu.edit.selectAll" : "Selecciona-ho tot",
44 "menu.edit.speech" : "Pronuncia",
45 "menu.edit.startDictation" : "Inicia el dictat",
46 "menu.edit.startSpeaking" : "Inicia la parla",
47 "menu.edit.stopSpeaking" : "Atura la parla",
48 "menu.edit.undo" : "Desfés",
49 "menu.file" : "Fitxer",
50 "menu.help" : "Ajuda",
51 "menu.help.changelog" : "Registre de canvis",
52 "menu.help.learnMore" : "Conegueu-ne més detalls",
53 "menu.help.privacy" : "Declaració de privacitat",
54 "menu.help.support" : "Suport",
55 "menu.help.tos" : "Condicions del Servei",
56 "menu.services" : "Serveis",
57 "menu.services.addNewService" : "Afegeix un servei...",
58 "menu.view" : "Visualitza",
59 "menu.view.enterFullScreen" : "Inicia la pantalla completa",
60 "menu.view.exitFullScreen" : "Surt de pantalla completa",
61 "menu.view.reloadFranz" : "Torna a carregar Franz",
62 "menu.view.reloadService" : "Torna a carregar el servei",
63 "menu.view.resetZoom" : "Mida real",
64 "menu.view.toggleDevTools" : "Activa serveis per a desenvolupadors",
65 "menu.view.toggleFullScreen" : "Activa la pantalla completa",
66 "menu.view.toggleServiceDevTools" : "Habilita les eines de desenvolupador de serveis",
67 "menu.view.zoomIn" : "Amplia",
68 "menu.view.zoomOut" : "Redueix",
69 "menu.window" : "Finestra",
70 "menu.window.close" : "Tanca",
71 "menu.window.minimize" : "Minimitza",
29 "password.email.label" : "Correu electrònic", 72 "password.email.label" : "Correu electrònic",
30 "password.headline" : "Restablir contrasenya", 73 "password.headline" : "Restablir contrasenya",
31 "password.link.login" : "Inicia la sessió al teu compte", 74 "password.link.login" : "Inicia la sessió al teu compte",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "Obrir en segon plà", 116 "settings.app.form.autoLaunchInBackground" : "Obrir en segon plà",
74 "settings.app.form.autoLaunchOnStart" : "Iniciar Franz a l'inici", 117 "settings.app.form.autoLaunchOnStart" : "Iniciar Franz a l'inici",
75 "settings.app.form.beta" : "Inclou versions beta", 118 "settings.app.form.beta" : "Inclou versions beta",
119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
76 "settings.app.form.enableMenuBar" : "Mostra Franz a la barra de menú", 120 "settings.app.form.enableMenuBar" : "Mostra Franz a la barra de menú",
77 "settings.app.form.enableSpellchecking" : "Habilita la comprobació ortogràfica", 121 "settings.app.form.enableSpellchecking" : "Habilita la comprobació ortogràfica",
78 "settings.app.form.enableSystemTray" : "Mostra Franz a la safata del sistema", 122 "settings.app.form.enableSystemTray" : "Mostra Franz a la safata del sistema",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Activa les notificacions", 241 "tabs.item.enableNotification" : "Activa les notificacions",
198 "tabs.item.enableService" : "Activa el servei", 242 "tabs.item.enableService" : "Activa el servei",
199 "tabs.item.reload" : "Recarrega", 243 "tabs.item.reload" : "Recarrega",
244 "validation.email" : "{field} no es vàlid",
245 "validation.minLength" : "{field} ha de ser al menys {length} caràcters de llargada",
246 "validation.required" : "{field} es requerit",
247 "validation.url" : "{field} es una URL no vàlida",
200 "welcome.loginButton" : "Inicia sessió al teu compte", 248 "welcome.loginButton" : "Inicia sessió al teu compte",
201 "welcome.signupButton" : "Crea un compte gratuït", 249 "welcome.signupButton" : "Crea un compte gratuït",
202 "welcome.slogan" : "Crea un compte gratuït" 250 "welcome.slogan" : "Crea un compte gratuït"
diff --git a/src/i18n/locales/cs.json b/src/i18n/locales/cs.json
index 62eb4a1ee..ed4c7a468 100644
--- a/src/i18n/locales/cs.json
+++ b/src/i18n/locales/cs.json
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "Sezení vypršelo, je třeba se znovu přihlásit.", 26 "login.serverLogout" : "Sezení vypršelo, je třeba se znovu přihlásit.",
27 "login.submit.label" : "Přihlásit se", 27 "login.submit.label" : "Přihlásit se",
28 "login.tokenExpired" : "Sezení vypršelo, prosím přihlaste se znovu", 28 "login.tokenExpired" : "Sezení vypršelo, prosím přihlaste se znovu",
29 "menu.app.about" : "O aplikaci",
30 "menu.app.hide" : "Skrýt",
31 "menu.app.hideOthers" : "Skryt ostatní",
32 "menu.app.quit" : "UkonÄit",
33 "menu.app.settings" : "Nastavení",
34 "menu.app.unhide" : "Odkrýt",
35 "menu.edit" : "Upravit",
36 "menu.edit.copy" : "Kopírovat",
37 "menu.edit.cut" : "Vyjmout",
38 "menu.edit.delete" : "Smazat",
39 "menu.edit.emojiSymbols" : "Emoji a znaky",
40 "menu.edit.paste" : "Vložit",
41 "menu.edit.pasteAndMatchStyle" : "Vložit vÄetnÄ› formátování",
42 "menu.edit.redo" : "Vpřed",
43 "menu.edit.selectAll" : "Vybrat vše",
44 "menu.edit.speech" : "Mluvené slovo",
45 "menu.edit.startDictation" : "ZaÄít diktovat",
46 "menu.edit.startSpeaking" : "ZaÄít mluvit",
47 "menu.edit.stopSpeaking" : "Přestat mluvit",
48 "menu.edit.undo" : "Vrátit zpět",
49 "menu.file" : "Soubor",
50 "menu.help" : "Nápověda",
51 "menu.help.changelog" : "Seznam změn",
52 "menu.help.learnMore" : "Dozvědět se více",
53 "menu.help.privacy" : "Prohlášení o ochraně soukromí",
54 "menu.help.support" : "Podpora",
55 "menu.help.tos" : "Podmínky použití",
56 "menu.services" : "Služby",
57 "menu.services.addNewService" : "Přidat novou službu...",
58 "menu.view" : "Zobrazení",
59 "menu.view.enterFullScreen" : "Spustit režim celé obrazovky",
60 "menu.view.exitFullScreen" : "UkonÄit celoobrazovkový režim",
61 "menu.view.reloadFranz" : "Obnovit Franze",
62 "menu.view.reloadService" : "Obnovit službu",
63 "menu.view.resetZoom" : "SkuteÄná velikost",
64 "menu.view.toggleDevTools" : "Zobrazit Vývojářské nástroje",
65 "menu.view.toggleFullScreen" : "Přepnout na celou obrazovku",
66 "menu.view.toggleServiceDevTools" : "Zobrazit Vývojářské nástroje pro službu",
67 "menu.view.zoomIn" : "Přiblížit",
68 "menu.view.zoomOut" : "Oddálit",
69 "menu.window" : "Okno",
70 "menu.window.close" : "Zavřít",
71 "menu.window.minimize" : "Minimalizovat",
29 "password.email.label" : "E-mailová adresa", 72 "password.email.label" : "E-mailová adresa",
30 "password.headline" : "Obnovit heslo", 73 "password.headline" : "Obnovit heslo",
31 "password.link.login" : "PÅ™ihlásit se k úÄtu", 74 "password.link.login" : "PÅ™ihlásit se k úÄtu",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "Spustit na pozadí", 116 "settings.app.form.autoLaunchInBackground" : "Spustit na pozadí",
74 "settings.app.form.autoLaunchOnStart" : "Spustit Franz při startu", 117 "settings.app.form.autoLaunchOnStart" : "Spustit Franz při startu",
75 "settings.app.form.beta" : "Zahrnout beta verze", 118 "settings.app.form.beta" : "Zahrnout beta verze",
119 "settings.app.form.enableGPUAcceleration" : "Aktivovat GPU zrychlení",
76 "settings.app.form.enableMenuBar" : "Zobraz Franz v Menu nabídce", 120 "settings.app.form.enableMenuBar" : "Zobraz Franz v Menu nabídce",
77 "settings.app.form.enableSpellchecking" : "Zapnout kontrolu pravopisu", 121 "settings.app.form.enableSpellchecking" : "Zapnout kontrolu pravopisu",
78 "settings.app.form.enableSystemTray" : "Zobrazit Franz v systémové liště", 122 "settings.app.form.enableSystemTray" : "Zobrazit Franz v systémové liště",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Povolit oznamování", 241 "tabs.item.enableNotification" : "Povolit oznamování",
198 "tabs.item.enableService" : "Zapnout službu", 242 "tabs.item.enableService" : "Zapnout službu",
199 "tabs.item.reload" : "Obnovit", 243 "tabs.item.reload" : "Obnovit",
244 "validation.email" : "{field} není validní",
245 "validation.minLength" : "{field} musí být alespoň {length} znaků dlouhé",
246 "validation.required" : "{field} je povinné",
247 "validation.url" : "{field} není validní URL",
200 "welcome.loginButton" : "PÅ™ihlášení do vaÅ¡eho úÄtu", 248 "welcome.loginButton" : "PÅ™ihlášení do vaÅ¡eho úÄtu",
201 "welcome.signupButton" : "VytvoÅ™it úÄet zdarma", 249 "welcome.signupButton" : "VytvoÅ™it úÄet zdarma",
202 "welcome.slogan" : "Zprávy, které pracují pro vás" 250 "welcome.slogan" : "Zprávy, které pracují pro vás"
diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json
index 7a03cce87..fa61d26d4 100644
--- a/src/i18n/locales/de.json
+++ b/src/i18n/locales/de.json
@@ -6,7 +6,7 @@
6 "import.skip.label" : "Ich möchte Dienste selbst hinzufügen", 6 "import.skip.label" : "Ich möchte Dienste selbst hinzufügen",
7 "import.submit.label" : "Dienste importieren", 7 "import.submit.label" : "Dienste importieren",
8 "infobar.buttonChangelog" : "Was gibt es neues?", 8 "infobar.buttonChangelog" : "Was gibt es neues?",
9 "infobar.buttonInstallUpdate" : "Neustarten & Update installieren", 9 "infobar.buttonInstallUpdate" : "Neu starten & Update installieren",
10 "infobar.buttonReloadServices" : "Dienste neuladen", 10 "infobar.buttonReloadServices" : "Dienste neuladen",
11 "infobar.requiredRequestsFailed" : "Dienste und Benutzerinformationen konnten nicht geladen werden", 11 "infobar.requiredRequestsFailed" : "Dienste und Benutzerinformationen konnten nicht geladen werden",
12 "infobar.servicesUpdated" : "Deine Dienste wurden aktualisiert.", 12 "infobar.servicesUpdated" : "Deine Dienste wurden aktualisiert.",
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "Deine Sitzung ist abgelaufen, bitte melde dich erneut an.", 26 "login.serverLogout" : "Deine Sitzung ist abgelaufen, bitte melde dich erneut an.",
27 "login.submit.label" : "Anmelden", 27 "login.submit.label" : "Anmelden",
28 "login.tokenExpired" : "Deine Sitzung ist abgelaufen, bitte melde dich erneut an.", 28 "login.tokenExpired" : "Deine Sitzung ist abgelaufen, bitte melde dich erneut an.",
29 "menu.app.about" : "Ãœber Franz",
30 "menu.app.hide" : "Franz ausblenden",
31 "menu.app.hideOthers" : "Andere ausblenden",
32 "menu.app.quit" : "Franz Beenden",
33 "menu.app.settings" : "Einstellungen",
34 "menu.app.unhide" : "Alle einblenden",
35 "menu.edit" : "Bearbeiten",
36 "menu.edit.copy" : "Kopieren",
37 "menu.edit.cut" : "Ausschneiden",
38 "menu.edit.delete" : "Löschen",
39 "menu.edit.emojiSymbols" : "Emoji & Symbole",
40 "menu.edit.paste" : "Einfügen",
41 "menu.edit.pasteAndMatchStyle" : "Einfügen und Stil anpassen",
42 "menu.edit.redo" : "Wiederholen",
43 "menu.edit.selectAll" : "Alle auswählen",
44 "menu.edit.speech" : "Sprachausgabe",
45 "menu.edit.startDictation" : "Diktat starten ...",
46 "menu.edit.startSpeaking" : "Sprachausgabe starten",
47 "menu.edit.stopSpeaking" : "Sprachausgabe stoppen",
48 "menu.edit.undo" : "Widerrufen",
49 "menu.file" : "Datei",
50 "menu.help" : "Hilfe",
51 "menu.help.changelog" : "Changelog",
52 "menu.help.learnMore" : "Mehr erfahren",
53 "menu.help.privacy" : "Datenschutzerklärung",
54 "menu.help.support" : "Hilfe",
55 "menu.help.tos" : "Nutzungsbedingungen",
56 "menu.services" : "Dienste",
57 "menu.services.addNewService" : "Dienst hinzufügen",
58 "menu.view" : "Darstellung",
59 "menu.view.enterFullScreen" : "Vollbildmodus",
60 "menu.view.exitFullScreen" : "Vollbildmodus aus",
61 "menu.view.reloadFranz" : "Franz neu laden",
62 "menu.view.reloadService" : "Dienst neu laden",
63 "menu.view.resetZoom" : "Originalgröße",
64 "menu.view.toggleDevTools" : "Entwicklertools anzeigen",
65 "menu.view.toggleFullScreen" : "Vollbildmodus umschalten",
66 "menu.view.toggleServiceDevTools" : "Entwicklertools für Dienst anzeigen",
67 "menu.view.zoomIn" : "Vergrößern",
68 "menu.view.zoomOut" : "Verkleinern",
69 "menu.window" : "Fenster",
70 "menu.window.close" : "Schließen",
71 "menu.window.minimize" : "Minimieren",
29 "password.email.label" : "E-Mail Adresse", 72 "password.email.label" : "E-Mail Adresse",
30 "password.headline" : "Passwort zurücksetzen", 73 "password.headline" : "Passwort zurücksetzen",
31 "password.link.login" : "An deinem Konto anmelden", 74 "password.link.login" : "An deinem Konto anmelden",
@@ -66,13 +109,14 @@
66 "settings.account.tryReloadUserInfoRequest" : "Erneut versuchen", 109 "settings.account.tryReloadUserInfoRequest" : "Erneut versuchen",
67 "settings.account.userInfoRequestFailed" : "Benutzerinformationen konnten nicht geladen werden", 110 "settings.account.userInfoRequestFailed" : "Benutzerinformationen konnten nicht geladen werden",
68 "settings.app.buttonClearAllCache" : "Cache leeren", 111 "settings.app.buttonClearAllCache" : "Cache leeren",
69 "settings.app.buttonInstallUpdate" : "Neustarten & Update installieren", 112 "settings.app.buttonInstallUpdate" : "Neu starten & Update installieren",
70 "settings.app.buttonSearchForUpdate" : "Nach Updates suchen", 113 "settings.app.buttonSearchForUpdate" : "Nach Updates suchen",
71 "settings.app.cacheInfo" : "Der Franz Cache belegt derzeit {size}.", 114 "settings.app.cacheInfo" : "Der Franz Cache belegt derzeit {size}.",
72 "settings.app.currentVersion" : "Aktuelle Version:", 115 "settings.app.currentVersion" : "Aktuelle Version:",
73 "settings.app.form.autoLaunchInBackground" : "Im Hintergrund öffnen", 116 "settings.app.form.autoLaunchInBackground" : "Im Hintergrund öffnen",
74 "settings.app.form.autoLaunchOnStart" : "Franz beim Systemstart ausführen", 117 "settings.app.form.autoLaunchOnStart" : "Franz beim Systemstart ausführen",
75 "settings.app.form.beta" : "Beta-Versionen einbeziehen", 118 "settings.app.form.beta" : "Beta-Versionen einbeziehen",
119 "settings.app.form.enableGPUAcceleration" : "Hardwarebeschleunigung aktivieren",
76 "settings.app.form.enableMenuBar" : "Franz in Menüleiste anzeigen", 120 "settings.app.form.enableMenuBar" : "Franz in Menüleiste anzeigen",
77 "settings.app.form.enableSpellchecking" : "Rechtschreibprüfung aktivieren", 121 "settings.app.form.enableSpellchecking" : "Rechtschreibprüfung aktivieren",
78 "settings.app.form.enableSystemTray" : "Franz in der Systemleiste anzeigen", 122 "settings.app.form.enableSystemTray" : "Franz in der Systemleiste anzeigen",
@@ -157,7 +201,7 @@
157 "settings.user.form.firstname" : "Vorname", 201 "settings.user.form.firstname" : "Vorname",
158 "settings.user.form.lastname" : "Nachname", 202 "settings.user.form.lastname" : "Nachname",
159 "settings.user.form.newPassword" : "Neues Passwort", 203 "settings.user.form.newPassword" : "Neues Passwort",
160 "sidebar.addNewService" : "Neuem Dienst hinzufügen", 204 "sidebar.addNewService" : "Neuen Dienst hinzufügen",
161 "sidebar.muteApp" : "Benachrichtigungen & Audio deaktivieren", 205 "sidebar.muteApp" : "Benachrichtigungen & Audio deaktivieren",
162 "sidebar.settings" : "Einstellungen", 206 "sidebar.settings" : "Einstellungen",
163 "sidebar.unmuteApp" : "Benachrichtigungen & Audio aktivieren", 207 "sidebar.unmuteApp" : "Benachrichtigungen & Audio aktivieren",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Benachrichtigungen aktivieren", 241 "tabs.item.enableNotification" : "Benachrichtigungen aktivieren",
198 "tabs.item.enableService" : "Dienst aktivieren", 242 "tabs.item.enableService" : "Dienst aktivieren",
199 "tabs.item.reload" : "Neuladen", 243 "tabs.item.reload" : "Neuladen",
244 "validation.email" : "{field} ist ungültig",
245 "validation.minLength" : "{field} muss mindestens {length} Zeichen enthalten",
246 "validation.required" : "{field} wird benötigt",
247 "validation.url" : "{field} ist keine gültige URL",
200 "welcome.loginButton" : "Bei Franz einloggen", 248 "welcome.loginButton" : "Bei Franz einloggen",
201 "welcome.signupButton" : "Kostenloses Konto erstellen", 249 "welcome.signupButton" : "Kostenloses Konto erstellen",
202 "welcome.slogan" : "Kommunikation, die für dich funktioniert" 250 "welcome.slogan" : "Kommunikation, die für dich funktioniert"
diff --git a/src/i18n/locales/el.json b/src/i18n/locales/el.json
index b0c465940..7361d29b7 100644
--- a/src/i18n/locales/el.json
+++ b/src/i18n/locales/el.json
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "Η συνεδÏία σας έληξε, συνδεθείτε ξανά.", 26 "login.serverLogout" : "Η συνεδÏία σας έληξε, συνδεθείτε ξανά.",
27 "login.submit.label" : "ΣÏνδεση", 27 "login.submit.label" : "ΣÏνδεση",
28 "login.tokenExpired" : "Η συνεδÏία σας έληξε, συνδεθείτε ξανά.", 28 "login.tokenExpired" : "Η συνεδÏία σας έληξε, συνδεθείτε ξανά.",
29 "menu.app.about" : "About Franz",
30 "menu.app.hide" : "Hide",
31 "menu.app.hideOthers" : "Hide Others",
32 "menu.app.quit" : "Quit",
33 "menu.app.settings" : "Ρυθμίσεις",
34 "menu.app.unhide" : "Unhide",
35 "menu.edit" : "ΕπεξεÏγασία",
36 "menu.edit.copy" : "Copy",
37 "menu.edit.cut" : "Cut",
38 "menu.edit.delete" : "ΔιαγÏαφή",
39 "menu.edit.emojiSymbols" : "Emoji & Symbols",
40 "menu.edit.paste" : "Paste",
41 "menu.edit.pasteAndMatchStyle" : "Paste And Match Style",
42 "menu.edit.redo" : "Redo",
43 "menu.edit.selectAll" : "Select All",
44 "menu.edit.speech" : "Speech",
45 "menu.edit.startDictation" : "Start Dictation",
46 "menu.edit.startSpeaking" : "Start Speaking",
47 "menu.edit.stopSpeaking" : "Stop Speaking",
48 "menu.edit.undo" : "Undo",
49 "menu.file" : "File",
50 "menu.help" : "Help",
51 "menu.help.changelog" : "Changelog",
52 "menu.help.learnMore" : "Learn More",
53 "menu.help.privacy" : "Δήλωση αποÏÏήτου",
54 "menu.help.support" : "Support",
55 "menu.help.tos" : "Terms of Service",
56 "menu.services" : "Services",
57 "menu.services.addNewService" : "Add New Service...",
58 "menu.view" : "View",
59 "menu.view.enterFullScreen" : "Enter Full Screen",
60 "menu.view.exitFullScreen" : "Exit Full Screen",
61 "menu.view.reloadFranz" : "Reload Franz",
62 "menu.view.reloadService" : "Reload Service",
63 "menu.view.resetZoom" : "Actual Size",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Toggle Full Screen",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Zoom In",
68 "menu.view.zoomOut" : "Zoom Out",
69 "menu.window" : "Window",
70 "menu.window.close" : "Close",
71 "menu.window.minimize" : "Minimize",
29 "password.email.label" : "ΔιεÏθυνση ηλεκτÏÎ¿Î½Î¹ÎºÎ¿Ï Ï„Î±Ï‡Ï…Î´Ïομείου", 72 "password.email.label" : "ΔιεÏθυνση ηλεκτÏÎ¿Î½Î¹ÎºÎ¿Ï Ï„Î±Ï‡Ï…Î´Ïομείου",
30 "password.headline" : "ΕπαναφοÏά ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης", 73 "password.headline" : "ΕπαναφοÏά ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης",
31 "password.link.login" : "ΣÏνδεση στο λογαÏιασμό σας", 74 "password.link.login" : "ΣÏνδεση στο λογαÏιασμό σας",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "Άνοιγμα στο παÏασκήνιο", 116 "settings.app.form.autoLaunchInBackground" : "Άνοιγμα στο παÏασκήνιο",
74 "settings.app.form.autoLaunchOnStart" : "Εκκίνηση του Franz κατά την εκκίνηση του συστήματος", 117 "settings.app.form.autoLaunchOnStart" : "Εκκίνηση του Franz κατά την εκκίνηση του συστήματος",
75 "settings.app.form.beta" : "ΣυμπεÏιλάβετε εκδόσεις beta", 118 "settings.app.form.beta" : "ΣυμπεÏιλάβετε εκδόσεις beta",
119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
76 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar", 120 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar",
77 "settings.app.form.enableSpellchecking" : "ΕνεÏγοποίηση οÏθογÏÎ±Ï†Î¹ÎºÎ¿Ï ÎµÎ»Î­Î³Ï‡Î¿Ï…", 121 "settings.app.form.enableSpellchecking" : "ΕνεÏγοποίηση οÏθογÏÎ±Ï†Î¹ÎºÎ¿Ï ÎµÎ»Î­Î³Ï‡Î¿Ï…",
78 "settings.app.form.enableSystemTray" : "Εμφάνιση του Franz στη γÏαμμή ειδοποιήσεων", 122 "settings.app.form.enableSystemTray" : "Εμφάνιση του Franz στη γÏαμμή ειδοποιήσεων",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "ΕνεÏγοποίηση ειδοποιήσεων", 241 "tabs.item.enableNotification" : "ΕνεÏγοποίηση ειδοποιήσεων",
198 "tabs.item.enableService" : "ΕνεÏγοποίηση υπηÏεσίας", 242 "tabs.item.enableService" : "ΕνεÏγοποίηση υπηÏεσίας",
199 "tabs.item.reload" : "ΕπαναφόÏτωση", 243 "tabs.item.reload" : "ΕπαναφόÏτωση",
244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
200 "welcome.loginButton" : "ΣÏνδεση στο λογαÏιασμό σας", 248 "welcome.loginButton" : "ΣÏνδεση στο λογαÏιασμό σας",
201 "welcome.signupButton" : "ΔημιουÏγία δωÏεάν λογαÏιασμοÏ", 249 "welcome.signupButton" : "ΔημιουÏγία δωÏεάν λογαÏιασμοÏ",
202 "welcome.slogan" : "Επικοινωνία που λειτουÏγεί για εσάς" 250 "welcome.slogan" : "Επικοινωνία που λειτουÏγεί για εσάς"
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index d5c0ea441..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.",
@@ -158,12 +159,14 @@
158 "settings.app.form.autoLaunchOnStart": "Launch Franz on start", 159 "settings.app.form.autoLaunchOnStart": "Launch Franz on start",
159 "settings.app.form.autoLaunchInBackground": "Open in background", 160 "settings.app.form.autoLaunchInBackground": "Open in background",
160 "settings.app.form.enableSystemTray": "Show Franz in system tray", 161 "settings.app.form.enableSystemTray": "Show Franz in system tray",
162 "settings.app.form.darkMode": "Join the Dark Side",
161 "settings.app.form.minimizeToSystemTray": "Minimize Franz to system tray", 163 "settings.app.form.minimizeToSystemTray": "Minimize Franz to system tray",
162 "settings.app.form.enableMenuBar": "Show Franz in Menu Bar", 164 "settings.app.form.enableMenuBar": "Show Franz in Menu Bar",
163 "settings.app.form.hideDockIcon": "Hide Franz icon in Dock", 165 "settings.app.form.hideDockIcon": "Hide Franz icon in Dock",
164 "settings.app.form.runInBackground": "Keep Franz in background when closing the window", 166 "settings.app.form.runInBackground": "Keep Franz in background when closing the window",
165 "settings.app.form.language": "Language", 167 "settings.app.form.language": "Language",
166 "settings.app.form.enableSpellchecking": "Enable spell checking", 168 "settings.app.form.enableSpellchecking": "Enable spell checking",
169 "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration",
167 "settings.app.form.showDisabledServices": "Display disabled services tabs", 170 "settings.app.form.showDisabledServices": "Display disabled services tabs",
168 "settings.app.form.showMessagesBadgesWhenMuted": "Show unread message badge when notifications are disabled", 171 "settings.app.form.showMessagesBadgesWhenMuted": "Show unread message badge when notifications are disabled",
169 "settings.app.form.beta": "Include beta versions", 172 "settings.app.form.beta": "Include beta versions",
@@ -199,5 +202,52 @@
199 "service.crashHandler.action": "Reload {name}", 202 "service.crashHandler.action": "Reload {name}",
200 "service.crashHandler.autoReload": "Trying to automatically restore {name} in {seconds} seconds", 203 "service.crashHandler.autoReload": "Trying to automatically restore {name} in {seconds} seconds",
201 "service.disabledHandler.headline": "{name} is disabled", 204 "service.disabledHandler.headline": "{name} is disabled",
202 "service.disabledHandler.action": "Enable {name}" 205 "service.disabledHandler.action": "Enable {name}",
206 "menu.edit": "Edit",
207 "menu.edit.undo": "Undo",
208 "menu.edit.redo": "Redo",
209 "menu.edit.cut": "Cut",
210 "menu.edit.copy": "Copy",
211 "menu.edit.paste": "Paste",
212 "menu.edit.pasteAndMatchStyle": "Paste And Match Style",
213 "menu.edit.delete": "Delete",
214 "menu.edit.selectAll": "Select All",
215 "menu.edit.speech": "Speech",
216 "menu.edit.startSpeaking": "Start Speaking",
217 "menu.edit.stopSpeaking": "Stop Speaking",
218 "menu.edit.startDictation": "Start Dictation",
219 "menu.edit.emojiSymbols": "Emoji & Symbols",
220 "menu.view.resetZoom": "Actual Size",
221 "menu.view.zoomIn": "Zoom In",
222 "menu.view.zoomOut": "Zoom Out",
223 "menu.view.enterFullScreen": "Enter Full Screen",
224 "menu.view.exitFullScreen": "Exit Full Screen",
225 "menu.view.toggleFullScreen": "Toggle Full Screen",
226 "menu.view.toggleDevTools": "Toggle Developer Tools",
227 "menu.view.toggleServiceDevTools": "Toggle Service Developer Tools",
228 "menu.view.reloadService": "Reload Service",
229 "menu.view.reloadFranz": "Reload Franz",
230 "menu.window.minimize": "Minimize",
231 "menu.window.close": "Close",
232 "menu.help.learnMore": "Learn More",
233 "menu.help.changelog": "Changelog",
234 "menu.help.support": "Support",
235 "menu.help.tos": "Terms of Service",
236 "menu.help.privacy": "Privacy Statement",
237 "menu.file": "File",
238 "menu.view": "View",
239 "menu.services": "Services",
240 "menu.window": "Window",
241 "menu.help": "Help",
242 "menu.app.about": "About Franz",
243 "menu.app.settings": "Settings",
244 "menu.app.hide": "Hide",
245 "menu.app.hideOthers": "Hide Others",
246 "menu.app.unhide": "Unhide",
247 "menu.app.quit": "Quit",
248 "menu.services.addNewService": "Add New Service...",
249 "validation.required": "{field} is required",
250 "validation.email": "{field} is not valid",
251 "validation.url": "{field} is not a valid URL",
252 "validation.minLength": "{field} should be at least {length} characters long"
203} 253}
diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json
index 407266285..75047a613 100644
--- a/src/i18n/locales/es.json
+++ b/src/i18n/locales/es.json
@@ -1,6 +1,6 @@
1{ 1{
2 "global.api.unhealthy" : "No se puede conectar a los servicios en línea de Franz.", 2 "global.api.unhealthy" : "No se puede conectar a los servicios en línea de Franz",
3 "global.notConnectedToTheInternet" : "No estás conectado a Internet", 3 "global.notConnectedToTheInternet" : "No está conectado a Internet.",
4 "import.headline" : "Importa tus servicios de Franz 4", 4 "import.headline" : "Importa tus servicios de Franz 4",
5 "import.notSupportedHeadline" : "Servicios no soportados aún en Franz 5", 5 "import.notSupportedHeadline" : "Servicios no soportados aún en Franz 5",
6 "import.skip.label" : "Quiero agregar servicios manualmente", 6 "import.skip.label" : "Quiero agregar servicios manualmente",
@@ -12,11 +12,11 @@
12 "infobar.servicesUpdated" : "Tus servicios han sido actualizados.", 12 "infobar.servicesUpdated" : "Tus servicios han sido actualizados.",
13 "infobar.updateAvailable" : "Una nueva actualización de Franz está disponible", 13 "infobar.updateAvailable" : "Una nueva actualización de Franz está disponible",
14 "invite.email.label" : "Dirección de correo electrónico", 14 "invite.email.label" : "Dirección de correo electrónico",
15 "invite.headline.friends" : "Invita a 3 de tus amigos o colegas", 15 "invite.headline.friends" : "Invita a 3 de tus amigos o compañeros",
16 "invite.name.label" : "Nombre", 16 "invite.name.label" : "Nombre",
17 "invite.skip.label" : "Quiero hacer esto después", 17 "invite.skip.label" : "Quiero hacer esto después",
18 "invite.submit.label" : "Enviar invitaciones", 18 "invite.submit.label" : "Enviar invitaciones",
19 "invite.successInfo" : "Invitaciones enviadas", 19 "invite.successInfo" : "Invitaciones enviadas correctamente",
20 "login.email.label" : "Dirección de correo electrónico", 20 "login.email.label" : "Dirección de correo electrónico",
21 "login.headline" : "Iniciar sesión", 21 "login.headline" : "Iniciar sesión",
22 "login.invalidCredentials" : "Correo electrónico o contraseña no válidos ", 22 "login.invalidCredentials" : "Correo electrónico o contraseña no válidos ",
@@ -25,7 +25,50 @@
25 "login.password.label" : "Contraseña", 25 "login.password.label" : "Contraseña",
26 "login.serverLogout" : "Tu sesión ha expirado, por favor inicia la sesión de nuevo.", 26 "login.serverLogout" : "Tu sesión ha expirado, por favor inicia la sesión de nuevo.",
27 "login.submit.label" : "Iniciar sesión", 27 "login.submit.label" : "Iniciar sesión",
28 "login.tokenExpired" : "Tu sesión expiró, por favor la inicia sesión nuevamente.", 28 "login.tokenExpired" : "Tu sesión expiró, por favor inicia sesión otra vez.",
29 "menu.app.about" : "Sobre Franz",
30 "menu.app.hide" : "Ocultar",
31 "menu.app.hideOthers" : "Ocultar otros",
32 "menu.app.quit" : "Salir",
33 "menu.app.settings" : "Configuración",
34 "menu.app.unhide" : "Mostrar",
35 "menu.edit" : "Editar",
36 "menu.edit.copy" : "Copiar",
37 "menu.edit.cut" : "Cortar",
38 "menu.edit.delete" : "Borrar",
39 "menu.edit.emojiSymbols" : "Emoji y Símbolos",
40 "menu.edit.paste" : "Pegar",
41 "menu.edit.pasteAndMatchStyle" : "Pegar con el mismo estilo",
42 "menu.edit.redo" : "Rehacer",
43 "menu.edit.selectAll" : "Seleccionar todo",
44 "menu.edit.speech" : "Leer",
45 "menu.edit.startDictation" : "Empezar dictado",
46 "menu.edit.startSpeaking" : "Empezar lectura",
47 "menu.edit.stopSpeaking" : "Detener lectura",
48 "menu.edit.undo" : "Deshacer",
49 "menu.file" : "Archivo",
50 "menu.help" : "Ayuda",
51 "menu.help.changelog" : "Registro de cambios",
52 "menu.help.learnMore" : "Conocer más",
53 "menu.help.privacy" : "Declaración de privacidad",
54 "menu.help.support" : "Soporte",
55 "menu.help.tos" : "Términos del Servicio",
56 "menu.services" : "Servicios",
57 "menu.services.addNewService" : "Añadir Nuevo Servicio...",
58 "menu.view" : "Mostrar",
59 "menu.view.enterFullScreen" : "Entrar a Pantalla Completa",
60 "menu.view.exitFullScreen" : "Salir de Pantalla Completa",
61 "menu.view.reloadFranz" : "Recargar Franz",
62 "menu.view.reloadService" : "Recargar Servicio",
63 "menu.view.resetZoom" : "Tamaño Actual",
64 "menu.view.toggleDevTools" : "Activar las Herramientas para Desarrolladores",
65 "menu.view.toggleFullScreen" : "Cambiar a Pantalla Completa",
66 "menu.view.toggleServiceDevTools" : "Mostrar Herramientas de Servicios para Desarrolladores",
67 "menu.view.zoomIn" : "Ampliar",
68 "menu.view.zoomOut" : "Reducir",
69 "menu.window" : "Ventana",
70 "menu.window.close" : "Cerrar",
71 "menu.window.minimize" : "Minimizar",
29 "password.email.label" : "Dirección de correo electrónico", 72 "password.email.label" : "Dirección de correo electrónico",
30 "password.headline" : "Restablecer contraseña", 73 "password.headline" : "Restablecer contraseña",
31 "password.link.login" : "Iniciar sesión en tu cuenta", 74 "password.link.login" : "Iniciar sesión en tu cuenta",
@@ -73,10 +116,11 @@
73 "settings.app.form.autoLaunchInBackground" : "Abrir en segundo plano", 116 "settings.app.form.autoLaunchInBackground" : "Abrir en segundo plano",
74 "settings.app.form.autoLaunchOnStart" : "Iniciar Franz al iniciar", 117 "settings.app.form.autoLaunchOnStart" : "Iniciar Franz al iniciar",
75 "settings.app.form.beta" : "Incluir versiones beta", 118 "settings.app.form.beta" : "Incluir versiones beta",
119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
76 "settings.app.form.enableMenuBar" : "mostrar a franz en La barra de menús", 120 "settings.app.form.enableMenuBar" : "mostrar a franz en La barra de menús",
77 "settings.app.form.enableSpellchecking" : "Activar corrección ortográfica", 121 "settings.app.form.enableSpellchecking" : "Activar corrección ortográfica",
78 "settings.app.form.enableSystemTray" : "Mostrar Franz en la bandeja del sistema", 122 "settings.app.form.enableSystemTray" : "Mostrar Franz en la bandeja del sistema",
79 "settings.app.form.hideDockIcon" : "Hide Franz icon in Dock", 123 "settings.app.form.hideDockIcon" : "Ocultar Icono de Franz en barra de herramientas",
80 "settings.app.form.language" : "Idioma", 124 "settings.app.form.language" : "Idioma",
81 "settings.app.form.minimizeToSystemTray" : "Minimizar Franz a la bandeja del sistema", 125 "settings.app.form.minimizeToSystemTray" : "Minimizar Franz a la bandeja del sistema",
82 "settings.app.form.runInBackground" : "Mantener Franz en segundo plano al cerrar la ventana", 126 "settings.app.form.runInBackground" : "Mantener Franz en segundo plano al cerrar la ventana",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Activar notificaciones", 241 "tabs.item.enableNotification" : "Activar notificaciones",
198 "tabs.item.enableService" : "Activar servicio", 242 "tabs.item.enableService" : "Activar servicio",
199 "tabs.item.reload" : "Recargar", 243 "tabs.item.reload" : "Recargar",
244 "validation.email" : "{field} no es válido",
245 "validation.minLength" : "{field} debería tener al menos {length} caracteres",
246 "validation.required" : "{field} es obligatorio",
247 "validation.url" : "{field} no es una URL válida",
200 "welcome.loginButton" : "Accede a tu cuenta", 248 "welcome.loginButton" : "Accede a tu cuenta",
201 "welcome.signupButton" : "Crear una cuenta gratuita", 249 "welcome.signupButton" : "Crear una cuenta gratuita",
202 "welcome.slogan" : "Mensajería que funciona para ti" 250 "welcome.slogan" : "Mensajería que funciona para ti"
diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json
index 35a55fcc2..e00a5671b 100644
--- a/src/i18n/locales/fr.json
+++ b/src/i18n/locales/fr.json
@@ -1,6 +1,6 @@
1{ 1{
2 "global.api.unhealthy" : "Impossible de se connecter aux services en ligne de Franz.", 2 "global.api.unhealthy" : "Impossible de se connecter aux services en ligne de Franz",
3 "global.notConnectedToTheInternet" : "Vous n'êtes pas connecté(e) à Internet.", 3 "global.notConnectedToTheInternet" : "Vous n'êtes pas connecté à Internet.",
4 "import.headline" : "Importez vos services depuis la version 4 de Franz.", 4 "import.headline" : "Importez vos services depuis la version 4 de Franz.",
5 "import.notSupportedHeadline" : "Ces services ne sont pas encore supportés par Franz 5", 5 "import.notSupportedHeadline" : "Ces services ne sont pas encore supportés par Franz 5",
6 "import.skip.label" : "Je veux ajouter des services manuellement", 6 "import.skip.label" : "Je veux ajouter des services manuellement",
@@ -16,7 +16,7 @@
16 "invite.name.label" : "Nom", 16 "invite.name.label" : "Nom",
17 "invite.skip.label" : "Je veux faire cela plus tard", 17 "invite.skip.label" : "Je veux faire cela plus tard",
18 "invite.submit.label" : "Envoyer des invitations", 18 "invite.submit.label" : "Envoyer des invitations",
19 "invite.successInfo" : "Invitations envoyées.", 19 "invite.successInfo" : "Invitations envoyées",
20 "login.email.label" : "Adresse e-mail", 20 "login.email.label" : "Adresse e-mail",
21 "login.headline" : "S'inscrire", 21 "login.headline" : "S'inscrire",
22 "login.invalidCredentials" : "E-mail ou mot de passe invalide", 22 "login.invalidCredentials" : "E-mail ou mot de passe invalide",
@@ -26,11 +26,54 @@
26 "login.serverLogout" : "Votre session a expiré. Reconnectez-vous s'il vous plaît.", 26 "login.serverLogout" : "Votre session a expiré. Reconnectez-vous s'il vous plaît.",
27 "login.submit.label" : "Connexion", 27 "login.submit.label" : "Connexion",
28 "login.tokenExpired" : "Votre session a expiré, veuillez vous reconnecter.", 28 "login.tokenExpired" : "Votre session a expiré, veuillez vous reconnecter.",
29 "menu.app.about" : "À propos de Franz",
30 "menu.app.hide" : "Masquer",
31 "menu.app.hideOthers" : "Masquer les autres",
32 "menu.app.quit" : "Quitter",
33 "menu.app.settings" : "Paramètres",
34 "menu.app.unhide" : "Afficher",
35 "menu.edit" : "Éditer",
36 "menu.edit.copy" : "Copier",
37 "menu.edit.cut" : "Couper",
38 "menu.edit.delete" : "Supprimer",
39 "menu.edit.emojiSymbols" : "Emoji & Symboles",
40 "menu.edit.paste" : "Coller",
41 "menu.edit.pasteAndMatchStyle" : "Coller et fusionner le style",
42 "menu.edit.redo" : "Rétablir",
43 "menu.edit.selectAll" : "Tout sélectionner",
44 "menu.edit.speech" : "Synthèse vocale",
45 "menu.edit.startDictation" : "Démarrer la synthèse vocale",
46 "menu.edit.startSpeaking" : "Démarrer la lecture",
47 "menu.edit.stopSpeaking" : "Arrêter la synthèse vocale",
48 "menu.edit.undo" : "Annuler",
49 "menu.file" : "Fichier",
50 "menu.help" : "Aide",
51 "menu.help.changelog" : "Liste des modifications",
52 "menu.help.learnMore" : "En savoir plus",
53 "menu.help.privacy" : "Déclaration de confidentialité",
54 "menu.help.support" : "Support",
55 "menu.help.tos" : "Conditions d'utilisation",
56 "menu.services" : "Services",
57 "menu.services.addNewService" : "Ajouter un nouveau service...",
58 "menu.view" : "Affichage",
59 "menu.view.enterFullScreen" : "Entrer en mode plein écran",
60 "menu.view.exitFullScreen" : "Sortir du mode plein écran",
61 "menu.view.reloadFranz" : "Actualiser Franz",
62 "menu.view.reloadService" : "Redémarrer le service",
63 "menu.view.resetZoom" : "Taille réelle",
64 "menu.view.toggleDevTools" : "Activer les outils développeur",
65 "menu.view.toggleFullScreen" : "Activer \/ désactiver le mode plein écran",
66 "menu.view.toggleServiceDevTools" : "Activer les outils de développement WebKit",
67 "menu.view.zoomIn" : "Zoom Avant",
68 "menu.view.zoomOut" : "Zoom Arrière",
69 "menu.window" : "Fenêtre",
70 "menu.window.close" : "Fermer",
71 "menu.window.minimize" : "Réduire",
29 "password.email.label" : "Adresse e-mail", 72 "password.email.label" : "Adresse e-mail",
30 "password.headline" : "Réinitialiser le mot de passe", 73 "password.headline" : "Réinitialiser le mot de passe",
31 "password.link.login" : "Connectez-vous à votre compte", 74 "password.link.login" : "Connectez-vous à votre compte",
32 "password.link.signup" : "Créer un compte gratuit.", 75 "password.link.signup" : "Créer un compte gratuit.",
33 "password.noUser" : "Aucun utilisateur n'a été trouvé avec cette adresse e-mail ", 76 "password.noUser" : "Aucun utilisateur n'a été trouvé avec cette adresse e-mail",
34 "password.submit.label" : "Soumettre", 77 "password.submit.label" : "Soumettre",
35 "password.successInfo" : "Merci de consulter vos e-mails", 78 "password.successInfo" : "Merci de consulter vos e-mails",
36 "pricing.headline" : "Soutenez Franz", 79 "pricing.headline" : "Soutenez Franz",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "Ouvrir en arrière-plan", 116 "settings.app.form.autoLaunchInBackground" : "Ouvrir en arrière-plan",
74 "settings.app.form.autoLaunchOnStart" : "Lancer Franz au démarrage", 117 "settings.app.form.autoLaunchOnStart" : "Lancer Franz au démarrage",
75 "settings.app.form.beta" : "Accepter les versions bêta", 118 "settings.app.form.beta" : "Accepter les versions bêta",
119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
76 "settings.app.form.enableMenuBar" : "Afficher Franz dans la barre des menus", 120 "settings.app.form.enableMenuBar" : "Afficher Franz dans la barre des menus",
77 "settings.app.form.enableSpellchecking" : "Activer la vérification orthographique", 121 "settings.app.form.enableSpellchecking" : "Activer la vérification orthographique",
78 "settings.app.form.enableSystemTray" : "Afficher Franz dans la barre d'état système", 122 "settings.app.form.enableSystemTray" : "Afficher Franz dans la barre d'état système",
@@ -94,7 +138,7 @@
94 "settings.app.updateStatusAvailable" : "Mise à jour disponible, téléchargement en cours...", 138 "settings.app.updateStatusAvailable" : "Mise à jour disponible, téléchargement en cours...",
95 "settings.app.updateStatusSearching" : "Recherche d'une mise à jour", 139 "settings.app.updateStatusSearching" : "Recherche d'une mise à jour",
96 "settings.app.updateStatusUpToDate" : "Vous utilisez la dernière version de Franz", 140 "settings.app.updateStatusUpToDate" : "Vous utilisez la dernière version de Franz",
97 "settings.invite.headline" : "Invite 3 amis", 141 "settings.invite.headline" : "Invite des amis",
98 "settings.navigation.account" : "Compte", 142 "settings.navigation.account" : "Compte",
99 "settings.navigation.availableServices" : "Services disponibles", 143 "settings.navigation.availableServices" : "Services disponibles",
100 "settings.navigation.inviteFriends" : "Inviter des amis", 144 "settings.navigation.inviteFriends" : "Inviter des amis",
@@ -138,7 +182,7 @@
138 "settings.service.form.tabHosted" : "Hébergé", 182 "settings.service.form.tabHosted" : "Hébergé",
139 "settings.service.form.tabOnPremise" : "Auto-hébergé â­ï¸", 183 "settings.service.form.tabOnPremise" : "Auto-hébergé â­ï¸",
140 "settings.service.form.team" : "Équipe", 184 "settings.service.form.team" : "Équipe",
141 "settings.service.form.useHostedService" : "Utilisez le service hébergé {nom}.", 185 "settings.service.form.useHostedService" : "Utilisez le service hébergé {name}.",
142 "settings.service.form.yourServices" : "Vos services", 186 "settings.service.form.yourServices" : "Vos services",
143 "settings.services.deletedInfo" : "Le service a été supprimé", 187 "settings.services.deletedInfo" : "Le service a été supprimé",
144 "settings.services.discoverServices" : "Découvrir les services", 188 "settings.services.discoverServices" : "Découvrir les services",
@@ -174,30 +218,34 @@
174 "signup.password.label" : "Mot de passe", 218 "signup.password.label" : "Mot de passe",
175 "signup.submit.label" : "Créer un compte", 219 "signup.submit.label" : "Créer un compte",
176 "subscription.euTaxInfo" : "Résidents de l'UE : une taxe locale peut s'appliquer", 220 "subscription.euTaxInfo" : "Résidents de l'UE : une taxe locale peut s'appliquer",
177 "subscription.features.ads" : "No ads, ever!", 221 "subscription.features.ads" : "Plus de pubs !",
178 "subscription.features.comingSoon" : "coming soon", 222 "subscription.features.comingSoon" : "Bientôt disponible",
179 "subscription.features.customServices" : "Private services for you and your team", 223 "subscription.features.customServices" : "Services privés pour vous et votre équipe",
180 "subscription.features.encryptedSync" : "Encrypted session synchronization", 224 "subscription.features.encryptedSync" : "Synchronisation de session cryptée",
181 "subscription.features.onpremise" : "Ajouter des services locaux\/hébergés comme HipChat", 225 "subscription.features.onpremise" : "Ajouter des services locaux\/hébergés comme HipChat",
182 "subscription.features.vpn" : "Support des Proxy et VPN", 226 "subscription.features.vpn" : "Support des Proxy et VPN",
183 "subscription.includedFeatures" : "Le compte payant Supporter Premium Franz inclut", 227 "subscription.includedFeatures" : "Le compte payant Supporter Premium Franz inclut",
184 "subscription.paymentSessionError" : "Could not initialize payment form", 228 "subscription.paymentSessionError" : "Initialisation du paiement impossible",
185 "subscription.submit.label" : "I want to support the development of Franz", 229 "subscription.submit.label" : "Je souhaite aider au développement de Franz",
186 "subscription.type.free" : "free", 230 "subscription.type.free" : "gratuit",
187 "subscription.type.month" : "month", 231 "subscription.type.month" : "mois",
188 "subscription.type.year" : "year", 232 "subscription.type.year" : "année",
189 "subscriptionPopup.buttonCancel" : "Cancel", 233 "subscriptionPopup.buttonCancel" : "Annuler",
190 "subscriptionPopup.buttonDone" : "Done", 234 "subscriptionPopup.buttonDone" : "Terminé",
191 "tabs.item.deleteService" : "Delete service", 235 "tabs.item.deleteService" : "Supprimer le service",
192 "tabs.item.disableAudio" : "Désactiver l'audio", 236 "tabs.item.disableAudio" : "Désactiver l'audio",
193 "tabs.item.disableNotifications" : "Disable notifications", 237 "tabs.item.disableNotifications" : "Désactiver les notifications",
194 "tabs.item.disableService" : "Disable service", 238 "tabs.item.disableService" : "Désactiver le service",
195 "tabs.item.edit" : "Edit", 239 "tabs.item.edit" : "Éditer",
196 "tabs.item.enableAudio" : "Activer l'audio", 240 "tabs.item.enableAudio" : "Activer l'audio",
197 "tabs.item.enableNotification" : "Enable notifications", 241 "tabs.item.enableNotification" : "Activer les notifications",
198 "tabs.item.enableService" : "Activer le service", 242 "tabs.item.enableService" : "Activer le service",
199 "tabs.item.reload" : "Reload", 243 "tabs.item.reload" : "Recharger",
200 "welcome.loginButton" : "Login to your account", 244 "validation.email" : "{field} n'est pas valide",
201 "welcome.signupButton" : "Create a free account", 245 "validation.minLength" : "{field} doit contenir au moins {length} caractère(s)",
246 "validation.required" : "{field} est requis",
247 "validation.url" : "{field} n'est pas une URL valide",
248 "welcome.loginButton" : "Se connecter sur son compte",
249 "welcome.signupButton" : "Créer un compte gratuit",
202 "welcome.slogan" : "Une messagerie qui fonctionne pour vous" 250 "welcome.slogan" : "Une messagerie qui fonctionne pour vous"
203} 251}
diff --git a/src/i18n/locales/ga.json b/src/i18n/locales/ga.json
index 13b251755..1c0b537b2 100644
--- a/src/i18n/locales/ga.json
+++ b/src/i18n/locales/ga.json
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "D'éag do sheisiún, logáil isteach arís le do thoil.", 26 "login.serverLogout" : "D'éag do sheisiún, logáil isteach arís le do thoil.",
27 "login.submit.label" : "Logáil isteach", 27 "login.submit.label" : "Logáil isteach",
28 "login.tokenExpired" : "D'éag do sheisiún, logáil isteach arís le do thoil.", 28 "login.tokenExpired" : "D'éag do sheisiún, logáil isteach arís le do thoil.",
29 "menu.app.about" : "Faoi Franz",
30 "menu.app.hide" : "Cuir Franz i bhfolach",
31 "menu.app.hideOthers" : "Folaigh feidhmchláir eile",
32 "menu.app.quit" : "Scoir",
33 "menu.app.settings" : "Socruithe",
34 "menu.app.unhide" : "Taispeáin",
35 "menu.edit" : "Cuir in eagar",
36 "menu.edit.copy" : "Cóipeáil",
37 "menu.edit.cut" : "Gearr",
38 "menu.edit.delete" : "Scrios",
39 "menu.edit.emojiSymbols" : "Straoiseoga ⊠siombailí",
40 "menu.edit.paste" : "Greamaigh",
41 "menu.edit.pasteAndMatchStyle" : "Greamaigh agus cuir stíl in oiriúint",
42 "menu.edit.redo" : "Athdhéan",
43 "menu.edit.selectAll" : "Roghnaigh gach rud",
44 "menu.edit.speech" : "Caint",
45 "menu.edit.startDictation" : "Tosaigh deachtú",
46 "menu.edit.startSpeaking" : "Tosaigh ag caint",
47 "menu.edit.stopSpeaking" : "Stad ag caint",
48 "menu.edit.undo" : "Cealaigh",
49 "menu.file" : "Comhad",
50 "menu.help" : "Cabhair",
51 "menu.help.changelog" : "Athruithe",
52 "menu.help.learnMore" : "Tuilleadh eolais",
53 "menu.help.privacy" : "Ráiteas phríobháideachais",
54 "menu.help.support" : "Tacaíocht",
55 "menu.help.tos" : "Téarmaí tagartha",
56 "menu.services" : "Seirbhísí",
57 "menu.services.addNewService" : "Cuir seirbhís nua leis",
58 "menu.view" : "Amharc",
59 "menu.view.enterFullScreen" : "Cuir isteach mód lánscáileáin",
60 "menu.view.exitFullScreen" : "Scoir mód lánscáileáin",
61 "menu.view.reloadFranz" : "Athlódáil Franz",
62 "menu.view.reloadService" : "Athlódáil seirbhís",
63 "menu.view.resetZoom" : "Fíormhéid",
64 "menu.view.toggleDevTools" : "Scoránaigh uirlis forbróra",
65 "menu.view.toggleFullScreen" : "Scoránaigh mód lánscáileáin",
66 "menu.view.toggleServiceDevTools" : "Scoránaigh uirlis forbróra seirbhíse",
67 "menu.view.zoomIn" : "Zúmáil isteach",
68 "menu.view.zoomOut" : "Zúmáil amach",
69 "menu.window" : "Fuinneog",
70 "menu.window.close" : "Dún",
71 "menu.window.minimize" : "Ãoslaghdaigh",
29 "password.email.label" : "Seoladh ríomhphoist", 72 "password.email.label" : "Seoladh ríomhphoist",
30 "password.headline" : "Athshocraigh pasfhocal", 73 "password.headline" : "Athshocraigh pasfhocal",
31 "password.link.login" : "Logáil isteach i do chuntas", 74 "password.link.login" : "Logáil isteach i do chuntas",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "Oscail sa chúlra", 116 "settings.app.form.autoLaunchInBackground" : "Oscail sa chúlra",
74 "settings.app.form.autoLaunchOnStart" : "Láinseáil Franz ón tús", 117 "settings.app.form.autoLaunchOnStart" : "Láinseáil Franz ón tús",
75 "settings.app.form.beta" : "Cuir leagain béite san áireamh", 118 "settings.app.form.beta" : "Cuir leagain béite san áireamh",
119 "settings.app.form.enableGPUAcceleration" : "Cumasaigh luasghéarú APG",
76 "settings.app.form.enableMenuBar" : "Taispeáin Franz sa bharra roghchláir", 120 "settings.app.form.enableMenuBar" : "Taispeáin Franz sa bharra roghchláir",
77 "settings.app.form.enableSpellchecking" : "Cumasaigh seiceáil litrithe", 121 "settings.app.form.enableSpellchecking" : "Cumasaigh seiceáil litrithe",
78 "settings.app.form.enableSystemTray" : "Taispeáin Franz i dtráidire an chórais", 122 "settings.app.form.enableSystemTray" : "Taispeáin Franz i dtráidire an chórais",
@@ -168,7 +212,7 @@
168 "signup.headline" : "Cláraigh", 212 "signup.headline" : "Cláraigh",
169 "signup.lastname.label" : "Sloinne", 213 "signup.lastname.label" : "Sloinne",
170 "signup.legal.info" : "Agus tú ag cruthú cuntas Franz glacann tú le", 214 "signup.legal.info" : "Agus tú ag cruthú cuntas Franz glacann tú le",
171 "signup.legal.privacy" : "Ráiteas phríobháideachais", 215 "signup.legal.privacy" : "Polasaí príobháideachais",
172 "signup.legal.terms" : "Tearmaí seirbhíse", 216 "signup.legal.terms" : "Tearmaí seirbhíse",
173 "signup.link.login" : "Cuntas agat cheana féin, logáil isteach?", 217 "signup.link.login" : "Cuntas agat cheana féin, logáil isteach?",
174 "signup.password.label" : "Pasfhocal", 218 "signup.password.label" : "Pasfhocal",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Cumasaigh fógraí", 241 "tabs.item.enableNotification" : "Cumasaigh fógraí",
198 "tabs.item.enableService" : "Cumasaigh seirbhís", 242 "tabs.item.enableService" : "Cumasaigh seirbhís",
199 "tabs.item.reload" : "Athlódáil", 243 "tabs.item.reload" : "Athlódáil",
244 "validation.email" : "Níl {field} neamhbhailí",
245 "validation.minLength" : "Ba cheart go mbeadh {field} ar a laghad {length} charactar fada",
246 "validation.required" : "Tá {field} de dhíth",
247 "validation.url" : "Ní AAA bhailí é {field}",
200 "welcome.loginButton" : "Logáil isteach i do chuntas", 248 "welcome.loginButton" : "Logáil isteach i do chuntas",
201 "welcome.signupButton" : "Cruthaigh cuntas nua", 249 "welcome.signupButton" : "Cruthaigh cuntas nua",
202 "welcome.slogan" : "Teachtaireachtaí a oibríonn duitse" 250 "welcome.slogan" : "Teachtaireachtaí a oibríonn duitse"
diff --git a/src/i18n/locales/hr.json b/src/i18n/locales/hr.json
index 97bfc19f4..e227c4537 100644
--- a/src/i18n/locales/hr.json
+++ b/src/i18n/locales/hr.json
@@ -16,7 +16,7 @@
16 "invite.name.label" : "Ime", 16 "invite.name.label" : "Ime",
17 "invite.skip.label" : "Želim da ovo uradim kasnije", 17 "invite.skip.label" : "Želim da ovo uradim kasnije",
18 "invite.submit.label" : "Pošalji pozivnice", 18 "invite.submit.label" : "Pošalji pozivnice",
19 "invite.successInfo" : "Invitations sent successfully", 19 "invite.successInfo" : "Pozivnice uspješno poslane",
20 "login.email.label" : "Vaša e-adresa:", 20 "login.email.label" : "Vaša e-adresa:",
21 "login.headline" : "Prijavite se", 21 "login.headline" : "Prijavite se",
22 "login.invalidCredentials" : "E-mail ili lozinka nisu toÄni", 22 "login.invalidCredentials" : "E-mail ili lozinka nisu toÄni",
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "Vaša sesija je istekla, prijavite se ponovo.", 26 "login.serverLogout" : "Vaša sesija je istekla, prijavite se ponovo.",
27 "login.submit.label" : "Prijavite se", 27 "login.submit.label" : "Prijavite se",
28 "login.tokenExpired" : "Vaša sesija je istekla, prijavite se ponovo.", 28 "login.tokenExpired" : "Vaša sesija je istekla, prijavite se ponovo.",
29 "menu.app.about" : "About Franz",
30 "menu.app.hide" : "Hide",
31 "menu.app.hideOthers" : "Hide Others",
32 "menu.app.quit" : "Quit",
33 "menu.app.settings" : "Postavke",
34 "menu.app.unhide" : "Unhide",
35 "menu.edit" : "Uredi",
36 "menu.edit.copy" : "Copy",
37 "menu.edit.cut" : "Cut",
38 "menu.edit.delete" : "Obriši",
39 "menu.edit.emojiSymbols" : "Emoji & Symbols",
40 "menu.edit.paste" : "Paste",
41 "menu.edit.pasteAndMatchStyle" : "Paste And Match Style",
42 "menu.edit.redo" : "Redo",
43 "menu.edit.selectAll" : "Select All",
44 "menu.edit.speech" : "Speech",
45 "menu.edit.startDictation" : "Start Dictation",
46 "menu.edit.startSpeaking" : "Start Speaking",
47 "menu.edit.stopSpeaking" : "Stop Speaking",
48 "menu.edit.undo" : "Undo",
49 "menu.file" : "File",
50 "menu.help" : "Help",
51 "menu.help.changelog" : "Changelog",
52 "menu.help.learnMore" : "Learn More",
53 "menu.help.privacy" : "Izjava o privatnosti ",
54 "menu.help.support" : "Support",
55 "menu.help.tos" : "Terms of Service",
56 "menu.services" : "Services",
57 "menu.services.addNewService" : "Add New Service...",
58 "menu.view" : "View",
59 "menu.view.enterFullScreen" : "Enter Full Screen",
60 "menu.view.exitFullScreen" : "Exit Full Screen",
61 "menu.view.reloadFranz" : "Reload Franz",
62 "menu.view.reloadService" : "Reload Service",
63 "menu.view.resetZoom" : "Actual Size",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Toggle Full Screen",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Zoom In",
68 "menu.view.zoomOut" : "Zoom Out",
69 "menu.window" : "Window",
70 "menu.window.close" : "Close",
71 "menu.window.minimize" : "Minimize",
29 "password.email.label" : "Vaša e-adresa", 72 "password.email.label" : "Vaša e-adresa",
30 "password.headline" : "Stvorite novu zaporku", 73 "password.headline" : "Stvorite novu zaporku",
31 "password.link.login" : "Prijavite se na VaÅ¡ raÄun", 74 "password.link.login" : "Prijavite se na VaÅ¡ raÄun",
@@ -68,12 +111,13 @@
68 "settings.app.buttonClearAllCache" : "OÄisti memoriju", 111 "settings.app.buttonClearAllCache" : "OÄisti memoriju",
69 "settings.app.buttonInstallUpdate" : "Ponovo pokreni i instaliraj ažuriranje", 112 "settings.app.buttonInstallUpdate" : "Ponovo pokreni i instaliraj ažuriranje",
70 "settings.app.buttonSearchForUpdate" : "Potraži ažuriranja", 113 "settings.app.buttonSearchForUpdate" : "Potraži ažuriranja",
71 "settings.app.cacheInfo" : "Franz cache is currently using {size} of disk space.", 114 "settings.app.cacheInfo" : "Franz predmemorija trenutno koristi {size} prostora na disku",
72 "settings.app.currentVersion" : "Trenutna verzija:", 115 "settings.app.currentVersion" : "Trenutna verzija:",
73 "settings.app.form.autoLaunchInBackground" : "Otvori u pozadini", 116 "settings.app.form.autoLaunchInBackground" : "Otvori u pozadini",
74 "settings.app.form.autoLaunchOnStart" : "Pokreni Franz sa sistemom", 117 "settings.app.form.autoLaunchOnStart" : "Pokreni Franz sa sistemom",
75 "settings.app.form.beta" : "Obuhvati i beta verzije", 118 "settings.app.form.beta" : "Obuhvati i beta verzije",
76 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar", 119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
120 "settings.app.form.enableMenuBar" : "Prikaži Franz u traci izbornika",
77 "settings.app.form.enableSpellchecking" : "Omogući provjeru pravopisa", 121 "settings.app.form.enableSpellchecking" : "Omogući provjeru pravopisa",
78 "settings.app.form.enableSystemTray" : "Prikaži aplikaciju u sustavskoj traci", 122 "settings.app.form.enableSystemTray" : "Prikaži aplikaciju u sustavskoj traci",
79 "settings.app.form.hideDockIcon" : "Hide Franz icon in Dock", 123 "settings.app.form.hideDockIcon" : "Hide Franz icon in Dock",
@@ -89,15 +133,15 @@
89 "settings.app.headlineLanguage" : "Jezik", 133 "settings.app.headlineLanguage" : "Jezik",
90 "settings.app.headlineUpdates" : "Nadogradnje", 134 "settings.app.headlineUpdates" : "Nadogradnje",
91 "settings.app.restartRequired" : "Promjene postavki zahtijevaju ponovni zagon", 135 "settings.app.restartRequired" : "Promjene postavki zahtijevaju ponovni zagon",
92 "settings.app.subheadlineCache" : "Cache", 136 "settings.app.subheadlineCache" : "Predmemorija",
93 "settings.app.translationHelp" : "Pomozite nam prevesti aplikaciju na Vaš jezik. ", 137 "settings.app.translationHelp" : "Pomozite nam prevesti aplikaciju na Vaš jezik. ",
94 "settings.app.updateStatusAvailable" : "Nadogradnja dostupna, preuzimanje...", 138 "settings.app.updateStatusAvailable" : "Nadogradnja dostupna, preuzimanje...",
95 "settings.app.updateStatusSearching" : "Traže se ažuriranja", 139 "settings.app.updateStatusSearching" : "Traže se ažuriranja",
96 "settings.app.updateStatusUpToDate" : "Koristite najnoviju verziju Franca.", 140 "settings.app.updateStatusUpToDate" : "Koristite najnoviju verziju Franca.",
97 "settings.invite.headline" : "Invite Friends", 141 "settings.invite.headline" : "Pozovi prijatelje",
98 "settings.navigation.account" : "RaÄun", 142 "settings.navigation.account" : "RaÄun",
99 "settings.navigation.availableServices" : "Dostupne usluge", 143 "settings.navigation.availableServices" : "Dostupne usluge",
100 "settings.navigation.inviteFriends" : "Invite Friends", 144 "settings.navigation.inviteFriends" : "Pozovi prijatelje",
101 "settings.navigation.logout" : "Odjava", 145 "settings.navigation.logout" : "Odjava",
102 "settings.navigation.settings" : "Postavke", 146 "settings.navigation.settings" : "Postavke",
103 "settings.navigation.yourServices" : "Vaše usluge", 147 "settings.navigation.yourServices" : "Vaše usluge",
@@ -108,7 +152,7 @@
108 "settings.recipes.mostPopular" : "Najpopularniji", 152 "settings.recipes.mostPopular" : "Najpopularniji",
109 "settings.recipes.nothingFound" : "Žao nam je, ali ne postoje usluge koje se poklapaju s onima koje tražite. ", 153 "settings.recipes.nothingFound" : "Žao nam je, ali ne postoje usluge koje se poklapaju s onima koje tražite. ",
110 "settings.recipes.servicesSuccessfulAddedInfo" : "Usluga uspješno dodana. ", 154 "settings.recipes.servicesSuccessfulAddedInfo" : "Usluga uspješno dodana. ",
111 "settings.searchService" : "Search service", 155 "settings.searchService" : "Potraži servis",
112 "settings.service.error.goBack" : "Nazad do servisa", 156 "settings.service.error.goBack" : "Nazad do servisa",
113 "settings.service.error.headline" : "Greška", 157 "settings.service.error.headline" : "Greška",
114 "settings.service.error.message" : "Nemoguće uÄitati sadržaj usluge. ", 158 "settings.service.error.message" : "Nemoguće uÄitati sadržaj usluge. ",
@@ -126,10 +170,10 @@
126 "settings.service.form.enableService" : "Omogućite usluge", 170 "settings.service.form.enableService" : "Omogućite usluge",
127 "settings.service.form.headlineBadges" : "Unread message badges", 171 "settings.service.form.headlineBadges" : "Unread message badges",
128 "settings.service.form.headlineGeneral" : "Općenito", 172 "settings.service.form.headlineGeneral" : "Općenito",
129 "settings.service.form.headlineNotifications" : "Notifications", 173 "settings.service.form.headlineNotifications" : "Obavijesti",
130 "settings.service.form.icon" : "Custom icon", 174 "settings.service.form.icon" : "Prilagođena ikona",
131 "settings.service.form.iconDelete" : "Delete", 175 "settings.service.form.iconDelete" : "Obriši",
132 "settings.service.form.iconUpload" : "Drop your image, or click here", 176 "settings.service.form.iconUpload" : "Ispusti sliku ili klikni ovdje",
133 "settings.service.form.indirectMessageInfo" : "Vi ćete biti obavješteni o svim novim porukama na kanalu, ne samo o @imenima, kanalima, @ovdje, ...", 177 "settings.service.form.indirectMessageInfo" : "Vi ćete biti obavješteni o svim novim porukama na kanalu, ne samo o @imenima, kanalima, @ovdje, ...",
134 "settings.service.form.indirectMessages" : "Prikaži znaÄku na svim novim porukuama", 178 "settings.service.form.indirectMessages" : "Prikaži znaÄku na svim novim porukuama",
135 "settings.service.form.isMutedInfo" : "Kada je onemogućeno, sve obavijesti, svi zvukovi i sva pozadinska podrÅ¡ka će biti neÄujna. ", 179 "settings.service.form.isMutedInfo" : "Kada je onemogućeno, sve obavijesti, svi zvukovi i sva pozadinska podrÅ¡ka će biti neÄujna. ",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Omogući obavijesti", 241 "tabs.item.enableNotification" : "Omogući obavijesti",
198 "tabs.item.enableService" : "Omogući usluge", 242 "tabs.item.enableService" : "Omogući usluge",
199 "tabs.item.reload" : "Ponovno uÄitavanje", 243 "tabs.item.reload" : "Ponovno uÄitavanje",
244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
200 "welcome.loginButton" : "Prijavite se na raÄun", 248 "welcome.loginButton" : "Prijavite se na raÄun",
201 "welcome.signupButton" : "Stvorite novi korisniÄki raÄun", 249 "welcome.signupButton" : "Stvorite novi korisniÄki raÄun",
202 "welcome.slogan" : "Poruke koje su stvorene za tebe" 250 "welcome.slogan" : "Poruke koje su stvorene za tebe"
diff --git a/src/i18n/locales/hu.json b/src/i18n/locales/hu.json
index 15bdd33f4..3571ad453 100644
--- a/src/i18n/locales/hu.json
+++ b/src/i18n/locales/hu.json
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "A munkamenet lejárt, kérlek lépj be újra.", 26 "login.serverLogout" : "A munkamenet lejárt, kérlek lépj be újra.",
27 "login.submit.label" : "Bejelentkezés", 27 "login.submit.label" : "Bejelentkezés",
28 "login.tokenExpired" : "A munkamenet lejárt, kérlek lépj be újra.", 28 "login.tokenExpired" : "A munkamenet lejárt, kérlek lépj be újra.",
29 "menu.app.about" : "About Franz",
30 "menu.app.hide" : "Hide",
31 "menu.app.hideOthers" : "Hide Others",
32 "menu.app.quit" : "Quit",
33 "menu.app.settings" : "Beállítások",
34 "menu.app.unhide" : "Unhide",
35 "menu.edit" : "Szerkesztés",
36 "menu.edit.copy" : "Copy",
37 "menu.edit.cut" : "Cut",
38 "menu.edit.delete" : "Törlés",
39 "menu.edit.emojiSymbols" : "Emoji & Symbols",
40 "menu.edit.paste" : "Paste",
41 "menu.edit.pasteAndMatchStyle" : "Paste And Match Style",
42 "menu.edit.redo" : "Redo",
43 "menu.edit.selectAll" : "Select All",
44 "menu.edit.speech" : "Speech",
45 "menu.edit.startDictation" : "Start Dictation",
46 "menu.edit.startSpeaking" : "Start Speaking",
47 "menu.edit.stopSpeaking" : "Stop Speaking",
48 "menu.edit.undo" : "Undo",
49 "menu.file" : "File",
50 "menu.help" : "Help",
51 "menu.help.changelog" : "Changelog",
52 "menu.help.learnMore" : "Learn More",
53 "menu.help.privacy" : "Adatvédelmi Nyilatkozatot",
54 "menu.help.support" : "Support",
55 "menu.help.tos" : "Terms of Service",
56 "menu.services" : "Services",
57 "menu.services.addNewService" : "Add New Service...",
58 "menu.view" : "View",
59 "menu.view.enterFullScreen" : "Enter Full Screen",
60 "menu.view.exitFullScreen" : "Exit Full Screen",
61 "menu.view.reloadFranz" : "Reload Franz",
62 "menu.view.reloadService" : "Reload Service",
63 "menu.view.resetZoom" : "Actual Size",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Toggle Full Screen",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Zoom In",
68 "menu.view.zoomOut" : "Zoom Out",
69 "menu.window" : "Window",
70 "menu.window.close" : "Close",
71 "menu.window.minimize" : "Minimize",
29 "password.email.label" : "Email cím", 72 "password.email.label" : "Email cím",
30 "password.headline" : "Jelszó visszaállítása", 73 "password.headline" : "Jelszó visszaállítása",
31 "password.link.login" : "Jelentkezz be a fiókodba", 74 "password.link.login" : "Jelentkezz be a fiókodba",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "Megnyitás háttérben", 116 "settings.app.form.autoLaunchInBackground" : "Megnyitás háttérben",
74 "settings.app.form.autoLaunchOnStart" : "Franz betöltése indításkor", 117 "settings.app.form.autoLaunchOnStart" : "Franz betöltése indításkor",
75 "settings.app.form.beta" : "Béta verziók keresése", 118 "settings.app.form.beta" : "Béta verziók keresése",
119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
76 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar", 120 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar",
77 "settings.app.form.enableSpellchecking" : "Helyesírás-ellenőrzés engedélyezése", 121 "settings.app.form.enableSpellchecking" : "Helyesírás-ellenőrzés engedélyezése",
78 "settings.app.form.enableSystemTray" : "Franz mutatása a tálcán", 122 "settings.app.form.enableSystemTray" : "Franz mutatása a tálcán",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Értesítések engedélyezése", 241 "tabs.item.enableNotification" : "Értesítések engedélyezése",
198 "tabs.item.enableService" : "Szolgáltatás engedélyezése", 242 "tabs.item.enableService" : "Szolgáltatás engedélyezése",
199 "tabs.item.reload" : "Újratöltés", 243 "tabs.item.reload" : "Újratöltés",
244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
200 "welcome.loginButton" : "Jelentkezz be a fiókodba", 248 "welcome.loginButton" : "Jelentkezz be a fiókodba",
201 "welcome.signupButton" : "Új fiók létrehozása", 249 "welcome.signupButton" : "Új fiók létrehozása",
202 "welcome.slogan" : "Üzenetküldés okosan" 250 "welcome.slogan" : "Üzenetküldés okosan"
diff --git a/src/i18n/locales/id.json b/src/i18n/locales/id.json
index 509c5ad9c..e6d671c1b 100644
--- a/src/i18n/locales/id.json
+++ b/src/i18n/locales/id.json
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "Sesi Anda telah berakhir, silakan masuk kembali.", 26 "login.serverLogout" : "Sesi Anda telah berakhir, silakan masuk kembali.",
27 "login.submit.label" : "Masuk", 27 "login.submit.label" : "Masuk",
28 "login.tokenExpired" : "Sesi Anda kedaluwarsa, silakan masuk kembali.", 28 "login.tokenExpired" : "Sesi Anda kedaluwarsa, silakan masuk kembali.",
29 "menu.app.about" : "About Franz",
30 "menu.app.hide" : "Hide",
31 "menu.app.hideOthers" : "Hide Others",
32 "menu.app.quit" : "Quit",
33 "menu.app.settings" : "Pengaturan",
34 "menu.app.unhide" : "Unhide",
35 "menu.edit" : "Edit",
36 "menu.edit.copy" : "Copy",
37 "menu.edit.cut" : "Cut",
38 "menu.edit.delete" : "Hapus",
39 "menu.edit.emojiSymbols" : "Emoji & Symbols",
40 "menu.edit.paste" : "Paste",
41 "menu.edit.pasteAndMatchStyle" : "Paste And Match Style",
42 "menu.edit.redo" : "Redo",
43 "menu.edit.selectAll" : "Select All",
44 "menu.edit.speech" : "Speech",
45 "menu.edit.startDictation" : "Start Dictation",
46 "menu.edit.startSpeaking" : "Start Speaking",
47 "menu.edit.stopSpeaking" : "Stop Speaking",
48 "menu.edit.undo" : "Undo",
49 "menu.file" : "File",
50 "menu.help" : "Help",
51 "menu.help.changelog" : "Changelog",
52 "menu.help.learnMore" : "Learn More",
53 "menu.help.privacy" : "Pernyataan Privasi",
54 "menu.help.support" : "Support",
55 "menu.help.tos" : "Terms of Service",
56 "menu.services" : "Services",
57 "menu.services.addNewService" : "Add New Service...",
58 "menu.view" : "View",
59 "menu.view.enterFullScreen" : "Enter Full Screen",
60 "menu.view.exitFullScreen" : "Exit Full Screen",
61 "menu.view.reloadFranz" : "Reload Franz",
62 "menu.view.reloadService" : "Reload Service",
63 "menu.view.resetZoom" : "Actual Size",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Toggle Full Screen",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Zoom In",
68 "menu.view.zoomOut" : "Zoom Out",
69 "menu.window" : "Window",
70 "menu.window.close" : "Close",
71 "menu.window.minimize" : "Minimize",
29 "password.email.label" : "Alamat email", 72 "password.email.label" : "Alamat email",
30 "password.headline" : "Setel ulang sandi", 73 "password.headline" : "Setel ulang sandi",
31 "password.link.login" : "Masuk ke akun Anda", 74 "password.link.login" : "Masuk ke akun Anda",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "Buka di latar belakang", 116 "settings.app.form.autoLaunchInBackground" : "Buka di latar belakang",
74 "settings.app.form.autoLaunchOnStart" : "Jalankan Franz saat komputer dimulai", 117 "settings.app.form.autoLaunchOnStart" : "Jalankan Franz saat komputer dimulai",
75 "settings.app.form.beta" : "Sertakan versi beta", 118 "settings.app.form.beta" : "Sertakan versi beta",
119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
76 "settings.app.form.enableMenuBar" : "Tampilkan Franz di Bilah Menu", 120 "settings.app.form.enableMenuBar" : "Tampilkan Franz di Bilah Menu",
77 "settings.app.form.enableSpellchecking" : "Aktifkan pemeriksaan ejaan", 121 "settings.app.form.enableSpellchecking" : "Aktifkan pemeriksaan ejaan",
78 "settings.app.form.enableSystemTray" : "Tampilkan Franz di baki sistem", 122 "settings.app.form.enableSystemTray" : "Tampilkan Franz di baki sistem",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Aktifkan pemberitahuan", 241 "tabs.item.enableNotification" : "Aktifkan pemberitahuan",
198 "tabs.item.enableService" : "Aktifkan layanan", 242 "tabs.item.enableService" : "Aktifkan layanan",
199 "tabs.item.reload" : "Muat Ulang", 243 "tabs.item.reload" : "Muat Ulang",
244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
200 "welcome.loginButton" : "Masuk ke akun Anda", 248 "welcome.loginButton" : "Masuk ke akun Anda",
201 "welcome.signupButton" : "Buat akun gratis", 249 "welcome.signupButton" : "Buat akun gratis",
202 "welcome.slogan" : "Perpesanan yang bekerja untuk Anda" 250 "welcome.slogan" : "Perpesanan yang bekerja untuk Anda"
diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json
index a1392afba..9d23a309b 100644
--- a/src/i18n/locales/it.json
+++ b/src/i18n/locales/it.json
@@ -6,7 +6,7 @@
6 "import.skip.label" : "Voglio aggiungere servizi manualmente", 6 "import.skip.label" : "Voglio aggiungere servizi manualmente",
7 "import.submit.label" : "Importa servizi", 7 "import.submit.label" : "Importa servizi",
8 "infobar.buttonChangelog" : "Cosa c'è di nuovo?", 8 "infobar.buttonChangelog" : "Cosa c'è di nuovo?",
9 "infobar.buttonInstallUpdate" : "Riavvia e installa gli aggiornamenti", 9 "infobar.buttonInstallUpdate" : "Riavvia e installa aggiornamento",
10 "infobar.buttonReloadServices" : "Ricarica servizi", 10 "infobar.buttonReloadServices" : "Ricarica servizi",
11 "infobar.requiredRequestsFailed" : "Impossibile caricare servizi e info utente", 11 "infobar.requiredRequestsFailed" : "Impossibile caricare servizi e info utente",
12 "infobar.servicesUpdated" : "I tuoi servizi sono stati aggiornati.", 12 "infobar.servicesUpdated" : "I tuoi servizi sono stati aggiornati.",
@@ -26,17 +26,60 @@
26 "login.serverLogout" : "La tua sessione è scaduta, accedi nuovamente.", 26 "login.serverLogout" : "La tua sessione è scaduta, accedi nuovamente.",
27 "login.submit.label" : "Accedi", 27 "login.submit.label" : "Accedi",
28 "login.tokenExpired" : "La tua sessione è scaduta, accedi nuovamente.", 28 "login.tokenExpired" : "La tua sessione è scaduta, accedi nuovamente.",
29 "menu.app.about" : "Info su Franz",
30 "menu.app.hide" : "Nascondi",
31 "menu.app.hideOthers" : "Nascondi Altri",
32 "menu.app.quit" : "Esci",
33 "menu.app.settings" : "Impostazioni",
34 "menu.app.unhide" : "Mostra",
35 "menu.edit" : "Modifica",
36 "menu.edit.copy" : "Copia",
37 "menu.edit.cut" : "Taglia",
38 "menu.edit.delete" : "Elimina",
39 "menu.edit.emojiSymbols" : "Emoji e Simboli",
40 "menu.edit.paste" : "Incolla",
41 "menu.edit.pasteAndMatchStyle" : "Incolla e Mantieni Stile",
42 "menu.edit.redo" : "Ripeti",
43 "menu.edit.selectAll" : "Seleziona Tutto",
44 "menu.edit.speech" : "Sintesi Vocale",
45 "menu.edit.startDictation" : "Avvia Dettatura",
46 "menu.edit.startSpeaking" : "Avvia Pronuncia",
47 "menu.edit.stopSpeaking" : "Interrompi Pronuncia",
48 "menu.edit.undo" : "Annulla",
49 "menu.file" : "File",
50 "menu.help" : "Aiuto",
51 "menu.help.changelog" : "Changelog",
52 "menu.help.learnMore" : "Maggiori Informazioni",
53 "menu.help.privacy" : "Informativa sulla Privacy",
54 "menu.help.support" : "Supporto",
55 "menu.help.tos" : "Termini di Servizio",
56 "menu.services" : "Servizi",
57 "menu.services.addNewService" : "Aggiungi Nuovo Servizio...",
58 "menu.view" : "Visualizza",
59 "menu.view.enterFullScreen" : "Visualizza a Tutto Schermo",
60 "menu.view.exitFullScreen" : "Modalità Finestra",
61 "menu.view.reloadFranz" : "Ricarica Franz",
62 "menu.view.reloadService" : "Ricarica Servizio",
63 "menu.view.resetZoom" : "Dimensione Attuale",
64 "menu.view.toggleDevTools" : "Attiva Strumenti Sviluppo",
65 "menu.view.toggleFullScreen" : "Passa a Schermo Intero",
66 "menu.view.toggleServiceDevTools" : "Attiva Strumenti per Sviluppatori di Servizi",
67 "menu.view.zoomIn" : "Aumenta Zoom",
68 "menu.view.zoomOut" : "Diminuisci Zoom",
69 "menu.window" : "Finestra",
70 "menu.window.close" : "Chiudi",
71 "menu.window.minimize" : "Minimizza",
29 "password.email.label" : "Indirizzo email", 72 "password.email.label" : "Indirizzo email",
30 "password.headline" : "Reimposta password", 73 "password.headline" : "Reimposta password",
31 "password.link.login" : "Accedi al tuo account", 74 "password.link.login" : "Accedi al tuo account",
32 "password.link.signup" : "Crea un account gratuito", 75 "password.link.signup" : "Crea un account gratuito",
33 "password.noUser" : "Non è stato trovato nessun utente con questo indirizzo e-mail", 76 "password.noUser" : "Non è stato trovato nessun utente con questo indirizzo e-mail",
34 "password.submit.label" : "Invia", 77 "password.submit.label" : "Invia",
35 "password.successInfo" : "Controlla la tua email", 78 "password.successInfo" : "Please check your email",
36 "pricing.headline" : "Supporta Franz", 79 "pricing.headline" : "Support Franz",
37 "pricing.link.skipPayment" : "Non voglio supportare lo sviluppo di Franz", 80 "pricing.link.skipPayment" : "Non voglio supportare lo sviluppo di Franz.",
38 "pricing.submit.label" : "Voglio supportare lo sviluppo di Franz", 81 "pricing.submit.label" : "Voglio supportare lo sviluppo di Franz",
39 "pricing.support.label" : "Seleziona il tuo piano di supporto", 82 "pricing.support.label" : "Select your support plan",
40 "service.crashHandler.action" : "Ricarica {name}", 83 "service.crashHandler.action" : "Ricarica {name}",
41 "service.crashHandler.autoReload" : "Tentativo di ripristino automatico di {name} in {seconds} secondi", 84 "service.crashHandler.autoReload" : "Tentativo di ripristino automatico di {name} in {seconds} secondi",
42 "service.crashHandler.headline" : "Oh no!", 85 "service.crashHandler.headline" : "Oh no!",
@@ -44,7 +87,7 @@
44 "service.disabledHandler.action" : "Attiva {name}", 87 "service.disabledHandler.action" : "Attiva {name}",
45 "service.disabledHandler.headline" : "{name} è disattivato", 88 "service.disabledHandler.headline" : "{name} è disattivato",
46 "services.getStarted" : "Iniziamo", 89 "services.getStarted" : "Iniziamo",
47 "services.welcome" : "Benvenuto in Franz", 90 "services.welcome" : "Welcome to Franz",
48 "settings.account.account.editButton" : "Modifica account", 91 "settings.account.account.editButton" : "Modifica account",
49 "settings.account.accountType.basic" : "Account Basic", 92 "settings.account.accountType.basic" : "Account Basic",
50 "settings.account.accountType.premium" : "Premium Supporter Account", 93 "settings.account.accountType.premium" : "Premium Supporter Account",
@@ -67,12 +110,13 @@
67 "settings.account.userInfoRequestFailed" : "Impossibile caricare informazioni utente.", 110 "settings.account.userInfoRequestFailed" : "Impossibile caricare informazioni utente.",
68 "settings.app.buttonClearAllCache" : "Svuota la cache", 111 "settings.app.buttonClearAllCache" : "Svuota la cache",
69 "settings.app.buttonInstallUpdate" : "Riavvia e installa l'aggiornamento", 112 "settings.app.buttonInstallUpdate" : "Riavvia e installa l'aggiornamento",
70 "settings.app.buttonSearchForUpdate" : "Controlla gli aggiornamenti", 113 "settings.app.buttonSearchForUpdate" : "Controlla aggiornamenti",
71 "settings.app.cacheInfo" : "Franz sta utilizzando {size} di spazio su disco.", 114 "settings.app.cacheInfo" : "Franz sta utilizzando {size} di spazio su disco.",
72 "settings.app.currentVersion" : "Versione attuale:", 115 "settings.app.currentVersion" : "Versione attuale:",
73 "settings.app.form.autoLaunchInBackground" : "Apri in background", 116 "settings.app.form.autoLaunchInBackground" : "Apri in background",
74 "settings.app.form.autoLaunchOnStart" : "Esegui Franz all'avvio", 117 "settings.app.form.autoLaunchOnStart" : "Esegui Franz all'avvio",
75 "settings.app.form.beta" : "Includi versioni beta", 118 "settings.app.form.beta" : "Includi versioni beta",
119 "settings.app.form.enableGPUAcceleration" : "Attiva Accelerazione GPU",
76 "settings.app.form.enableMenuBar" : "Mostra Franz nella Barra del Menu", 120 "settings.app.form.enableMenuBar" : "Mostra Franz nella Barra del Menu",
77 "settings.app.form.enableSpellchecking" : "Attiva controllo ortografico", 121 "settings.app.form.enableSpellchecking" : "Attiva controllo ortografico",
78 "settings.app.form.enableSystemTray" : "Mostra Franz nell'area di notifica", 122 "settings.app.form.enableSystemTray" : "Mostra Franz nell'area di notifica",
@@ -158,9 +202,9 @@
158 "settings.user.form.lastname" : "Cognome", 202 "settings.user.form.lastname" : "Cognome",
159 "settings.user.form.newPassword" : "Nuova password", 203 "settings.user.form.newPassword" : "Nuova password",
160 "sidebar.addNewService" : "Aggiungi un nuovo servizio", 204 "sidebar.addNewService" : "Aggiungi un nuovo servizio",
161 "sidebar.muteApp" : "Disattiva notifiche & audio", 205 "sidebar.muteApp" : "Disattiva notifiche e audio",
162 "sidebar.settings" : "Impostazioni", 206 "sidebar.settings" : "Impostazioni",
163 "sidebar.unmuteApp" : "Attiva notifiche & audio", 207 "sidebar.unmuteApp" : "Attiva notifiche e audio",
164 "signup.company.label" : "Società", 208 "signup.company.label" : "Società",
165 "signup.email.label" : "Indirizzo email", 209 "signup.email.label" : "Indirizzo email",
166 "signup.emailDuplicate" : "Esiste già un utente con lo stesso indirizzo email", 210 "signup.emailDuplicate" : "Esiste già un utente con lo stesso indirizzo email",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Attiva notifiche", 241 "tabs.item.enableNotification" : "Attiva notifiche",
198 "tabs.item.enableService" : "Attiva servizio", 242 "tabs.item.enableService" : "Attiva servizio",
199 "tabs.item.reload" : "Aggiorna", 243 "tabs.item.reload" : "Aggiorna",
244 "validation.email" : "{field} non valido",
245 "validation.minLength" : "{field} dovrebbe contenere almeno {length} caratteri",
246 "validation.required" : "{field} è necessario",
247 "validation.url" : "{field} non è un URL valido",
200 "welcome.loginButton" : "Accedi al tuo account", 248 "welcome.loginButton" : "Accedi al tuo account",
201 "welcome.signupButton" : "Crea un account gratuito", 249 "welcome.signupButton" : "Crea un account gratuito",
202 "welcome.slogan" : "Un sistema di messaggistica che funziona" 250 "welcome.slogan" : "Un sistema di messaggistica che funziona"
diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json
index 9fc0a211b..a7af10aba 100644
--- a/src/i18n/locales/ja.json
+++ b/src/i18n/locales/ja.json
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "セッションã®æœŸé™ãŒåˆ‡ã‚Œã¾ã—ãŸã€‚ログインã—ç›´ã—ã¦ä¸‹ã•ã„。", 26 "login.serverLogout" : "セッションã®æœŸé™ãŒåˆ‡ã‚Œã¾ã—ãŸã€‚ログインã—ç›´ã—ã¦ä¸‹ã•ã„。",
27 "login.submit.label" : "サインイン", 27 "login.submit.label" : "サインイン",
28 "login.tokenExpired" : "セッションã®æœŸé™ãŒåˆ‡ã‚Œã¾ã—ãŸã€‚ログインã—ç›´ã—ã¦ä¸‹ã•ã„。", 28 "login.tokenExpired" : "セッションã®æœŸé™ãŒåˆ‡ã‚Œã¾ã—ãŸã€‚ログインã—ç›´ã—ã¦ä¸‹ã•ã„。",
29 "menu.app.about" : "Franzã«ã¤ã„ã¦",
30 "menu.app.hide" : "éš ã™",
31 "menu.app.hideOthers" : "ä»–ã‚’éš ã™",
32 "menu.app.quit" : "終了",
33 "menu.app.settings" : "設定",
34 "menu.app.unhide" : "表示ã™ã‚‹",
35 "menu.edit" : "編集",
36 "menu.edit.copy" : "コピー",
37 "menu.edit.cut" : "切りå–ã‚Š",
38 "menu.edit.delete" : "削除",
39 "menu.edit.emojiSymbols" : "絵文字ã¨ã‚·ãƒ³ãƒœãƒ«",
40 "menu.edit.paste" : "貼り付ã‘",
41 "menu.edit.pasteAndMatchStyle" : "書å¼ã‚’統一ã—ã¦è²¼ã‚Šä»˜ã‘",
42 "menu.edit.redo" : "ã‚„ã‚Šç›´ã—",
43 "menu.edit.selectAll" : "å…¨ã¦é¸æŠž",
44 "menu.edit.speech" : "読ã¿ä¸Šã’",
45 "menu.edit.startDictation" : "音声入力を開始",
46 "menu.edit.startSpeaking" : "読ã¿ä¸Šã’を開始",
47 "menu.edit.stopSpeaking" : "読ã¿ä¸Šã’ã‚’åœæ­¢",
48 "menu.edit.undo" : "å…ƒã«æˆ»ã™",
49 "menu.file" : "ファイル",
50 "menu.help" : "ヘルプ",
51 "menu.help.changelog" : "更新履歴",
52 "menu.help.learnMore" : "詳細ã«ã¤ã„ã¦",
53 "menu.help.privacy" : "プライãƒã‚·ãƒ¼ã«ã¤ã„ã¦",
54 "menu.help.support" : "サãƒãƒ¼ãƒˆ",
55 "menu.help.tos" : "サービス利用è¦ç´„",
56 "menu.services" : "サービス",
57 "menu.services.addNewService" : "サービスを追加",
58 "menu.view" : "表示",
59 "menu.view.enterFullScreen" : "全画é¢è¡¨ç¤º",
60 "menu.view.exitFullScreen" : "全画é¢è¡¨ç¤ºã‚’終了ã™ã‚‹",
61 "menu.view.reloadFranz" : "å†èµ·å‹•",
62 "menu.view.reloadService" : "サービスをリロードã™ã‚‹",
63 "menu.view.resetZoom" : "å…ƒã®ã‚µã‚¤ã‚º",
64 "menu.view.toggleDevTools" : "開発者ツールを切り替ãˆ",
65 "menu.view.toggleFullScreen" : "全画é¢è¡¨ç¤ºã®åˆ‡ã‚Šæ›¿ãˆ",
66 "menu.view.toggleServiceDevTools" : "サービス開発者ツールを切り替ãˆ",
67 "menu.view.zoomIn" : "表示を拡大ã™ã‚‹",
68 "menu.view.zoomOut" : "表示を縮å°ã™ã‚‹",
69 "menu.window" : "ウィンドウ",
70 "menu.window.close" : "é–‰ã˜ã‚‹",
71 "menu.window.minimize" : "最å°åŒ–",
29 "password.email.label" : "メールアドレス", 72 "password.email.label" : "メールアドレス",
30 "password.headline" : "パスワードã®ãƒªã‚»ãƒƒãƒˆ", 73 "password.headline" : "パスワードã®ãƒªã‚»ãƒƒãƒˆ",
31 "password.link.login" : "サインイン", 74 "password.link.login" : "サインイン",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰ã§é–‹ã", 116 "settings.app.form.autoLaunchInBackground" : "ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰ã§é–‹ã",
74 "settings.app.form.autoLaunchOnStart" : "システム起動時ã«Franzã‚’é–‹ã", 117 "settings.app.form.autoLaunchOnStart" : "システム起動時ã«Franzã‚’é–‹ã",
75 "settings.app.form.beta" : "Betaãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’å«ã‚ã‚‹", 118 "settings.app.form.beta" : "Betaãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’å«ã‚ã‚‹",
119 "settings.app.form.enableGPUAcceleration" : "GPUアクセラレーションを有効ã«ã™ã‚‹",
76 "settings.app.form.enableMenuBar" : "メニューãƒãƒ¼ã«Franzを表示ã™ã‚‹", 120 "settings.app.form.enableMenuBar" : "メニューãƒãƒ¼ã«Franzを表示ã™ã‚‹",
77 "settings.app.form.enableSpellchecking" : "スペルãƒã‚§ãƒƒã‚¯ã‚’有効ã«ã™ã‚‹", 121 "settings.app.form.enableSpellchecking" : "スペルãƒã‚§ãƒƒã‚¯ã‚’有効ã«ã™ã‚‹",
78 "settings.app.form.enableSystemTray" : "Franzをシステムトレイã«è¡¨ç¤ºã™ã‚‹", 122 "settings.app.form.enableSystemTray" : "Franzをシステムトレイã«è¡¨ç¤ºã™ã‚‹",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "通知を有効ã«ã™ã‚‹", 241 "tabs.item.enableNotification" : "通知を有効ã«ã™ã‚‹",
198 "tabs.item.enableService" : "サービスを有効ã«ã™ã‚‹", 242 "tabs.item.enableService" : "サービスを有効ã«ã™ã‚‹",
199 "tabs.item.reload" : "å†èª­ã¿è¾¼ã¿", 243 "tabs.item.reload" : "å†èª­ã¿è¾¼ã¿",
244 "validation.email" : "{field}ã¯æ­£ã—ãã‚ã‚Šã¾ã›ã‚“",
245 "validation.minLength" : "{field}ã¯å°‘ãªãã¨ã‚‚{length}文字以上ã§ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“",
246 "validation.required" : "{field}ã¯å¿…é ˆã§ã™",
247 "validation.url" : "{field}ã¯æ­£ã—ã„URLã§ã¯ã‚ã‚Šã¾ã›ã‚“",
200 "welcome.loginButton" : "アカウントã«ãƒ­ã‚°ã‚¤ãƒ³", 248 "welcome.loginButton" : "アカウントã«ãƒ­ã‚°ã‚¤ãƒ³",
201 "welcome.signupButton" : "無料アカウントを作æˆ", 249 "welcome.signupButton" : "無料アカウントを作æˆ",
202 "welcome.slogan" : "Messaging that works for you" 250 "welcome.slogan" : "Messaging that works for you"
diff --git a/src/i18n/locales/ka.json b/src/i18n/locales/ka.json
index 67e81e12b..7679bc6fc 100644
--- a/src/i18n/locales/ka.json
+++ b/src/i18n/locales/ka.json
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "თქვენს სესიáƒáƒ¡ ვáƒáƒ“რგáƒáƒ£áƒ•áƒ˜áƒ“áƒ, შედით áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¨áƒ˜ ხელáƒáƒ®áƒšáƒ.", 26 "login.serverLogout" : "თქვენს სესიáƒáƒ¡ ვáƒáƒ“რგáƒáƒ£áƒ•áƒ˜áƒ“áƒ, შედით áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¨áƒ˜ ხელáƒáƒ®áƒšáƒ.",
27 "login.submit.label" : "შესვლáƒ", 27 "login.submit.label" : "შესვლáƒ",
28 "login.tokenExpired" : "თქვენს სესიáƒáƒ¡ ვáƒáƒ“რგáƒáƒ£áƒ•áƒ˜áƒ“áƒ, შედით áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¨áƒ˜ ხელáƒáƒ®áƒšáƒ.", 28 "login.tokenExpired" : "თქვენს სესიáƒáƒ¡ ვáƒáƒ“რგáƒáƒ£áƒ•áƒ˜áƒ“áƒ, შედით áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¨áƒ˜ ხელáƒáƒ®áƒšáƒ.",
29 "menu.app.about" : "About Franz",
30 "menu.app.hide" : "Hide",
31 "menu.app.hideOthers" : "Hide Others",
32 "menu.app.quit" : "Quit",
33 "menu.app.settings" : "პáƒáƒ áƒáƒ›áƒ”ტრები",
34 "menu.app.unhide" : "Unhide",
35 "menu.edit" : "რედáƒáƒ¥áƒ¢áƒ˜áƒ áƒ”ბáƒ",
36 "menu.edit.copy" : "Copy",
37 "menu.edit.cut" : "Cut",
38 "menu.edit.delete" : "Delete",
39 "menu.edit.emojiSymbols" : "Emoji & Symbols",
40 "menu.edit.paste" : "Paste",
41 "menu.edit.pasteAndMatchStyle" : "Paste And Match Style",
42 "menu.edit.redo" : "Redo",
43 "menu.edit.selectAll" : "Select All",
44 "menu.edit.speech" : "Speech",
45 "menu.edit.startDictation" : "Start Dictation",
46 "menu.edit.startSpeaking" : "Start Speaking",
47 "menu.edit.stopSpeaking" : "Stop Speaking",
48 "menu.edit.undo" : "Undo",
49 "menu.file" : "File",
50 "menu.help" : "Help",
51 "menu.help.changelog" : "Changelog",
52 "menu.help.learnMore" : "Learn More",
53 "menu.help.privacy" : "კáƒáƒœáƒ¤áƒ˜áƒ“ენციáƒáƒšáƒ£áƒ áƒáƒ‘ის შესáƒáƒ®áƒ”ბ",
54 "menu.help.support" : "Support",
55 "menu.help.tos" : "Terms of Service",
56 "menu.services" : "Services",
57 "menu.services.addNewService" : "Add New Service...",
58 "menu.view" : "View",
59 "menu.view.enterFullScreen" : "Enter Full Screen",
60 "menu.view.exitFullScreen" : "Exit Full Screen",
61 "menu.view.reloadFranz" : "Reload Franz",
62 "menu.view.reloadService" : "Reload Service",
63 "menu.view.resetZoom" : "Actual Size",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Toggle Full Screen",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Zoom In",
68 "menu.view.zoomOut" : "Zoom Out",
69 "menu.window" : "Window",
70 "menu.window.close" : "Close",
71 "menu.window.minimize" : "Minimize",
29 "password.email.label" : "მეილი", 72 "password.email.label" : "მეილი",
30 "password.headline" : "პáƒáƒ áƒáƒšáƒ˜áƒ¡ áƒáƒ¦áƒ“გენáƒ", 73 "password.headline" : "პáƒáƒ áƒáƒšáƒ˜áƒ¡ áƒáƒ¦áƒ“გენáƒ",
31 "password.link.login" : "შედით თქვენს áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¨áƒ˜", 74 "password.link.login" : "შედით თქვენს áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¨áƒ˜",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "გáƒáƒ®áƒ¡áƒ”ნით ფáƒáƒœáƒ–ე", 116 "settings.app.form.autoLaunchInBackground" : "გáƒáƒ®áƒ¡áƒ”ნით ფáƒáƒœáƒ–ე",
74 "settings.app.form.autoLaunchOnStart" : "გáƒáƒ”შვáƒáƒ¡ Franz სისტემის ჩáƒáƒ¢áƒ•áƒ˜áƒ áƒ—ვისáƒáƒ¡", 117 "settings.app.form.autoLaunchOnStart" : "გáƒáƒ”შვáƒáƒ¡ Franz სისტემის ჩáƒáƒ¢áƒ•áƒ˜áƒ áƒ—ვისáƒáƒ¡",
75 "settings.app.form.beta" : "ჩáƒáƒ áƒ—ეთ ბეტრვერსიები", 118 "settings.app.form.beta" : "ჩáƒáƒ áƒ—ეთ ბეტრვერსიები",
119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
76 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar", 120 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar",
77 "settings.app.form.enableSpellchecking" : "Enable spell checking", 121 "settings.app.form.enableSpellchecking" : "Enable spell checking",
78 "settings.app.form.enableSystemTray" : "áƒáƒ©áƒ•áƒ”ნეთ Franz სისტემის უჯრáƒáƒ¨áƒ˜", 122 "settings.app.form.enableSystemTray" : "áƒáƒ©áƒ•áƒ”ნეთ Franz სისტემის უჯრáƒáƒ¨áƒ˜",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "შეტყáƒáƒ‘ინებების ჩáƒáƒ áƒ—ვáƒ", 241 "tabs.item.enableNotification" : "შეტყáƒáƒ‘ინებების ჩáƒáƒ áƒ—ვáƒ",
198 "tabs.item.enableService" : "სერვისის ჩáƒáƒ áƒ—ვáƒ", 242 "tabs.item.enableService" : "სერვისის ჩáƒáƒ áƒ—ვáƒ",
199 "tabs.item.reload" : "ჩáƒáƒ¢áƒ•áƒ˜áƒ áƒ—ვáƒ", 243 "tabs.item.reload" : "ჩáƒáƒ¢áƒ•áƒ˜áƒ áƒ—ვáƒ",
244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
200 "welcome.loginButton" : "შედით თქვენს áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¨áƒ˜", 248 "welcome.loginButton" : "შედით თქვენს áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ¨áƒ˜",
201 "welcome.signupButton" : "შექმენი áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ˜, ეს უფáƒáƒ¡áƒáƒ", 249 "welcome.signupButton" : "შექმენი áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ˜, ეს უფáƒáƒ¡áƒáƒ",
202 "welcome.slogan" : "შეტყáƒáƒ‘ინების áƒáƒžáƒšáƒ˜áƒ™áƒáƒªáƒ˜áƒ, რáƒáƒ›áƒ”ლიც მუშáƒáƒáƒ‘ს შენთვის" 250 "welcome.slogan" : "შეტყáƒáƒ‘ინების áƒáƒžáƒšáƒ˜áƒ™áƒáƒªáƒ˜áƒ, რáƒáƒ›áƒ”ლიც მუშáƒáƒáƒ‘ს შენთვის"
diff --git a/src/i18n/locales/nl-BE.json b/src/i18n/locales/nl-BE.json
index dab23f607..13dc7548e 100644
--- a/src/i18n/locales/nl-BE.json
+++ b/src/i18n/locales/nl-BE.json
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "De sessie is verlopen, log opnieuw in alsjeblieft.", 26 "login.serverLogout" : "De sessie is verlopen, log opnieuw in alsjeblieft.",
27 "login.submit.label" : "Log in", 27 "login.submit.label" : "Log in",
28 "login.tokenExpired" : "De sessie is verlopen, log opnieuw in alsjeblieft.", 28 "login.tokenExpired" : "De sessie is verlopen, log opnieuw in alsjeblieft.",
29 "menu.app.about" : "About Franz",
30 "menu.app.hide" : "Hide",
31 "menu.app.hideOthers" : "Hide Others",
32 "menu.app.quit" : "Quit",
33 "menu.app.settings" : "Instellingen",
34 "menu.app.unhide" : "Unhide",
35 "menu.edit" : "Aanpassen",
36 "menu.edit.copy" : "Copy",
37 "menu.edit.cut" : "Cut",
38 "menu.edit.delete" : "Delete",
39 "menu.edit.emojiSymbols" : "Emoji & Symbols",
40 "menu.edit.paste" : "Paste",
41 "menu.edit.pasteAndMatchStyle" : "Paste And Match Style",
42 "menu.edit.redo" : "Redo",
43 "menu.edit.selectAll" : "Select All",
44 "menu.edit.speech" : "Speech",
45 "menu.edit.startDictation" : "Start Dictation",
46 "menu.edit.startSpeaking" : "Start Speaking",
47 "menu.edit.stopSpeaking" : "Stop Speaking",
48 "menu.edit.undo" : "Undo",
49 "menu.file" : "File",
50 "menu.help" : "Help",
51 "menu.help.changelog" : "Changelog",
52 "menu.help.learnMore" : "Learn More",
53 "menu.help.privacy" : "Privacyverklaring",
54 "menu.help.support" : "Support",
55 "menu.help.tos" : "Terms of Service",
56 "menu.services" : "Services",
57 "menu.services.addNewService" : "Add New Service...",
58 "menu.view" : "View",
59 "menu.view.enterFullScreen" : "Enter Full Screen",
60 "menu.view.exitFullScreen" : "Exit Full Screen",
61 "menu.view.reloadFranz" : "Reload Franz",
62 "menu.view.reloadService" : "Reload Service",
63 "menu.view.resetZoom" : "Actual Size",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Toggle Full Screen",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Zoom In",
68 "menu.view.zoomOut" : "Zoom Out",
69 "menu.window" : "Window",
70 "menu.window.close" : "Close",
71 "menu.window.minimize" : "Minimize",
29 "password.email.label" : "E-mailadres", 72 "password.email.label" : "E-mailadres",
30 "password.headline" : "Wachtwoord resetten", 73 "password.headline" : "Wachtwoord resetten",
31 "password.link.login" : "Log in op je account", 74 "password.link.login" : "Log in op je account",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "Open op de achtergrond", 116 "settings.app.form.autoLaunchInBackground" : "Open op de achtergrond",
74 "settings.app.form.autoLaunchOnStart" : "Lanceer Franz bij opstarten", 117 "settings.app.form.autoLaunchOnStart" : "Lanceer Franz bij opstarten",
75 "settings.app.form.beta" : "Inclusief bèta versies", 118 "settings.app.form.beta" : "Inclusief bèta versies",
119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
76 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar", 120 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar",
77 "settings.app.form.enableSpellchecking" : "Enable spell checking", 121 "settings.app.form.enableSpellchecking" : "Enable spell checking",
78 "settings.app.form.enableSystemTray" : "Toon Franz in de systeembalk", 122 "settings.app.form.enableSystemTray" : "Toon Franz in de systeembalk",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Notificaties inschakelen", 241 "tabs.item.enableNotification" : "Notificaties inschakelen",
198 "tabs.item.enableService" : "Service aanzetten", 242 "tabs.item.enableService" : "Service aanzetten",
199 "tabs.item.reload" : "Herladen", 243 "tabs.item.reload" : "Herladen",
244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
200 "welcome.loginButton" : "Inloggen op je account", 248 "welcome.loginButton" : "Inloggen op je account",
201 "welcome.signupButton" : "Maak een gratis account aan", 249 "welcome.signupButton" : "Maak een gratis account aan",
202 "welcome.slogan" : "Messaging that works for you" 250 "welcome.slogan" : "Messaging that works for you"
diff --git a/src/i18n/locales/nl.json b/src/i18n/locales/nl.json
index e2b14ceab..4ff3cdd80 100644
--- a/src/i18n/locales/nl.json
+++ b/src/i18n/locales/nl.json
@@ -7,7 +7,7 @@
7 "import.submit.label" : "Importeer services", 7 "import.submit.label" : "Importeer services",
8 "infobar.buttonChangelog" : "Wat is er nieuw?", 8 "infobar.buttonChangelog" : "Wat is er nieuw?",
9 "infobar.buttonInstallUpdate" : "Opnieuw opstarten & update installeren", 9 "infobar.buttonInstallUpdate" : "Opnieuw opstarten & update installeren",
10 "infobar.buttonReloadServices" : "Laad services opnieuw", 10 "infobar.buttonReloadServices" : "Services opnieuw laden",
11 "infobar.requiredRequestsFailed" : "Kan de services en gebruikersinformatie niet laden", 11 "infobar.requiredRequestsFailed" : "Kan de services en gebruikersinformatie niet laden",
12 "infobar.servicesUpdated" : "Je services zijn bijgewerkt.", 12 "infobar.servicesUpdated" : "Je services zijn bijgewerkt.",
13 "infobar.updateAvailable" : "Er is een nieuwe update voor Franz beschikbaar.", 13 "infobar.updateAvailable" : "Er is een nieuwe update voor Franz beschikbaar.",
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "De sessie is verlopen, log alsjeblieft opnieuw in.", 26 "login.serverLogout" : "De sessie is verlopen, log alsjeblieft opnieuw in.",
27 "login.submit.label" : "Inloggen", 27 "login.submit.label" : "Inloggen",
28 "login.tokenExpired" : "De sessie is verlopen, log opnieuw in alsjeblieft.", 28 "login.tokenExpired" : "De sessie is verlopen, log opnieuw in alsjeblieft.",
29 "menu.app.about" : "Over Franz",
30 "menu.app.hide" : "Verbergen",
31 "menu.app.hideOthers" : "Andere verbergen",
32 "menu.app.quit" : "Afsluiten",
33 "menu.app.settings" : "Instellingen",
34 "menu.app.unhide" : "Tonen",
35 "menu.edit" : "Bewerk",
36 "menu.edit.copy" : "Kopiëren",
37 "menu.edit.cut" : "Knippen",
38 "menu.edit.delete" : "Verwijderen",
39 "menu.edit.emojiSymbols" : "Emoji's en symbolen",
40 "menu.edit.paste" : "Plakken",
41 "menu.edit.pasteAndMatchStyle" : "Plakken en stijl overeen laten komen",
42 "menu.edit.redo" : "Opnieuw doen",
43 "menu.edit.selectAll" : "Selecteer alles",
44 "menu.edit.speech" : "Spraak",
45 "menu.edit.startDictation" : "Beginnen met dicteren",
46 "menu.edit.startSpeaking" : "Beginnen met praten",
47 "menu.edit.stopSpeaking" : "Stoppen met spreken",
48 "menu.edit.undo" : "Ongedaan maken",
49 "menu.file" : "Bestand",
50 "menu.help" : "Hulp",
51 "menu.help.changelog" : "Wijzigingenlogboek",
52 "menu.help.learnMore" : "Meer informatie",
53 "menu.help.privacy" : "Privacy Voorwaarden",
54 "menu.help.support" : "Ondersteuning",
55 "menu.help.tos" : "Servicevoorwaarden",
56 "menu.services" : "Diensten",
57 "menu.services.addNewService" : "Nieuwe dienst toevoegen",
58 "menu.view" : "Weergave",
59 "menu.view.enterFullScreen" : "Gebruik volledig scherm",
60 "menu.view.exitFullScreen" : "Volledig scherm verlaten",
61 "menu.view.reloadFranz" : "Herlaad Franz",
62 "menu.view.reloadService" : "Reload Service",
63 "menu.view.resetZoom" : "Actual Size",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Toggle Full Screen",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Zoom In",
68 "menu.view.zoomOut" : "Zoom Out",
69 "menu.window" : "Window",
70 "menu.window.close" : "Close",
71 "menu.window.minimize" : "Minimize",
29 "password.email.label" : "E-mailadres", 72 "password.email.label" : "E-mailadres",
30 "password.headline" : "Wachtwoord vergeten", 73 "password.headline" : "Wachtwoord vergeten",
31 "password.link.login" : "Log in op je account", 74 "password.link.login" : "Log in op je account",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "Open op de achtergrond", 116 "settings.app.form.autoLaunchInBackground" : "Open op de achtergrond",
74 "settings.app.form.autoLaunchOnStart" : "Open Franz bij opstarten", 117 "settings.app.form.autoLaunchOnStart" : "Open Franz bij opstarten",
75 "settings.app.form.beta" : "Inclusief bètaversies", 118 "settings.app.form.beta" : "Inclusief bètaversies",
119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
76 "settings.app.form.enableMenuBar" : "Toon Franz in menubalk", 120 "settings.app.form.enableMenuBar" : "Toon Franz in menubalk",
77 "settings.app.form.enableSpellchecking" : "Zet spellingcontrole aan", 121 "settings.app.form.enableSpellchecking" : "Zet spellingcontrole aan",
78 "settings.app.form.enableSystemTray" : "Toon Franz in de systeembalk", 122 "settings.app.form.enableSystemTray" : "Toon Franz in de systeembalk",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Meldingen inschakelen", 241 "tabs.item.enableNotification" : "Meldingen inschakelen",
198 "tabs.item.enableService" : "Service inschakelen", 242 "tabs.item.enableService" : "Service inschakelen",
199 "tabs.item.reload" : "Laad opnieuw", 243 "tabs.item.reload" : "Laad opnieuw",
244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
200 "welcome.loginButton" : "Log in op je account", 248 "welcome.loginButton" : "Log in op je account",
201 "welcome.signupButton" : "Maak een gratis account", 249 "welcome.signupButton" : "Maak een gratis account",
202 "welcome.slogan" : "Messaging die voor jou werkt" 250 "welcome.slogan" : "Messaging die voor jou werkt"
diff --git a/src/i18n/locales/pl.json b/src/i18n/locales/pl.json
index 95e269ea7..9fb72b9c9 100644
--- a/src/i18n/locales/pl.json
+++ b/src/i18n/locales/pl.json
@@ -16,7 +16,7 @@
16 "invite.name.label" : "ImiÄ™", 16 "invite.name.label" : "ImiÄ™",
17 "invite.skip.label" : "Chcę to zrobić później", 17 "invite.skip.label" : "Chcę to zrobić później",
18 "invite.submit.label" : "Wyślij zaproszenia", 18 "invite.submit.label" : "Wyślij zaproszenia",
19 "invite.successInfo" : "Invitations sent successfully", 19 "invite.successInfo" : "Zaproszenia zostały wysłane",
20 "login.email.label" : "Adres email", 20 "login.email.label" : "Adres email",
21 "login.headline" : "Zaloguj siÄ™", 21 "login.headline" : "Zaloguj siÄ™",
22 "login.invalidCredentials" : "Adres email lub hasło są błędne", 22 "login.invalidCredentials" : "Adres email lub hasło są błędne",
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "Twoja sesja wygasła, zaloguj się ponownie.", 26 "login.serverLogout" : "Twoja sesja wygasła, zaloguj się ponownie.",
27 "login.submit.label" : "Zaloguj siÄ™", 27 "login.submit.label" : "Zaloguj siÄ™",
28 "login.tokenExpired" : "Twoja sesja wygasła, zaloguj się ponownie.", 28 "login.tokenExpired" : "Twoja sesja wygasła, zaloguj się ponownie.",
29 "menu.app.about" : "O Franz",
30 "menu.app.hide" : "Ukryj",
31 "menu.app.hideOthers" : "Hide Others",
32 "menu.app.quit" : "Quit",
33 "menu.app.settings" : "Ustawienia",
34 "menu.app.unhide" : "Pokaż",
35 "menu.edit" : "Edytuj",
36 "menu.edit.copy" : "Skopiuj",
37 "menu.edit.cut" : "Wytnij",
38 "menu.edit.delete" : "Usuń",
39 "menu.edit.emojiSymbols" : "Emoji & Symbols",
40 "menu.edit.paste" : "Wklej",
41 "menu.edit.pasteAndMatchStyle" : "Wklej i dopasuj styl",
42 "menu.edit.redo" : "Ponów",
43 "menu.edit.selectAll" : "Zaznacz wszystko",
44 "menu.edit.speech" : "Speech",
45 "menu.edit.startDictation" : "Zacznij dyktować",
46 "menu.edit.startSpeaking" : "Zacznij mówić",
47 "menu.edit.stopSpeaking" : "Przestań mówić",
48 "menu.edit.undo" : "Cofnij",
49 "menu.file" : "Plik",
50 "menu.help" : "Pomoc",
51 "menu.help.changelog" : "Lista zmian",
52 "menu.help.learnMore" : "Dowiedz się więcej",
53 "menu.help.privacy" : "Polityka prywatności",
54 "menu.help.support" : "Wsparcie",
55 "menu.help.tos" : "Terms of Service",
56 "menu.services" : "Usługi",
57 "menu.services.addNewService" : "Dodaj nową usługę...",
58 "menu.view" : "Widok",
59 "menu.view.enterFullScreen" : "Włącz tryb pełnoekranowy",
60 "menu.view.exitFullScreen" : "Zakończ tryb pełnoekranowy",
61 "menu.view.reloadFranz" : "Przeładuj Franz",
62 "menu.view.reloadService" : "Przeładuj usługę",
63 "menu.view.resetZoom" : "Domyślny rozmiar",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Przełącz tryb pełnoekranowy",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Powiększ",
68 "menu.view.zoomOut" : "Pomniejsz",
69 "menu.window" : "Okno",
70 "menu.window.close" : "Zamknij",
71 "menu.window.minimize" : "Zminimalizuj",
29 "password.email.label" : "Adres email", 72 "password.email.label" : "Adres email",
30 "password.headline" : "Wyzeruj hasło", 73 "password.headline" : "Wyzeruj hasło",
31 "password.link.login" : "Zaloguj siÄ™ na swoje konto", 74 "password.link.login" : "Zaloguj siÄ™ na swoje konto",
@@ -41,7 +84,7 @@
41 "service.crashHandler.autoReload" : "Próba automatycznego odnowienia {name} za {seconds} sekund\/y", 84 "service.crashHandler.autoReload" : "Próba automatycznego odnowienia {name} za {seconds} sekund\/y",
42 "service.crashHandler.headline" : "O nie!", 85 "service.crashHandler.headline" : "O nie!",
43 "service.crashHandler.text" : "{name} spowodował problem.", 86 "service.crashHandler.text" : "{name} spowodował problem.",
44 "service.disabledHandler.action" : "Dostępny {name}", 87 "service.disabledHandler.action" : "Włącz {name}",
45 "service.disabledHandler.headline" : "{name} jest wyłączony", 88 "service.disabledHandler.headline" : "{name} jest wyłączony",
46 "services.getStarted" : "Zacznij", 89 "services.getStarted" : "Zacznij",
47 "services.welcome" : "Witaj w programie Franz", 90 "services.welcome" : "Witaj w programie Franz",
@@ -65,18 +108,19 @@
65 "settings.account.successInfo" : "Twoje zmiany zostały zapisane", 108 "settings.account.successInfo" : "Twoje zmiany zostały zapisane",
66 "settings.account.tryReloadUserInfoRequest" : "Spróbuj ponownie", 109 "settings.account.tryReloadUserInfoRequest" : "Spróbuj ponownie",
67 "settings.account.userInfoRequestFailed" : "Nie można wczytać informacji o użytkowniku", 110 "settings.account.userInfoRequestFailed" : "Nie można wczytać informacji o użytkowniku",
68 "settings.app.buttonClearAllCache" : "Clear cache", 111 "settings.app.buttonClearAllCache" : "Wyczyść pamięć podręczną (cache)",
69 "settings.app.buttonInstallUpdate" : "Uruchom ponownie i zainstaluj aktualizacjÄ™", 112 "settings.app.buttonInstallUpdate" : "Uruchom ponownie i zainstaluj aktualizacjÄ™",
70 "settings.app.buttonSearchForUpdate" : "Sprawdź aktualizacje", 113 "settings.app.buttonSearchForUpdate" : "Sprawdź aktualizacje",
71 "settings.app.cacheInfo" : "Franz cache is currently using {size} of disk space.", 114 "settings.app.cacheInfo" : "Pamięć podręczna zajmuje obecnie {size} przestrzeni dyskowej",
72 "settings.app.currentVersion" : "Aktualna wersja:", 115 "settings.app.currentVersion" : "Aktualna wersja:",
73 "settings.app.form.autoLaunchInBackground" : "Otwórz w tle", 116 "settings.app.form.autoLaunchInBackground" : "Otwórz w tle",
74 "settings.app.form.autoLaunchOnStart" : "Uruchom Franz na poczÄ…tku", 117 "settings.app.form.autoLaunchOnStart" : "Uruchom Franz na poczÄ…tku",
75 "settings.app.form.beta" : "Uwzględnij wersje beta", 118 "settings.app.form.beta" : "Uwzględnij wersje beta",
76 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar", 119 "settings.app.form.enableGPUAcceleration" : "WÅ‚Ä…cz akceleracjÄ™ GPU",
120 "settings.app.form.enableMenuBar" : "Pokaż Franz na pasku menu",
77 "settings.app.form.enableSpellchecking" : "WÅ‚Ä…cz sprawdzanie pisowni", 121 "settings.app.form.enableSpellchecking" : "WÅ‚Ä…cz sprawdzanie pisowni",
78 "settings.app.form.enableSystemTray" : "Pokaż Franza w tacy systemowej", 122 "settings.app.form.enableSystemTray" : "Pokaż Franza w tacy systemowej",
79 "settings.app.form.hideDockIcon" : "Hide Franz icon in Dock", 123 "settings.app.form.hideDockIcon" : "Ukrywaj ikonÄ™ Franz w zasobniku systemowym",
80 "settings.app.form.language" : "Język", 124 "settings.app.form.language" : "Język",
81 "settings.app.form.minimizeToSystemTray" : "Zminimalizuj aplikacjÄ™ Franz", 125 "settings.app.form.minimizeToSystemTray" : "Zminimalizuj aplikacjÄ™ Franz",
82 "settings.app.form.runInBackground" : "Zachowaj aplikację Franz w tle po zamknięciu okna", 126 "settings.app.form.runInBackground" : "Zachowaj aplikację Franz w tle po zamknięciu okna",
@@ -89,15 +133,15 @@
89 "settings.app.headlineLanguage" : "Język", 133 "settings.app.headlineLanguage" : "Język",
90 "settings.app.headlineUpdates" : "Aktualizacje", 134 "settings.app.headlineUpdates" : "Aktualizacje",
91 "settings.app.restartRequired" : "Zmiany. wymagajÄ… ponownego uruchomienia", 135 "settings.app.restartRequired" : "Zmiany. wymagajÄ… ponownego uruchomienia",
92 "settings.app.subheadlineCache" : "Cache", 136 "settings.app.subheadlineCache" : "Pamięć podręczna",
93 "settings.app.translationHelp" : "Pomóż nam tłumaczyć Franz na Twój język.", 137 "settings.app.translationHelp" : "Pomóż nam tłumaczyć Franz na Twój język.",
94 "settings.app.updateStatusAvailable" : "Dostępna aktualizacja, pobieram...", 138 "settings.app.updateStatusAvailable" : "Dostępna aktualizacja, pobieram...",
95 "settings.app.updateStatusSearching" : "Szukam aktualizacji", 139 "settings.app.updateStatusSearching" : "Szukam aktualizacji",
96 "settings.app.updateStatusUpToDate" : "Używasz najnowszej wersji aplikacji Franz", 140 "settings.app.updateStatusUpToDate" : "Używasz najnowszej wersji aplikacji Franz",
97 "settings.invite.headline" : "Invite Friends", 141 "settings.invite.headline" : "ZaproÅ› znajomych",
98 "settings.navigation.account" : "Konto", 142 "settings.navigation.account" : "Konto",
99 "settings.navigation.availableServices" : "Dostępne usługi", 143 "settings.navigation.availableServices" : "Dostępne usługi",
100 "settings.navigation.inviteFriends" : "Invite Friends", 144 "settings.navigation.inviteFriends" : "ZaproÅ› znajomych",
101 "settings.navigation.logout" : "Wyloguj", 145 "settings.navigation.logout" : "Wyloguj",
102 "settings.navigation.settings" : "Ustawienia", 146 "settings.navigation.settings" : "Ustawienia",
103 "settings.navigation.yourServices" : "Twoje usługi", 147 "settings.navigation.yourServices" : "Twoje usługi",
@@ -108,7 +152,7 @@
108 "settings.recipes.mostPopular" : "Najpopularniejsze", 152 "settings.recipes.mostPopular" : "Najpopularniejsze",
109 "settings.recipes.nothingFound" : "Żadna usługa nie została znaleziona.", 153 "settings.recipes.nothingFound" : "Żadna usługa nie została znaleziona.",
110 "settings.recipes.servicesSuccessfulAddedInfo" : "Usługa została dodana pomyślnie", 154 "settings.recipes.servicesSuccessfulAddedInfo" : "Usługa została dodana pomyślnie",
111 "settings.searchService" : "Search service", 155 "settings.searchService" : "Wyszukaj serwis",
112 "settings.service.error.goBack" : "Wróć do usług", 156 "settings.service.error.goBack" : "Wróć do usług",
113 "settings.service.error.headline" : "BÅ‚Ä…d", 157 "settings.service.error.headline" : "BÅ‚Ä…d",
114 "settings.service.error.message" : "Nie można wczytać przepisu usługi.", 158 "settings.service.error.message" : "Nie można wczytać przepisu usługi.",
@@ -121,15 +165,15 @@
121 "settings.service.form.deleteButton" : "Usuń usługę", 165 "settings.service.form.deleteButton" : "Usuń usługę",
122 "settings.service.form.editServiceHeadline" : "Edytuj {name}", 166 "settings.service.form.editServiceHeadline" : "Edytuj {name}",
123 "settings.service.form.enableAudio" : "Włącz dźwięk", 167 "settings.service.form.enableAudio" : "Włącz dźwięk",
124 "settings.service.form.enableBadge" : "Show unread message badges", 168 "settings.service.form.enableBadge" : "Pokaż znacznik nieprzeczytanych wiadomości",
125 "settings.service.form.enableNotification" : "Aktywuj powiadomienia", 169 "settings.service.form.enableNotification" : "Aktywuj powiadomienia",
126 "settings.service.form.enableService" : "Aktywuj usługę", 170 "settings.service.form.enableService" : "Aktywuj usługę",
127 "settings.service.form.headlineBadges" : "Unread message badges", 171 "settings.service.form.headlineBadges" : "Znaczniki nieprzeczytanych wiadomości",
128 "settings.service.form.headlineGeneral" : "Ogólne", 172 "settings.service.form.headlineGeneral" : "Ogólne",
129 "settings.service.form.headlineNotifications" : "Notifications", 173 "settings.service.form.headlineNotifications" : "Powiadomienia",
130 "settings.service.form.icon" : "Custom icon", 174 "settings.service.form.icon" : "WÅ‚asna ikona",
131 "settings.service.form.iconDelete" : "Delete", 175 "settings.service.form.iconDelete" : "Usuń",
132 "settings.service.form.iconUpload" : "Drop your image, or click here", 176 "settings.service.form.iconUpload" : "Przeciągnij i upuść obraz lub kliknij tutaj",
133 "settings.service.form.indirectMessageInfo" : "Będziesz informowany o wszystkich nowych wiadomościach na kanale, nie tylko @username, @channel, @here, ...", 177 "settings.service.form.indirectMessageInfo" : "Będziesz informowany o wszystkich nowych wiadomościach na kanale, nie tylko @username, @channel, @here, ...",
134 "settings.service.form.indirectMessages" : "Pokaż ikonę wiadomości dla wszystkich nowych wiadomości", 178 "settings.service.form.indirectMessages" : "Pokaż ikonę wiadomości dla wszystkich nowych wiadomości",
135 "settings.service.form.isMutedInfo" : "Kiedy nieaktywne, wszystkie dźwięki powiadomień są wyciszone", 179 "settings.service.form.isMutedInfo" : "Kiedy nieaktywne, wszystkie dźwięki powiadomień są wyciszone",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "WÅ‚Ä…cz powiadomienia", 241 "tabs.item.enableNotification" : "WÅ‚Ä…cz powiadomienia",
198 "tabs.item.enableService" : "Aktywuj usługę", 242 "tabs.item.enableService" : "Aktywuj usługę",
199 "tabs.item.reload" : "Przeładuj", 243 "tabs.item.reload" : "Przeładuj",
244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
200 "welcome.loginButton" : "Zaloguj siÄ™ na swoje konto", 248 "welcome.loginButton" : "Zaloguj siÄ™ na swoje konto",
201 "welcome.signupButton" : "Stwórz darmowe konto", 249 "welcome.signupButton" : "Stwórz darmowe konto",
202 "welcome.slogan" : "Komunikator który działa" 250 "welcome.slogan" : "Komunikator który działa"
diff --git a/src/i18n/locales/pt-BR.json b/src/i18n/locales/pt-BR.json
index dd1633a9c..ceb35ce30 100644
--- a/src/i18n/locales/pt-BR.json
+++ b/src/i18n/locales/pt-BR.json
@@ -1,5 +1,5 @@
1{ 1{
2 "global.api.unhealthy" : "Não foi possível conectar-se aos serviços do Franz", 2 "global.api.unhealthy" : "Não foi possível conectar aos serviços do Franz",
3 "global.notConnectedToTheInternet" : "Você não está conectado à internet.", 3 "global.notConnectedToTheInternet" : "Você não está conectado à internet.",
4 "import.headline" : "Importe seus serviços do Franz 4", 4 "import.headline" : "Importe seus serviços do Franz 4",
5 "import.notSupportedHeadline" : "Serviços ainda não suportados pelo Franz 5", 5 "import.notSupportedHeadline" : "Serviços ainda não suportados pelo Franz 5",
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "Sua sessão expirou, faça o login novamente.", 26 "login.serverLogout" : "Sua sessão expirou, faça o login novamente.",
27 "login.submit.label" : "Entrar", 27 "login.submit.label" : "Entrar",
28 "login.tokenExpired" : "Sua sessão expirou, faça o login novamente.", 28 "login.tokenExpired" : "Sua sessão expirou, faça o login novamente.",
29 "menu.app.about" : "Sobre Franz",
30 "menu.app.hide" : "Ocultar",
31 "menu.app.hideOthers" : "Ocultar os demais",
32 "menu.app.quit" : "Sair",
33 "menu.app.settings" : "Ajustes",
34 "menu.app.unhide" : "Exibir",
35 "menu.edit" : "Editar",
36 "menu.edit.copy" : "Copiar",
37 "menu.edit.cut" : "Cortar",
38 "menu.edit.delete" : "Excluir",
39 "menu.edit.emojiSymbols" : "Emoji & Símbolos ",
40 "menu.edit.paste" : "Colar",
41 "menu.edit.pasteAndMatchStyle" : "Colar e corresponder ao estilo",
42 "menu.edit.redo" : "Refazer",
43 "menu.edit.selectAll" : "Selecionar tudo",
44 "menu.edit.speech" : "Discurso",
45 "menu.edit.startDictation" : "iniciar digitação por voz",
46 "menu.edit.startSpeaking" : "começar a falar",
47 "menu.edit.stopSpeaking" : "Parar de falar",
48 "menu.edit.undo" : "Desfazer",
49 "menu.file" : "Arquivo",
50 "menu.help" : "Ajuda",
51 "menu.help.changelog" : "Registro de alterações",
52 "menu.help.learnMore" : "Saiba Mais",
53 "menu.help.privacy" : "Declaração de privacidade",
54 "menu.help.support" : "Suporte",
55 "menu.help.tos" : "Termos de Serviço",
56 "menu.services" : "Serviços",
57 "menu.services.addNewService" : "Adicionar Novo Serviço",
58 "menu.view" : "Visualizar",
59 "menu.view.enterFullScreen" : "Entrar na tela cheia",
60 "menu.view.exitFullScreen" : "Sair da tela cheia",
61 "menu.view.reloadFranz" : "Recarregar Franz",
62 "menu.view.reloadService" : "Reiniciar o serviço",
63 "menu.view.resetZoom" : "Tamanho real",
64 "menu.view.toggleDevTools" : "alternar ferramentas de desenvolvedor",
65 "menu.view.toggleFullScreen" : "tela cheia",
66 "menu.view.toggleServiceDevTools" : "habilitar ferramentas de serviços de desenvolvedor",
67 "menu.view.zoomIn" : "Ampliar",
68 "menu.view.zoomOut" : "reduzir",
69 "menu.window" : "Janela",
70 "menu.window.close" : "Fechar",
71 "menu.window.minimize" : "Minimizar",
29 "password.email.label" : "Endereço de e-mail", 72 "password.email.label" : "Endereço de e-mail",
30 "password.headline" : "Trocar a senha", 73 "password.headline" : "Trocar a senha",
31 "password.link.login" : "Fazer login na sua conta", 74 "password.link.login" : "Fazer login na sua conta",
@@ -73,16 +116,17 @@
73 "settings.app.form.autoLaunchInBackground" : "Abrir em segundo plano", 116 "settings.app.form.autoLaunchInBackground" : "Abrir em segundo plano",
74 "settings.app.form.autoLaunchOnStart" : "Abrir o Franz ao iniciar o sistema", 117 "settings.app.form.autoLaunchOnStart" : "Abrir o Franz ao iniciar o sistema",
75 "settings.app.form.beta" : "Incluir versões beta", 118 "settings.app.form.beta" : "Incluir versões beta",
119 "settings.app.form.enableGPUAcceleration" : "Ativar Aceleração de GPU (Unidade de Processamento Gráfico)",
76 "settings.app.form.enableMenuBar" : "Mostrar Franz na Barra de Menu", 120 "settings.app.form.enableMenuBar" : "Mostrar Franz na Barra de Menu",
77 "settings.app.form.enableSpellchecking" : "Ativar correção ortográfica", 121 "settings.app.form.enableSpellchecking" : "Ativar correção ortográfica",
78 "settings.app.form.enableSystemTray" : "Exibir o Franz na área de notificações", 122 "settings.app.form.enableSystemTray" : "Exibir o Franz na área de notificações",
79 "settings.app.form.hideDockIcon" : "Hide Franz icon in Dock", 123 "settings.app.form.hideDockIcon" : "Ocultar ícone do Franz no Dock",
80 "settings.app.form.language" : "Idioma", 124 "settings.app.form.language" : "Idioma",
81 "settings.app.form.minimizeToSystemTray" : "Minimizar o Franz para a área de notificações", 125 "settings.app.form.minimizeToSystemTray" : "Minimizar o Franz para a área de notificações",
82 "settings.app.form.runInBackground" : "Manter o Franz no fundo quando fechar a janela", 126 "settings.app.form.runInBackground" : "Manter o Franz no fundo quando fechar a janela",
83 "settings.app.form.showDisabledServices" : "Mostrar abas de serviços desativados", 127 "settings.app.form.showDisabledServices" : "Mostrar abas de serviços desativados",
84 "settings.app.form.showMessagesBadgesWhenMuted" : "Mostrar ícone de mensagem não lida quando as notificações estiverem desativadas", 128 "settings.app.form.showMessagesBadgesWhenMuted" : "Mostrar ícone de mensagem não lida quando as notificações estiverem desativadas",
85 "settings.app.headline" : "Ajustes", 129 "settings.app.headline" : "Configurações",
86 "settings.app.headlineAdvanced" : "Avançado", 130 "settings.app.headlineAdvanced" : "Avançado",
87 "settings.app.headlineAppearance" : "Aparência", 131 "settings.app.headlineAppearance" : "Aparência",
88 "settings.app.headlineGeneral" : "Geral", 132 "settings.app.headlineGeneral" : "Geral",
@@ -94,10 +138,10 @@
94 "settings.app.updateStatusAvailable" : "Atualização disponível, baixando...", 138 "settings.app.updateStatusAvailable" : "Atualização disponível, baixando...",
95 "settings.app.updateStatusSearching" : "Buscando atualizações", 139 "settings.app.updateStatusSearching" : "Buscando atualizações",
96 "settings.app.updateStatusUpToDate" : "Você está usando a última versão do Franz", 140 "settings.app.updateStatusUpToDate" : "Você está usando a última versão do Franz",
97 "settings.invite.headline" : "Invite Friends", 141 "settings.invite.headline" : "Convidar amigos",
98 "settings.navigation.account" : "Conta", 142 "settings.navigation.account" : "Conta",
99 "settings.navigation.availableServices" : "Serviços disponíveis", 143 "settings.navigation.availableServices" : "Serviços disponíveis",
100 "settings.navigation.inviteFriends" : "Invite Friends", 144 "settings.navigation.inviteFriends" : "Convidar amigos",
101 "settings.navigation.logout" : "Sair", 145 "settings.navigation.logout" : "Sair",
102 "settings.navigation.settings" : "Ajustes", 146 "settings.navigation.settings" : "Ajustes",
103 "settings.navigation.yourServices" : "Seus serviços", 147 "settings.navigation.yourServices" : "Seus serviços",
@@ -197,7 +241,11 @@
197 "tabs.item.enableNotification" : "Ativar notificações", 241 "tabs.item.enableNotification" : "Ativar notificações",
198 "tabs.item.enableService" : "Ativar serviço", 242 "tabs.item.enableService" : "Ativar serviço",
199 "tabs.item.reload" : "Recarregar", 243 "tabs.item.reload" : "Recarregar",
244 "validation.email" : "inválido",
245 "validation.minLength" : "{campo} deve ser pelo menos {comprimento} mais caracteres ",
246 "validation.required" : "{campo} obrigatório",
247 "validation.url" : "{campo} essa URL não é válida",
200 "welcome.loginButton" : "Entrar na sua conta", 248 "welcome.loginButton" : "Entrar na sua conta",
201 "welcome.signupButton" : "Criar uma conta grátis", 249 "welcome.signupButton" : "Criar uma conta grátis",
202 "welcome.slogan" : "Mensageria que funciona, para você" 250 "welcome.slogan" : "Mensagens que funcionam para você"
203} 251}
diff --git a/src/i18n/locales/pt.json b/src/i18n/locales/pt.json
index fb08ebd2a..44048e505 100644
--- a/src/i18n/locales/pt.json
+++ b/src/i18n/locales/pt.json
@@ -1,112 +1,156 @@
1{ 1{
2 "global.api.unhealthy" : "Não foi possível conectar aos serviços do Franz", 2 "global.api.unhealthy" : "Não foi possível conetar aos serviços do Franz",
3 "global.notConnectedToTheInternet" : "Não estás conectado à internet.", 3 "global.notConnectedToTheInternet" : "Não existe ligação à Internet.",
4 "import.headline" : "Importar os teus serviços do Franz 4", 4 "import.headline" : "Importe os seus serviços do Franz 4",
5 "import.notSupportedHeadline" : "Os serviços não são suportados no Franz 5", 5 "import.notSupportedHeadline" : "Serviços ainda não são suportados pelo Franz 5",
6 "import.skip.label" : "Quero adicionar serviços manualmente", 6 "import.skip.label" : "Quero adicionar serviços manualmente",
7 "import.submit.label" : "Importar serviços", 7 "import.submit.label" : "Importar serviços",
8 "infobar.buttonChangelog" : "Novidades?", 8 "infobar.buttonChangelog" : "Novidades?",
9 "infobar.buttonInstallUpdate" : "Reiniciar & Instalar Atualizações", 9 "infobar.buttonInstallUpdate" : "Reiniciar & Instalar Atualizações",
10 "infobar.buttonReloadServices" : "Recarregar serviços", 10 "infobar.buttonReloadServices" : "Recarregar serviços",
11 "infobar.requiredRequestsFailed" : "Não foi possivel carregar os serviços e informações do utilizador", 11 "infobar.requiredRequestsFailed" : "Não foi possivel carregar os serviços e informações do utilizador",
12 "infobar.servicesUpdated" : "Os teus serviços foram atualizados", 12 "infobar.servicesUpdated" : "Os seus serviços foram atualizados.",
13 "infobar.updateAvailable" : "Uma atualização está disponível", 13 "infobar.updateAvailable" : "Está disponível uma atualização",
14 "invite.email.label" : "Endereço de e-mail", 14 "invite.email.label" : "Endereço de e-mail",
15 "invite.headline.friends" : "Manda 3 convites aos teus amigos ou colegas", 15 "invite.headline.friends" : "Convide 3 amigos ou colegas",
16 "invite.name.label" : "Nome", 16 "invite.name.label" : "Nome",
17 "invite.skip.label" : "Quero adicionar mais tarde", 17 "invite.skip.label" : "Quero fazer isto mais tarde",
18 "invite.submit.label" : "Mandar convites", 18 "invite.submit.label" : "Enviar convites",
19 "invite.successInfo" : "Invitations sent successfully", 19 "invite.successInfo" : "Convites enviados com sucesso",
20 "login.email.label" : "Endereço de e-mail", 20 "login.email.label" : "Endereço de e-mail",
21 "login.headline" : "Registar", 21 "login.headline" : "Registar",
22 "login.invalidCredentials" : " O email ou a password estão incorretos", 22 "login.invalidCredentials" : "O email ou a palavra-passe estão incorretos",
23 "login.link.password" : "Repor a minha password", 23 "login.link.password" : "Repor a minha palavra-passe",
24 "login.link.signup" : "Criar uma conta gratuita", 24 "login.link.signup" : "Criar uma conta gratuita",
25 "login.password.label" : "Password", 25 "login.password.label" : "Palavra-passe",
26 "login.serverLogout" : "A tua sessão expirou, por favor, volta a fazer login.", 26 "login.serverLogout" : "A sua sessão expirou, inicie sessão novamente.",
27 "login.submit.label" : "Iniciar Sessão", 27 "login.submit.label" : "Iniciar sessão",
28 "login.tokenExpired" : "A tua sessão expirou, por favor, volta a fazer login.", 28 "login.tokenExpired" : "A sua sessão expirou, inicie sessão novamente.",
29 "menu.app.about" : "Sobre Franz",
30 "menu.app.hide" : "Ocultar",
31 "menu.app.hideOthers" : "Ocultar Outros",
32 "menu.app.quit" : "Sair",
33 "menu.app.settings" : "Definições",
34 "menu.app.unhide" : "Mostrar",
35 "menu.edit" : "Editar",
36 "menu.edit.copy" : "Copiar",
37 "menu.edit.cut" : "Cortar",
38 "menu.edit.delete" : "Apagar",
39 "menu.edit.emojiSymbols" : "Emoji & Symbols",
40 "menu.edit.paste" : "Paste",
41 "menu.edit.pasteAndMatchStyle" : "Paste And Match Style",
42 "menu.edit.redo" : "Redo",
43 "menu.edit.selectAll" : "Select All",
44 "menu.edit.speech" : "Speech",
45 "menu.edit.startDictation" : "Start Dictation",
46 "menu.edit.startSpeaking" : "Start Speaking",
47 "menu.edit.stopSpeaking" : "Stop Speaking",
48 "menu.edit.undo" : "Undo",
49 "menu.file" : "File",
50 "menu.help" : "Help",
51 "menu.help.changelog" : "Changelog",
52 "menu.help.learnMore" : "Learn More",
53 "menu.help.privacy" : "Declaração de privacidade",
54 "menu.help.support" : "Support",
55 "menu.help.tos" : "Terms of Service",
56 "menu.services" : "Services",
57 "menu.services.addNewService" : "Add New Service...",
58 "menu.view" : "View",
59 "menu.view.enterFullScreen" : "Enter Full Screen",
60 "menu.view.exitFullScreen" : "Exit Full Screen",
61 "menu.view.reloadFranz" : "Reload Franz",
62 "menu.view.reloadService" : "Reload Service",
63 "menu.view.resetZoom" : "Actual Size",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Toggle Full Screen",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Zoom In",
68 "menu.view.zoomOut" : "Zoom Out",
69 "menu.window" : "Window",
70 "menu.window.close" : "Close",
71 "menu.window.minimize" : "Minimize",
29 "password.email.label" : "Endereço de e-mail", 72 "password.email.label" : "Endereço de e-mail",
30 "password.headline" : "Repor a minha password", 73 "password.headline" : "Repor a minha palavra-passe",
31 "password.link.login" : "Fazer login", 74 "password.link.login" : "Iniciar sessão",
32 "password.link.signup" : "Criar uma conta gratuita", 75 "password.link.signup" : "Criar uma conta gratuita",
33 "password.noUser" : "Não existe nenhuma conta associada a esse email", 76 "password.noUser" : "Não existe nenhuma conta associada a esse email",
34 "password.submit.label" : "Submeter", 77 "password.submit.label" : "Submeter",
35 "password.successInfo" : "Por favor, revê o teu email", 78 "password.successInfo" : "Por favor reveja o seu email",
36 "pricing.headline" : "Apoia o Franz", 79 "pricing.headline" : "Apoie o Franz",
37 "pricing.link.skipPayment" : "Não quero ajudar no desenvolvimento do Franz", 80 "pricing.link.skipPayment" : "Não pretendo ajudar no desenvolvimento do Franz",
38 "pricing.submit.label" : "Quero ajudar o desenvolvimento do Franz", 81 "pricing.submit.label" : "Pretendo ajudar no desenvolvimento do Franz",
39 "pricing.support.label" : "Seleciona o teu plano", 82 "pricing.support.label" : "Selecione o seu plano",
40 "service.crashHandler.action" : "Actualizar", 83 "service.crashHandler.action" : "Recarregar",
41 "service.crashHandler.autoReload" : "A tentar restaurar automaticamente {name} em {seconds} seconds", 84 "service.crashHandler.autoReload" : "A tentar restaurar automaticamente {name} em {seconds} segundos",
42 "service.crashHandler.headline" : "Oh não!", 85 "service.crashHandler.headline" : "Oh não!",
43 "service.crashHandler.text" : "{name} causou um erro.", 86 "service.crashHandler.text" : "{name} causou um erro.",
44 "service.disabledHandler.action" : "Habilitar {name}", 87 "service.disabledHandler.action" : "Ativar {name}",
45 "service.disabledHandler.headline" : "{name} está desactivado", 88 "service.disabledHandler.headline" : "{name} está desativado",
46 "services.getStarted" : "Vamos começar", 89 "services.getStarted" : "Vamos começar",
47 "services.welcome" : "Bem-vindo ao Franz", 90 "services.welcome" : "Bem-vindo ao Franz",
48 "settings.account.account.editButton" : "Editar conta", 91 "settings.account.account.editButton" : "Editar conta",
49 "settings.account.accountType.basic" : "Conta básica", 92 "settings.account.accountType.basic" : "Conta básica",
50 "settings.account.accountType.premium" : "Conta Premium, a ajudar o Franz", 93 "settings.account.accountType.premium" : "Conta Premium",
51 "settings.account.buttonSave" : "Atualizar o perfil", 94 "settings.account.buttonSave" : "Atualizar o perfil",
52 "settings.account.deleteAccount" : "Apagar conta", 95 "settings.account.deleteAccount" : "Apagar conta",
53 "settings.account.deleteEmailSent" : "Recebeu um email com um endereço para confirmar a remoção da sua conta. A sua conta e dados não são possiveis de restaurar!", 96 "settings.account.deleteEmailSent" : "Recebeu um email com um endereço para confirmar a remoção da sua conta. A sua conta e dados não poderão ser restaurados!",
54 "settings.account.deleteInfo" : "Se não precisa mais da sua conta Franz, pode apagar a conta e os seus dados aqui.", 97 "settings.account.deleteInfo" : "Se não necessita mais da sua conta Franz, pode apagar a sua conta e dados aqui.",
55 "settings.account.headline" : "Conta", 98 "settings.account.headline" : "Conta",
56 "settings.account.headlineAccount" : "Informação da conta", 99 "settings.account.headlineAccount" : "Informação da conta",
57 "settings.account.headlineDangerZone" : "Zona de perigo", 100 "settings.account.headlineDangerZone" : "Zona de perigo",
58 "settings.account.headlineInvoices" : "Faturas", 101 "settings.account.headlineInvoices" : "Faturas",
59 "settings.account.headlinePassword" : "Mudar password", 102 "settings.account.headlinePassword" : "Alterar palavra-passe",
60 "settings.account.headlineProfile" : "Actualizar o perfil", 103 "settings.account.headlineProfile" : "Atualizar perfil",
61 "settings.account.headlineSubscription" : "A tua subscrição", 104 "settings.account.headlineSubscription" : "A sua subscrição",
62 "settings.account.headlineUpgrade" : "Atualiza a tua conta e ajuda o Franz", 105 "settings.account.headlineUpgrade" : "Atualize a sua conta e ajude o Franz",
63 "settings.account.invoiceDownload" : "Descarregar", 106 "settings.account.invoiceDownload" : "Transferir",
64 "settings.account.manageSubscription.label" : "Gerir as minhas subscrições", 107 "settings.account.manageSubscription.label" : "Gerir as minhas subscrições",
65 "settings.account.successInfo" : "As tuas mudanças foram feitas com sucesso", 108 "settings.account.successInfo" : "As suas mudanças foram efetuadas com sucesso",
66 "settings.account.tryReloadUserInfoRequest" : "Tentar novamente", 109 "settings.account.tryReloadUserInfoRequest" : "Tentar novamente",
67 "settings.account.userInfoRequestFailed" : "Não é possível carregar a informação do utilizador.", 110 "settings.account.userInfoRequestFailed" : "Não é possível carregar a informação do utilizador",
68 "settings.app.buttonClearAllCache" : "Limpar a 'cache'", 111 "settings.app.buttonClearAllCache" : "Limpar cache",
69 "settings.app.buttonInstallUpdate" : "Reiniciar & Instalar Atualizações", 112 "settings.app.buttonInstallUpdate" : "Reiniciar & Instalar Atualizações",
70 "settings.app.buttonSearchForUpdate" : "Procurar por atualizações", 113 "settings.app.buttonSearchForUpdate" : "Procurar por atualizações",
71 "settings.app.cacheInfo" : "O 'chache' do Franz está a usar {size} do espaço do disco.", 114 "settings.app.cacheInfo" : "A cache do Franz está a usar {size} do espaço em disco.",
72 "settings.app.currentVersion" : "Versão atual", 115 "settings.app.currentVersion" : "Versão atual",
73 "settings.app.form.autoLaunchInBackground" : "Abrir em segundo plano", 116 "settings.app.form.autoLaunchInBackground" : "Abrir em segundo plano",
74 "settings.app.form.autoLaunchOnStart" : "Inicar o Franz quando inciar o computador", 117 "settings.app.form.autoLaunchOnStart" : "Iniciar o Franz com o início de sessão",
75 "settings.app.form.beta" : "Incluir versões instáveis (beta)", 118 "settings.app.form.beta" : "Incluir versões instáveis (beta)",
76 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar", 119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
120 "settings.app.form.enableMenuBar" : "Mostrar Franz na Barra de Menu",
77 "settings.app.form.enableSpellchecking" : "Ativar verificação ortográfica", 121 "settings.app.form.enableSpellchecking" : "Ativar verificação ortográfica",
78 "settings.app.form.enableSystemTray" : "Mostrar o Franz na bandeja do sistema", 122 "settings.app.form.enableSystemTray" : "Mostrar o Franz na barra do sistema",
79 "settings.app.form.hideDockIcon" : "Hide Franz icon in Dock", 123 "settings.app.form.hideDockIcon" : "Esconder ícone na Dock",
80 "settings.app.form.language" : "Idioma:", 124 "settings.app.form.language" : "Idioma:",
81 "settings.app.form.minimizeToSystemTray" : "Minimizar o Franz para a bandeja do sistema", 125 "settings.app.form.minimizeToSystemTray" : "Minimizar o Franz para a barra do sistema",
82 "settings.app.form.runInBackground" : "Manter o Franz em segundo plano ao fechar a janela", 126 "settings.app.form.runInBackground" : "Manter o Franz em segundo plano ao fechar a janela",
83 "settings.app.form.showDisabledServices" : "Apresentar separadores de serviços desactivados", 127 "settings.app.form.showDisabledServices" : "Apresentar separadores de serviços desativados",
84 "settings.app.form.showMessagesBadgesWhenMuted" : "Apresentar icon com o número de mensagens não lidas quando as notificações estão desactivadas", 128 "settings.app.form.showMessagesBadgesWhenMuted" : "Apresentar emblema com o número de mensagens não lidas quando as notificações estão desactivadas",
85 "settings.app.headline" : "Definições", 129 "settings.app.headline" : "Definições",
86 "settings.app.headlineAdvanced" : "Avançado", 130 "settings.app.headlineAdvanced" : "Avançado",
87 "settings.app.headlineAppearance" : "Aparência", 131 "settings.app.headlineAppearance" : "Aparência",
88 "settings.app.headlineGeneral" : "Geral", 132 "settings.app.headlineGeneral" : "Geral",
89 "settings.app.headlineLanguage" : "Idioma:", 133 "settings.app.headlineLanguage" : "Idioma:",
90 "settings.app.headlineUpdates" : "Atualizações", 134 "settings.app.headlineUpdates" : "Atualizações",
91 "settings.app.restartRequired" : "Alterações requerem reiniciar", 135 "settings.app.restartRequired" : "Alterações requerem reinício",
92 "settings.app.subheadlineCache" : "Cache", 136 "settings.app.subheadlineCache" : "Cache",
93 "settings.app.translationHelp" : "Ajude a traduzir a Franz para a sua língua. ", 137 "settings.app.translationHelp" : "Ajude-nos a traduzir a Franz para a sua língua.",
94 "settings.app.updateStatusAvailable" : "Atualização disponivel, a descarregar...", 138 "settings.app.updateStatusAvailable" : "Atualização disponivel, a transferir...",
95 "settings.app.updateStatusSearching" : "Está a procurar atualizações", 139 "settings.app.updateStatusSearching" : "Procurando por atualizações",
96 "settings.app.updateStatusUpToDate" : "Estás a usar a última versão do Franz", 140 "settings.app.updateStatusUpToDate" : "Está a usar a versão mais recente do Franz",
97 "settings.invite.headline" : "Invite Friends", 141 "settings.invite.headline" : "Convide amigos",
98 "settings.navigation.account" : "Conta", 142 "settings.navigation.account" : "Conta",
99 "settings.navigation.availableServices" : "Serviços disponíveis", 143 "settings.navigation.availableServices" : "Serviços disponíveis",
100 "settings.navigation.inviteFriends" : "Invite Friends", 144 "settings.navigation.inviteFriends" : "Convide amigos",
101 "settings.navigation.logout" : "Terminar Sessão", 145 "settings.navigation.logout" : "Terminar sessão",
102 "settings.navigation.settings" : "Definições", 146 "settings.navigation.settings" : "Definições",
103 "settings.navigation.yourServices" : "Os teus serviços", 147 "settings.navigation.yourServices" : "Os seus serviços",
104 "settings.recipes.all" : "Todos os serviços", 148 "settings.recipes.all" : "Todos os serviços",
105 "settings.recipes.dev" : "Desenvolvimento", 149 "settings.recipes.dev" : "Desenvolvimento",
106 "settings.recipes.headline" : "Serviços disponíveis", 150 "settings.recipes.headline" : "Serviços disponíveis",
107 "settings.recipes.missingService" : "A faltar um serviço?", 151 "settings.recipes.missingService" : "A faltar um serviço?",
108 "settings.recipes.mostPopular" : "Os mais populares", 152 "settings.recipes.mostPopular" : "Os mais populares",
109 "settings.recipes.nothingFound" : "Desculpa, mas não existe nenhum serviço com este termo.", 153 "settings.recipes.nothingFound" : "Lamentamos, mas não existe nenhum serviço corresponde ao pesquisado.",
110 "settings.recipes.servicesSuccessfulAddedInfo" : "Serviço adicionado", 154 "settings.recipes.servicesSuccessfulAddedInfo" : "Serviço adicionado",
111 "settings.searchService" : "Pesquisa de serviço", 155 "settings.searchService" : "Pesquisa de serviço",
112 "settings.service.error.goBack" : "Voltar aos serviços", 156 "settings.service.error.goBack" : "Voltar aos serviços",
@@ -115,89 +159,93 @@
115 "settings.service.form.addServiceHeadline" : "Adicionar {name}", 159 "settings.service.form.addServiceHeadline" : "Adicionar {name}",
116 "settings.service.form.availableServices" : "Serviços disponíveis", 160 "settings.service.form.availableServices" : "Serviços disponíveis",
117 "settings.service.form.customUrl" : "Servidor personalizado", 161 "settings.service.form.customUrl" : "Servidor personalizado",
118 "settings.service.form.customUrlPremiumInfo" : "Para adicionar serviços de hospedagem própria, precisas de uma Conta Premium Franz.", 162 "settings.service.form.customUrlPremiumInfo" : "Para adicionar serviços com domínio próprio, é necessário uma Conta Premium.",
119 "settings.service.form.customUrlUpgradeAccount" : "Atualizar a conta", 163 "settings.service.form.customUrlUpgradeAccount" : "Atualizar a conta",
120 "settings.service.form.customUrlValidationError" : "Não foi possível validar o servidor {nome}.", 164 "settings.service.form.customUrlValidationError" : "Não foi possível validar o servidor {nome}.",
121 "settings.service.form.deleteButton" : "Apagar serviço", 165 "settings.service.form.deleteButton" : "Apagar serviço",
122 "settings.service.form.editServiceHeadline" : "Editar {name}", 166 "settings.service.form.editServiceHeadline" : "Editar {name}",
123 "settings.service.form.enableAudio" : "Activar áudio", 167 "settings.service.form.enableAudio" : "Ativar áudio",
124 "settings.service.form.enableBadge" : "Mostrar emblema de mensagens não vistas", 168 "settings.service.form.enableBadge" : "Mostrar emblema de mensagens não vistas",
125 "settings.service.form.enableNotification" : "Ativar notificações", 169 "settings.service.form.enableNotification" : "Ativar notificações",
126 "settings.service.form.enableService" : "Ativar serviço", 170 "settings.service.form.enableService" : "Ativar serviço",
127 "settings.service.form.headlineBadges" : "Emblema de mensagem não vista", 171 "settings.service.form.headlineBadges" : "Emblema de mensagem não vista",
128 "settings.service.form.headlineGeneral" : "Geral", 172 "settings.service.form.headlineGeneral" : "Geral",
129 "settings.service.form.headlineNotifications" : "Notificações", 173 "settings.service.form.headlineNotifications" : "Notificações",
130 "settings.service.form.icon" : "Icon personalizável", 174 "settings.service.form.icon" : "Personalizar ícone",
131 "settings.service.form.iconDelete" : "Apagar", 175 "settings.service.form.iconDelete" : "Apagar",
132 "settings.service.form.iconUpload" : "Arraste a sua imagem, ou clique aqui", 176 "settings.service.form.iconUpload" : "Arraste a sua imagem, ou clique aqui",
133 "settings.service.form.indirectMessageInfo" : "Serás notificado sobre todas as novas mensagens de um canal, não só @username, @channel, @here, ...", 177 "settings.service.form.indirectMessageInfo" : "Será notificado sobre todas as novas mensagens de um canal, não apenas @username, @channel, @here, ...",
134 "settings.service.form.indirectMessages" : "Mostrar o emblema da mensagem para todas as novas mensagens", 178 "settings.service.form.indirectMessages" : "Mostrar o emblema da mensagem para todas as novas mensagens",
135 "settings.service.form.isMutedInfo" : "Quando desactivado, todo o audio das notificações estará em silêncio", 179 "settings.service.form.isMutedInfo" : "Quando desativado, todas as notificações de áudio serão silenciadas",
136 "settings.service.form.name" : "Nome", 180 "settings.service.form.name" : "Nome",
137 "settings.service.form.saveButton" : "Guardar serviço", 181 "settings.service.form.saveButton" : "Guardar serviço",
138 "settings.service.form.tabHosted" : "Hospedado", 182 "settings.service.form.tabHosted" : "Com domínio",
139 "settings.service.form.tabOnPremise" : "Hospedado por si próprio â­ï¸", 183 "settings.service.form.tabOnPremise" : "Com domínio próprio â­ï¸",
140 "settings.service.form.team" : "Equipa", 184 "settings.service.form.team" : "Equipa",
141 "settings.service.form.useHostedService" : "Utilize o serviço oferecido por {name}.", 185 "settings.service.form.useHostedService" : "Utilize o serviço oferecido por {name}.",
142 "settings.service.form.yourServices" : "Os teus serviços", 186 "settings.service.form.yourServices" : "Os seus serviços",
143 "settings.services.deletedInfo" : "O serviço foi apagado", 187 "settings.services.deletedInfo" : "O serviço foi apagado",
144 "settings.services.discoverServices" : "Descobrir serviços", 188 "settings.services.discoverServices" : "Descobrir serviços",
145 "settings.services.headline" : "Os teus serviços", 189 "settings.services.headline" : "Os seus serviços",
146 "settings.services.noServicesAdded" : "Ainda não adicionaste nenhum serviço.", 190 "settings.services.noServicesAdded" : "Ainda não adicionou nenhum serviço.",
147 "settings.services.tooltip.isDisabled" : "O serviço está desativado", 191 "settings.services.tooltip.isDisabled" : "O serviço está desativado",
148 "settings.services.tooltip.isMuted" : "Todos os sons estão silenciados", 192 "settings.services.tooltip.isMuted" : "Todos os sons estão silenciados",
149 "settings.services.tooltip.notificationsDisabled" : "As notificações estão desabilitadas", 193 "settings.services.tooltip.notificationsDisabled" : "As notificações estão desativadas",
150 "settings.services.updatedInfo" : "As alterações foram guardadas", 194 "settings.services.updatedInfo" : "As alterações foram guardadas",
151 "settings.user.form.accountType.company" : "Empresa", 195 "settings.user.form.accountType.company" : "Empresa",
152 "settings.user.form.accountType.individual" : "Individual", 196 "settings.user.form.accountType.individual" : "Indivíduo",
153 "settings.user.form.accountType.label" : "Tipo de conta", 197 "settings.user.form.accountType.label" : "Tipo de conta",
154 "settings.user.form.accountType.non-profit" : "Sem fins lucrativos", 198 "settings.user.form.accountType.non-profit" : "Sem fins lucrativos",
155 "settings.user.form.currentPassword" : "Password atual", 199 "settings.user.form.currentPassword" : "Palavra-passe atual",
156 "settings.user.form.email" : "Email", 200 "settings.user.form.email" : "Email",
157 "settings.user.form.firstname" : "Primeiro Nome", 201 "settings.user.form.firstname" : "Primeiro Nome",
158 "settings.user.form.lastname" : "Último Nome", 202 "settings.user.form.lastname" : "Último Nome",
159 "settings.user.form.newPassword" : "Nova password", 203 "settings.user.form.newPassword" : "Nova palavra-passe",
160 "sidebar.addNewService" : "Adicionar um novo serviço", 204 "sidebar.addNewService" : "Adicionar um novo serviço",
161 "sidebar.muteApp" : "Desactivar notificações e audio", 205 "sidebar.muteApp" : "Desativar notificações e áudio",
162 "sidebar.settings" : "Definições", 206 "sidebar.settings" : "Definições",
163 "sidebar.unmuteApp" : "Activar notificações e audio", 207 "sidebar.unmuteApp" : "Ativar notificações e áudio",
164 "signup.company.label" : "Empresa", 208 "signup.company.label" : "Empresa",
165 "signup.email.label" : "Endereço de e-mail", 209 "signup.email.label" : "Endereço de e-mail",
166 "signup.emailDuplicate" : "Uma conta já tem esse email associado", 210 "signup.emailDuplicate" : "Já existe uma conta associada a este endereço",
167 "signup.firstname.label" : "Primeiro Nome", 211 "signup.firstname.label" : "Primeiro Nome",
168 "signup.headline" : "Iniciar Sessão", 212 "signup.headline" : "Criar conta",
169 "signup.lastname.label" : "Último Nome", 213 "signup.lastname.label" : "Último Nome",
170 "signup.legal.info" : "Ao criar uma conta Franz, aceitas", 214 "signup.legal.info" : "Ao criar uma conta Franz, aceita",
171 "signup.legal.privacy" : "Declaração de privacidade", 215 "signup.legal.privacy" : "Declaração de privacidade",
172 "signup.legal.terms" : "Termos de Serviço", 216 "signup.legal.terms" : "Termos de Serviço",
173 "signup.link.login" : "Já tens uma conta?", 217 "signup.link.login" : "Já tem conta, iniciar sessão?",
174 "signup.password.label" : "Password", 218 "signup.password.label" : "Palavra-passe",
175 "signup.submit.label" : "Criar uma Conta", 219 "signup.submit.label" : "Criar uma conta",
176 "subscription.euTaxInfo" : "Residentes EU: Taxas locais podem ser aplicadas", 220 "subscription.euTaxInfo" : "Residentes EU: Taxas locais podem ser aplicadas",
177 "subscription.features.ads" : "Sem anúncios, para sempre!", 221 "subscription.features.ads" : "Sem anúncios, para sempre!",
178 "subscription.features.comingSoon" : "Em breve!", 222 "subscription.features.comingSoon" : "Em breve!",
179 "subscription.features.customServices" : "Serviços privados para ti e para os teus amigos\/colegas", 223 "subscription.features.customServices" : "Serviços privados para si e para a sua equipa",
180 "subscription.features.encryptedSync" : "Sincronização encriptada de serviços", 224 "subscription.features.encryptedSync" : "Sincronização encriptada de serviços",
181 "subscription.features.onpremise" : "Adicionar serviços 'on-premise'\/hosted como o HipChat", 225 "subscription.features.onpremise" : "Adicionar serviços 'on-premise'\/hosted como o HipChat",
182 "subscription.features.vpn" : "Suporte para proxy\/VPN", 226 "subscription.features.vpn" : "Suporte para proxy\/VPN",
183 "subscription.includedFeatures" : "Ao pagares para o Franz Premium, tens acesso a", 227 "subscription.includedFeatures" : "Ao subscrever uma Conta Premium, tem acesso a",
184 "subscription.paymentSessionError" : "Erro no serviço de pagamento", 228 "subscription.paymentSessionError" : "Erro no serviço de pagamento",
185 "subscription.submit.label" : "Quero ajudar o desenvolvimento do Franz", 229 "subscription.submit.label" : "Pretendo ajudar o desenvolvimento do Franz",
186 "subscription.type.free" : "Gratuito", 230 "subscription.type.free" : "Gratuito",
187 "subscription.type.month" : "mês", 231 "subscription.type.month" : "mês",
188 "subscription.type.year" : "ano", 232 "subscription.type.year" : "ano",
189 "subscriptionPopup.buttonCancel" : "Cancelar", 233 "subscriptionPopup.buttonCancel" : "Cancelar",
190 "subscriptionPopup.buttonDone" : "Concluído", 234 "subscriptionPopup.buttonDone" : "Concluído",
191 "tabs.item.deleteService" : "Apagar serviço", 235 "tabs.item.deleteService" : "Apagar serviço",
192 "tabs.item.disableAudio" : "Desactivar audio", 236 "tabs.item.disableAudio" : "Desativar áudio",
193 "tabs.item.disableNotifications" : "Desativar notificações", 237 "tabs.item.disableNotifications" : "Desativar notificações",
194 "tabs.item.disableService" : "Desativar serviço", 238 "tabs.item.disableService" : "Desativar serviço",
195 "tabs.item.edit" : "Editar", 239 "tabs.item.edit" : "Editar",
196 "tabs.item.enableAudio" : "Activar áudio", 240 "tabs.item.enableAudio" : "Ativar áudio",
197 "tabs.item.enableNotification" : "Ativar notificações", 241 "tabs.item.enableNotification" : "Ativar notificações",
198 "tabs.item.enableService" : "Ativar serviço", 242 "tabs.item.enableService" : "Ativar serviço",
199 "tabs.item.reload" : "Recarregar", 243 "tabs.item.reload" : "Recarregar",
200 "welcome.loginButton" : "Fazer login", 244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
248 "welcome.loginButton" : "Iniciar sessão",
201 "welcome.signupButton" : "Criar uma conta gratuita", 249 "welcome.signupButton" : "Criar uma conta gratuita",
202 "welcome.slogan" : "Messaging that works for you" 250 "welcome.slogan" : "Messaging that works for you"
203} 251}
diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json
index 65b61c1e5..ecebc46ee 100644
--- a/src/i18n/locales/ru.json
+++ b/src/i18n/locales/ru.json
@@ -26,13 +26,56 @@
26 "login.serverLogout" : "СеÑÑÐ¸Ñ ÑƒÑтарела, пожалуйÑта, войдите Ñнова.", 26 "login.serverLogout" : "СеÑÑÐ¸Ñ ÑƒÑтарела, пожалуйÑта, войдите Ñнова.",
27 "login.submit.label" : "Вход", 27 "login.submit.label" : "Вход",
28 "login.tokenExpired" : "СеÑÑÐ¸Ñ ÑƒÑтарела, пожалуйÑта, войдите Ñнова.", 28 "login.tokenExpired" : "СеÑÑÐ¸Ñ ÑƒÑтарела, пожалуйÑта, войдите Ñнова.",
29 "menu.app.about" : "О Franz",
30 "menu.app.hide" : "Скрыть",
31 "menu.app.hideOthers" : "Скрыть оÑтальные",
32 "menu.app.quit" : "Выйти",
33 "menu.app.settings" : "ÐаÑтройки",
34 "menu.app.unhide" : "Показать",
35 "menu.edit" : "Редактировать",
36 "menu.edit.copy" : "Копировать",
37 "menu.edit.cut" : "Вырезать",
38 "menu.edit.delete" : "Удалить",
39 "menu.edit.emojiSymbols" : "Эмодзи и Ñимволы",
40 "menu.edit.paste" : "Ð’Ñтавить",
41 "menu.edit.pasteAndMatchStyle" : "Paste And Match Style",
42 "menu.edit.redo" : "Вернуть",
43 "menu.edit.selectAll" : "Выделить вÑÑ‘",
44 "menu.edit.speech" : "Speech",
45 "menu.edit.startDictation" : "Start Dictation",
46 "menu.edit.startSpeaking" : "Start Speaking",
47 "menu.edit.stopSpeaking" : "Stop Speaking",
48 "menu.edit.undo" : "Отменить",
49 "menu.file" : "Файл",
50 "menu.help" : "Справка",
51 "menu.help.changelog" : "Журнал изменений",
52 "menu.help.learnMore" : "Подробнее",
53 "menu.help.privacy" : "Политика конфиденциальноÑти",
54 "menu.help.support" : "Поддержка",
55 "menu.help.tos" : "УÑÐ»Ð¾Ð²Ð¸Ñ Ð¸ÑпользованиÑ",
56 "menu.services" : "Services",
57 "menu.services.addNewService" : "Добавить ÑервиÑ...",
58 "menu.view" : "Вид",
59 "menu.view.enterFullScreen" : "Ðа веÑÑŒ Ñкран",
60 "menu.view.exitFullScreen" : "В окне",
61 "menu.view.reloadFranz" : "Перезагрузить Franz",
62 "menu.view.reloadService" : "ПерезапуÑтить ÑервиÑ",
63 "menu.view.resetZoom" : "ФактичеÑкий размер",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Toggle Full Screen",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Увеличить маÑштаб",
68 "menu.view.zoomOut" : "Уменьшить маÑштаб",
69 "menu.window" : "Окно",
70 "menu.window.close" : "Закрыть",
71 "menu.window.minimize" : "Свернуть",
29 "password.email.label" : "Email адреÑ", 72 "password.email.label" : "Email адреÑ",
30 "password.headline" : "ÐоÑÑтановление паролÑ", 73 "password.headline" : "СбÑоÑить пароль",
31 "password.link.login" : "Вход", 74 "password.link.login" : "Вход",
32 "password.link.signup" : "Создать аккаунт", 75 "password.link.signup" : "Создать учётную запиÑÑŒ пользователÑ",
33 "password.noUser" : "Ðе найдено Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ñ Ñ‚Ð°ÐºÐ¸Ð¼ email", 76 "password.noUser" : "Ðе найдено Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ñ Ñ‚Ð°ÐºÐ¸Ð¼ email",
34 "password.submit.label" : "ВоÑÑтановить", 77 "password.submit.label" : "ПÑинÑÑ‚ÑŒ",
35 "password.successInfo" : "Проверьте Ваш email", 78 "password.successInfo" : "Проверьте Вашу Ñлектронную почту",
36 "pricing.headline" : "Поддержать Franz", 79 "pricing.headline" : "Поддержать Franz",
37 "pricing.link.skipPayment" : "Я не хочу поддерживать разработку Franz.", 80 "pricing.link.skipPayment" : "Я не хочу поддерживать разработку Franz.",
38 "pricing.submit.label" : "Я хочу поддержать разработку Franz", 81 "pricing.submit.label" : "Я хочу поддержать разработку Franz",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "Открывать в фоне", 116 "settings.app.form.autoLaunchInBackground" : "Открывать в фоне",
74 "settings.app.form.autoLaunchOnStart" : "ЗапуÑкать Franz при Ñтарте", 117 "settings.app.form.autoLaunchOnStart" : "ЗапуÑкать Franz при Ñтарте",
75 "settings.app.form.beta" : "Ð’ÐºÐ»ÑŽÑ‡Ð°Ñ Ð±ÐµÑ‚Ð° верÑии", 118 "settings.app.form.beta" : "Ð’ÐºÐ»ÑŽÑ‡Ð°Ñ Ð±ÐµÑ‚Ð° верÑии",
119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
76 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar", 120 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar",
77 "settings.app.form.enableSpellchecking" : "Включить проверку правопиÑаниÑ", 121 "settings.app.form.enableSpellchecking" : "Включить проверку правопиÑаниÑ",
78 "settings.app.form.enableSystemTray" : "Показывать Franz в ÑиÑтемном трее", 122 "settings.app.form.enableSystemTray" : "Показывать Franz в ÑиÑтемном трее",
@@ -127,9 +171,9 @@
127 "settings.service.form.headlineBadges" : "Значки непрочитанных Ñообщений", 171 "settings.service.form.headlineBadges" : "Значки непрочитанных Ñообщений",
128 "settings.service.form.headlineGeneral" : "Общие", 172 "settings.service.form.headlineGeneral" : "Общие",
129 "settings.service.form.headlineNotifications" : "УведомлениÑ", 173 "settings.service.form.headlineNotifications" : "УведомлениÑ",
130 "settings.service.form.icon" : "Ðначок пользователÑ", 174 "settings.service.form.icon" : "ÐеÑÑ‚Ð°Ð½Ð´Ð°Ñ€Ñ‚Ð½Ð°Ñ Ð¸ÐºÐ¾Ð½ÐºÐ°",
131 "settings.service.form.iconDelete" : "Удалить", 175 "settings.service.form.iconDelete" : "Удалить",
132 "settings.service.form.iconUpload" : "Перетащите картинку или наÐмите Ñюда", 176 "settings.service.form.iconUpload" : "Перетащите картинку или кликните здеÑÑŒ",
133 "settings.service.form.indirectMessageInfo" : "Ð’Ñ‹ будете получать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð²Ñех Ñообщений, не только Ð´Ð»Ñ @username, @channel, @here, ...", 177 "settings.service.form.indirectMessageInfo" : "Ð’Ñ‹ будете получать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð²Ñех Ñообщений, не только Ð´Ð»Ñ @username, @channel, @here, ...",
134 "settings.service.form.indirectMessages" : "Показывать значок уведомлений Ð´Ð»Ñ Ð²Ñех новых Ñообщений", 178 "settings.service.form.indirectMessages" : "Показывать значок уведомлений Ð´Ð»Ñ Ð²Ñех новых Ñообщений",
135 "settings.service.form.isMutedInfo" : "Когда выключено, вÑе звуковые ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÑƒÑ‚ отключены", 179 "settings.service.form.isMutedInfo" : "Когда выключено, вÑе звуковые ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÑƒÑ‚ отключены",
@@ -192,11 +236,15 @@
192 "tabs.item.disableAudio" : "Отключить звук", 236 "tabs.item.disableAudio" : "Отключить звук",
193 "tabs.item.disableNotifications" : "Отключить уведомлениÑ", 237 "tabs.item.disableNotifications" : "Отключить уведомлениÑ",
194 "tabs.item.disableService" : "Отключить ÑервиÑ", 238 "tabs.item.disableService" : "Отключить ÑервиÑ",
195 "tabs.item.edit" : "Редактировать", 239 "tabs.item.edit" : "Правка",
196 "tabs.item.enableAudio" : "Включить аудио", 240 "tabs.item.enableAudio" : "Включить аудио",
197 "tabs.item.enableNotification" : "Включить уведомлениÑ", 241 "tabs.item.enableNotification" : "Включить уведомлениÑ",
198 "tabs.item.enableService" : "Включить Ñлужбу", 242 "tabs.item.enableService" : "Включить Ñлужбу",
199 "tabs.item.reload" : "Перезагрузить", 243 "tabs.item.reload" : "Перезагрузить",
244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
200 "welcome.loginButton" : "Вход", 248 "welcome.loginButton" : "Вход",
201 "welcome.signupButton" : "Создать аккаунт", 249 "welcome.signupButton" : "Создать аккаунт",
202 "welcome.slogan" : "Общение, которое проÑто работает" 250 "welcome.slogan" : "Общение, которое проÑто работает"
diff --git a/src/i18n/locales/sk.json b/src/i18n/locales/sk.json
index 35f06531f..c43e998a4 100644
--- a/src/i18n/locales/sk.json
+++ b/src/i18n/locales/sk.json
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "Relácia vypršala, prihláste sa prosím znova.", 26 "login.serverLogout" : "Relácia vypršala, prihláste sa prosím znova.",
27 "login.submit.label" : "Prihlásiť sa", 27 "login.submit.label" : "Prihlásiť sa",
28 "login.tokenExpired" : "Relácia vypršala, prosím prihláste sa znova.", 28 "login.tokenExpired" : "Relácia vypršala, prosím prihláste sa znova.",
29 "menu.app.about" : "About Franz",
30 "menu.app.hide" : "Hide",
31 "menu.app.hideOthers" : "Hide Others",
32 "menu.app.quit" : "Quit",
33 "menu.app.settings" : "Nastavenia",
34 "menu.app.unhide" : "Unhide",
35 "menu.edit" : "Upraviť",
36 "menu.edit.copy" : "Copy",
37 "menu.edit.cut" : "Cut",
38 "menu.edit.delete" : "Zmazať",
39 "menu.edit.emojiSymbols" : "Emoji & Symbols",
40 "menu.edit.paste" : "Paste",
41 "menu.edit.pasteAndMatchStyle" : "Paste And Match Style",
42 "menu.edit.redo" : "Redo",
43 "menu.edit.selectAll" : "Select All",
44 "menu.edit.speech" : "Speech",
45 "menu.edit.startDictation" : "Start Dictation",
46 "menu.edit.startSpeaking" : "Start Speaking",
47 "menu.edit.stopSpeaking" : "Stop Speaking",
48 "menu.edit.undo" : "Undo",
49 "menu.file" : "File",
50 "menu.help" : "Help",
51 "menu.help.changelog" : "Changelog",
52 "menu.help.learnMore" : "Learn More",
53 "menu.help.privacy" : "Vyhlásenie o ochrane súkromia",
54 "menu.help.support" : "Support",
55 "menu.help.tos" : "Terms of Service",
56 "menu.services" : "Services",
57 "menu.services.addNewService" : "Add New Service...",
58 "menu.view" : "View",
59 "menu.view.enterFullScreen" : "Enter Full Screen",
60 "menu.view.exitFullScreen" : "Exit Full Screen",
61 "menu.view.reloadFranz" : "Reload Franz",
62 "menu.view.reloadService" : "Reload Service",
63 "menu.view.resetZoom" : "Actual Size",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Toggle Full Screen",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Zoom In",
68 "menu.view.zoomOut" : "Zoom Out",
69 "menu.window" : "Window",
70 "menu.window.close" : "Close",
71 "menu.window.minimize" : "Minimize",
29 "password.email.label" : "Emailová adresa", 72 "password.email.label" : "Emailová adresa",
30 "password.headline" : "Vynulovať heslo", 73 "password.headline" : "Vynulovať heslo",
31 "password.link.login" : "Prihlásenie do vaÅ¡eho úÄtu", 74 "password.link.login" : "Prihlásenie do vaÅ¡eho úÄtu",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "Otvoriť na pozadí", 116 "settings.app.form.autoLaunchInBackground" : "Otvoriť na pozadí",
74 "settings.app.form.autoLaunchOnStart" : "Spustiť Franz pri štarte", 117 "settings.app.form.autoLaunchOnStart" : "Spustiť Franz pri štarte",
75 "settings.app.form.beta" : "Vrátane beta verzií", 118 "settings.app.form.beta" : "Vrátane beta verzií",
119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
76 "settings.app.form.enableMenuBar" : "Zobraz Franz v Menu bare", 120 "settings.app.form.enableMenuBar" : "Zobraz Franz v Menu bare",
77 "settings.app.form.enableSpellchecking" : "Povoliť zvýraznenie preklepov", 121 "settings.app.form.enableSpellchecking" : "Povoliť zvýraznenie preklepov",
78 "settings.app.form.enableSystemTray" : "Zobrazovať Franz v systémovej lište", 122 "settings.app.form.enableSystemTray" : "Zobrazovať Franz v systémovej lište",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Povoliť oznámenia", 241 "tabs.item.enableNotification" : "Povoliť oznámenia",
198 "tabs.item.enableService" : "Povoliť službu", 242 "tabs.item.enableService" : "Povoliť službu",
199 "tabs.item.reload" : "Obnoviť", 243 "tabs.item.reload" : "Obnoviť",
244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
200 "welcome.loginButton" : "Prihláste sa k vášmu úÄtu", 248 "welcome.loginButton" : "Prihláste sa k vášmu úÄtu",
201 "welcome.signupButton" : "VytvoriÅ¥ užívateľský úÄet zdarma", 249 "welcome.signupButton" : "VytvoriÅ¥ užívateľský úÄet zdarma",
202 "welcome.slogan" : "Správy, ktoré pracujú pre vás" 250 "welcome.slogan" : "Správy, ktoré pracujú pre vás"
diff --git a/src/i18n/locales/sr.json b/src/i18n/locales/sr.json
index 90e1a04b9..1f31bc834 100644
--- a/src/i18n/locales/sr.json
+++ b/src/i18n/locales/sr.json
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "Vaša sesija je istekla, prijavite se ponovo.", 26 "login.serverLogout" : "Vaša sesija je istekla, prijavite se ponovo.",
27 "login.submit.label" : "Prijavite se", 27 "login.submit.label" : "Prijavite se",
28 "login.tokenExpired" : "Vaša sesija je istekla, prijavite se ponovo.", 28 "login.tokenExpired" : "Vaša sesija je istekla, prijavite se ponovo.",
29 "menu.app.about" : "About Franz",
30 "menu.app.hide" : "Hide",
31 "menu.app.hideOthers" : "Hide Others",
32 "menu.app.quit" : "Quit",
33 "menu.app.settings" : "Postavke",
34 "menu.app.unhide" : "Unhide",
35 "menu.edit" : "Uredi",
36 "menu.edit.copy" : "Copy",
37 "menu.edit.cut" : "Cut",
38 "menu.edit.delete" : "Delete",
39 "menu.edit.emojiSymbols" : "Emoji & Symbols",
40 "menu.edit.paste" : "Paste",
41 "menu.edit.pasteAndMatchStyle" : "Paste And Match Style",
42 "menu.edit.redo" : "Redo",
43 "menu.edit.selectAll" : "Select All",
44 "menu.edit.speech" : "Speech",
45 "menu.edit.startDictation" : "Start Dictation",
46 "menu.edit.startSpeaking" : "Start Speaking",
47 "menu.edit.stopSpeaking" : "Stop Speaking",
48 "menu.edit.undo" : "Undo",
49 "menu.file" : "File",
50 "menu.help" : "Help",
51 "menu.help.changelog" : "Changelog",
52 "menu.help.learnMore" : "Learn More",
53 "menu.help.privacy" : "Izjava o privatnosti",
54 "menu.help.support" : "Support",
55 "menu.help.tos" : "Terms of Service",
56 "menu.services" : "Services",
57 "menu.services.addNewService" : "Add New Service...",
58 "menu.view" : "View",
59 "menu.view.enterFullScreen" : "Enter Full Screen",
60 "menu.view.exitFullScreen" : "Exit Full Screen",
61 "menu.view.reloadFranz" : "Reload Franz",
62 "menu.view.reloadService" : "Reload Service",
63 "menu.view.resetZoom" : "Actual Size",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Toggle Full Screen",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Zoom In",
68 "menu.view.zoomOut" : "Zoom Out",
69 "menu.window" : "Window",
70 "menu.window.close" : "Close",
71 "menu.window.minimize" : "Minimize",
29 "password.email.label" : "Vaša e-adresa", 72 "password.email.label" : "Vaša e-adresa",
30 "password.headline" : "Stvorite novu zaporku", 73 "password.headline" : "Stvorite novu zaporku",
31 "password.link.login" : "Prijavite se na VaÅ¡ raÄun", 74 "password.link.login" : "Prijavite se na VaÅ¡ raÄun",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "Otvori u pozadini", 116 "settings.app.form.autoLaunchInBackground" : "Otvori u pozadini",
74 "settings.app.form.autoLaunchOnStart" : "Pokreni Franz sa sistemom", 117 "settings.app.form.autoLaunchOnStart" : "Pokreni Franz sa sistemom",
75 "settings.app.form.beta" : "Obuhvati i beta verzije", 118 "settings.app.form.beta" : "Obuhvati i beta verzije",
119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
76 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar", 120 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar",
77 "settings.app.form.enableSpellchecking" : "Omogući provjeru pravopisa", 121 "settings.app.form.enableSpellchecking" : "Omogući provjeru pravopisa",
78 "settings.app.form.enableSystemTray" : "Prikaži aplikaciju u sustavskoj traci", 122 "settings.app.form.enableSystemTray" : "Prikaži aplikaciju u sustavskoj traci",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Omogući obavijesti", 241 "tabs.item.enableNotification" : "Omogući obavijesti",
198 "tabs.item.enableService" : "Omogući usluge", 242 "tabs.item.enableService" : "Omogući usluge",
199 "tabs.item.reload" : "Ponovno uÄitavanje", 243 "tabs.item.reload" : "Ponovno uÄitavanje",
244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
200 "welcome.loginButton" : "Prijavite se na raÄun", 248 "welcome.loginButton" : "Prijavite se na raÄun",
201 "welcome.signupButton" : "Stvorite novi korisniÄki raÄun", 249 "welcome.signupButton" : "Stvorite novi korisniÄki raÄun",
202 "welcome.slogan" : "Poruke koje su stvorene za tebe" 250 "welcome.slogan" : "Poruke koje su stvorene za tebe"
diff --git a/src/i18n/locales/tr.json b/src/i18n/locales/tr.json
index bcf0dd483..d03077d8d 100644
--- a/src/i18n/locales/tr.json
+++ b/src/i18n/locales/tr.json
@@ -16,7 +16,7 @@
16 "invite.name.label" : "Ad", 16 "invite.name.label" : "Ad",
17 "invite.skip.label" : "Daha sonra yapmak istiyorum", 17 "invite.skip.label" : "Daha sonra yapmak istiyorum",
18 "invite.submit.label" : "Davetiye gönder", 18 "invite.submit.label" : "Davetiye gönder",
19 "invite.successInfo" : "Invitations sent successfully", 19 "invite.successInfo" : "Davetiyeler başarıyla gönderildi",
20 "login.email.label" : "E-posta adresi", 20 "login.email.label" : "E-posta adresi",
21 "login.headline" : "Oturum Aç", 21 "login.headline" : "Oturum Aç",
22 "login.invalidCredentials" : "Yanlış parola ya da e-posta adresi", 22 "login.invalidCredentials" : "Yanlış parola ya da e-posta adresi",
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "Oturum süreniz dolmuş, lütfen tekrar giriş yapın.", 26 "login.serverLogout" : "Oturum süreniz dolmuş, lütfen tekrar giriş yapın.",
27 "login.submit.label" : "Oturum Aç", 27 "login.submit.label" : "Oturum Aç",
28 "login.tokenExpired" : "Oturum zaman aşımına uğradı, lütfen tekrar giriş yapın.", 28 "login.tokenExpired" : "Oturum zaman aşımına uğradı, lütfen tekrar giriş yapın.",
29 "menu.app.about" : "Franz Hakkında",
30 "menu.app.hide" : "Gizle",
31 "menu.app.hideOthers" : "DiÄŸerlerini Gizle",
32 "menu.app.quit" : "Çıkış",
33 "menu.app.settings" : "Ayarlar",
34 "menu.app.unhide" : "Göster",
35 "menu.edit" : "Düzenle",
36 "menu.edit.copy" : "Kopyala",
37 "menu.edit.cut" : "Kes",
38 "menu.edit.delete" : "Sil",
39 "menu.edit.emojiSymbols" : "Emoji & Semboller",
40 "menu.edit.paste" : "Yapıştır",
41 "menu.edit.pasteAndMatchStyle" : "Yapıştır ve Stili Eşleştir",
42 "menu.edit.redo" : "Yeniden Yap",
43 "menu.edit.selectAll" : "Hepsini Seç",
44 "menu.edit.speech" : "KonuÅŸma",
45 "menu.edit.startDictation" : "Ä°mlaya BaÅŸla",
46 "menu.edit.startSpeaking" : "KonuÅŸmaya BaÅŸla",
47 "menu.edit.stopSpeaking" : "Konuşmayı Bırak\/Durdur",
48 "menu.edit.undo" : "Geri al",
49 "menu.file" : "Dosya",
50 "menu.help" : "Yardım",
51 "menu.help.changelog" : "Değişim Günlüğü",
52 "menu.help.learnMore" : "Daha Fazlasını Öğrenin",
53 "menu.help.privacy" : "Gizlilik Sözleşmesi",
54 "menu.help.support" : "Destek",
55 "menu.help.tos" : "Kullanım Şartları",
56 "menu.services" : "Hizmetler",
57 "menu.services.addNewService" : "Yeni Servis Ekle",
58 "menu.view" : "Görünüm",
59 "menu.view.enterFullScreen" : "Tam Ekrana Geç",
60 "menu.view.exitFullScreen" : "Tam Ekrandan Çık",
61 "menu.view.reloadFranz" : "Franz'ı Yeniden Yükle",
62 "menu.view.reloadService" : "Servisi Tekrar Yükle",
63 "menu.view.resetZoom" : "Orijinal Boyut",
64 "menu.view.toggleDevTools" : "Geliştirici Araçlarına Geç",
65 "menu.view.toggleFullScreen" : "Tam Ekrana Geç",
66 "menu.view.toggleServiceDevTools" : "Hizmet Geliştirici Araçlarını Değiştir",
67 "menu.view.zoomIn" : "Yakınlaştır",
68 "menu.view.zoomOut" : "Uzaklaştır",
69 "menu.window" : "Pencere",
70 "menu.window.close" : "Kapat",
71 "menu.window.minimize" : "Simge Durumuna Küçült",
29 "password.email.label" : "E-posta adresi", 72 "password.email.label" : "E-posta adresi",
30 "password.headline" : "Parola sıfırla", 73 "password.headline" : "Parola sıfırla",
31 "password.link.login" : "Hesabına giriş yap", 74 "password.link.login" : "Hesabına giriş yap",
@@ -73,10 +116,11 @@
73 "settings.app.form.autoLaunchInBackground" : "Arka planda aç", 116 "settings.app.form.autoLaunchInBackground" : "Arka planda aç",
74 "settings.app.form.autoLaunchOnStart" : "Franz'ı başlangıçta aç", 117 "settings.app.form.autoLaunchOnStart" : "Franz'ı başlangıçta aç",
75 "settings.app.form.beta" : "Beta versiyonları dahil et", 118 "settings.app.form.beta" : "Beta versiyonları dahil et",
76 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar", 119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
120 "settings.app.form.enableMenuBar" : "Menü çubuğunda Franz'ı göster",
77 "settings.app.form.enableSpellchecking" : "Yazım denetimini etkinleştir", 121 "settings.app.form.enableSpellchecking" : "Yazım denetimini etkinleştir",
78 "settings.app.form.enableSystemTray" : "Franz'ı sistem tepsisinde göster", 122 "settings.app.form.enableSystemTray" : "Franz'ı sistem tepsisinde göster",
79 "settings.app.form.hideDockIcon" : "Hide Franz icon in Dock", 123 "settings.app.form.hideDockIcon" : "Franz Simgesini Gösterme",
80 "settings.app.form.language" : "Dil", 124 "settings.app.form.language" : "Dil",
81 "settings.app.form.minimizeToSystemTray" : "Franz'ı sistem tepsisine küçült", 125 "settings.app.form.minimizeToSystemTray" : "Franz'ı sistem tepsisine küçült",
82 "settings.app.form.runInBackground" : "Pencereyi kapatırken Franz'ı arka planda tut", 126 "settings.app.form.runInBackground" : "Pencereyi kapatırken Franz'ı arka planda tut",
@@ -94,10 +138,10 @@
94 "settings.app.updateStatusAvailable" : "Güncelleme mevcut, indiriliyor...", 138 "settings.app.updateStatusAvailable" : "Güncelleme mevcut, indiriliyor...",
95 "settings.app.updateStatusSearching" : "Güncellemeleri denetliyor", 139 "settings.app.updateStatusSearching" : "Güncellemeleri denetliyor",
96 "settings.app.updateStatusUpToDate" : "Franz'ın son versiyonunu kullanıyorsun", 140 "settings.app.updateStatusUpToDate" : "Franz'ın son versiyonunu kullanıyorsun",
97 "settings.invite.headline" : "Invite Friends", 141 "settings.invite.headline" : "Arkadaşlarını davet et",
98 "settings.navigation.account" : "Hesap", 142 "settings.navigation.account" : "Hesap",
99 "settings.navigation.availableServices" : "Mevcut servisler", 143 "settings.navigation.availableServices" : "Mevcut servisler",
100 "settings.navigation.inviteFriends" : "Invite Friends", 144 "settings.navigation.inviteFriends" : "Arkadaşlarını Davet Et",
101 "settings.navigation.logout" : "Çıkış", 145 "settings.navigation.logout" : "Çıkış",
102 "settings.navigation.settings" : "Ayarlar", 146 "settings.navigation.settings" : "Ayarlar",
103 "settings.navigation.yourServices" : "Servislerin", 147 "settings.navigation.yourServices" : "Servislerin",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Bildirimleri etkinleÅŸtir", 241 "tabs.item.enableNotification" : "Bildirimleri etkinleÅŸtir",
198 "tabs.item.enableService" : "Servisi etkinleÅŸtir", 242 "tabs.item.enableService" : "Servisi etkinleÅŸtir",
199 "tabs.item.reload" : "Yeniden Yükle", 243 "tabs.item.reload" : "Yeniden Yükle",
244 "validation.email" : "{alan} geçerli değil",
245 "validation.minLength" : "{field} en az {length} karakter uzunluğunda olmalı",
246 "validation.required" : "{field} gereklidir",
247 "validation.url" : "{field} geçerli bir URL değil",
200 "welcome.loginButton" : "Hesabına giriş yap", 248 "welcome.loginButton" : "Hesabına giriş yap",
201 "welcome.signupButton" : "Ãœcretsiz hesap oluÅŸtur", 249 "welcome.signupButton" : "Ãœcretsiz hesap oluÅŸtur",
202 "welcome.slogan" : "Sizin için çalışan mesajlaşma" 250 "welcome.slogan" : "Sizin için çalışan mesajlaşma"
diff --git a/src/i18n/locales/uk.json b/src/i18n/locales/uk.json
index 4baf234ac..3f732b896 100644
--- a/src/i18n/locales/uk.json
+++ b/src/i18n/locales/uk.json
@@ -16,7 +16,7 @@
16 "invite.name.label" : "Ім'Ñ", 16 "invite.name.label" : "Ім'Ñ",
17 "invite.skip.label" : "Я хочу зробити це пізніше", 17 "invite.skip.label" : "Я хочу зробити це пізніше",
18 "invite.submit.label" : "Відправити запрошеннÑ", 18 "invite.submit.label" : "Відправити запрошеннÑ",
19 "invite.successInfo" : "Invitations sent successfully", 19 "invite.successInfo" : "Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ ÑƒÑпішно відоÑлані",
20 "login.email.label" : "Email адреÑа", 20 "login.email.label" : "Email адреÑа",
21 "login.headline" : "Увійти", 21 "login.headline" : "Увійти",
22 "login.invalidCredentials" : "Email або пароль некоректні", 22 "login.invalidCredentials" : "Email або пароль некоректні",
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "Ваша ÑеÑÑ–Ñ Ð·Ð°ÐºÑ–Ð½Ñ‡Ð¸Ð»Ð°ÑÑŒ, будь лаÑка, зайдіть знову.", 26 "login.serverLogout" : "Ваша ÑеÑÑ–Ñ Ð·Ð°ÐºÑ–Ð½Ñ‡Ð¸Ð»Ð°ÑÑŒ, будь лаÑка, зайдіть знову.",
27 "login.submit.label" : "Увійти", 27 "login.submit.label" : "Увійти",
28 "login.tokenExpired" : "Ваша ÑеÑÑ–Ñ Ð·Ð°ÐºÑ–Ð½Ñ‡Ð¸Ð»Ð°ÑÑŒ, будь лаÑка, зайдіть знову.", 28 "login.tokenExpired" : "Ваша ÑеÑÑ–Ñ Ð·Ð°ÐºÑ–Ð½Ñ‡Ð¸Ð»Ð°ÑÑŒ, будь лаÑка, зайдіть знову.",
29 "menu.app.about" : "About Franz",
30 "menu.app.hide" : "Hide",
31 "menu.app.hideOthers" : "Hide Others",
32 "menu.app.quit" : "Quit",
33 "menu.app.settings" : "ÐалаштуваннÑ",
34 "menu.app.unhide" : "Unhide",
35 "menu.edit" : "Редагувати",
36 "menu.edit.copy" : "Copy",
37 "menu.edit.cut" : "Cut",
38 "menu.edit.delete" : "Видалити",
39 "menu.edit.emojiSymbols" : "Emoji & Symbols",
40 "menu.edit.paste" : "Paste",
41 "menu.edit.pasteAndMatchStyle" : "Paste And Match Style",
42 "menu.edit.redo" : "Redo",
43 "menu.edit.selectAll" : "Select All",
44 "menu.edit.speech" : "Speech",
45 "menu.edit.startDictation" : "Start Dictation",
46 "menu.edit.startSpeaking" : "Start Speaking",
47 "menu.edit.stopSpeaking" : "Stop Speaking",
48 "menu.edit.undo" : "Undo",
49 "menu.file" : "File",
50 "menu.help" : "Help",
51 "menu.help.changelog" : "Changelog",
52 "menu.help.learnMore" : "Learn More",
53 "menu.help.privacy" : "ЗаÑва про конфіденційніÑÑ‚ÑŒ",
54 "menu.help.support" : "Support",
55 "menu.help.tos" : "Terms of Service",
56 "menu.services" : "Services",
57 "menu.services.addNewService" : "Add New Service...",
58 "menu.view" : "View",
59 "menu.view.enterFullScreen" : "Enter Full Screen",
60 "menu.view.exitFullScreen" : "Exit Full Screen",
61 "menu.view.reloadFranz" : "Reload Franz",
62 "menu.view.reloadService" : "Reload Service",
63 "menu.view.resetZoom" : "Actual Size",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Toggle Full Screen",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Zoom In",
68 "menu.view.zoomOut" : "Zoom Out",
69 "menu.window" : "Window",
70 "menu.window.close" : "Close",
71 "menu.window.minimize" : "Minimize",
29 "password.email.label" : "Email адреÑа", 72 "password.email.label" : "Email адреÑа",
30 "password.headline" : "Скинути пароль", 73 "password.headline" : "Скинути пароль",
31 "password.link.login" : "Увійти до вашого акаунту", 74 "password.link.login" : "Увійти до вашого акаунту",
@@ -50,7 +93,7 @@
50 "settings.account.accountType.premium" : "Преміум Ðкаунт Прихильника", 93 "settings.account.accountType.premium" : "Преміум Ðкаунт Прихильника",
51 "settings.account.buttonSave" : "Оновити профіль", 94 "settings.account.buttonSave" : "Оновити профіль",
52 "settings.account.deleteAccount" : "Видалити обліковий запиÑ", 95 "settings.account.deleteAccount" : "Видалити обліковий запиÑ",
53 "settings.account.deleteEmailSent" : "You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!", 96 "settings.account.deleteEmailSent" : "Ви отримали електронного лиÑта з лінком, щоб підтвердити Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу. Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ– дані не можна буде відновити!",
54 "settings.account.deleteInfo" : "Якщо Вам більше не потрібний обліковий Ð·Ð°Ð¿Ð¸Ñ Franz, Ви можете його видалити Ñ– вÑÑ– Ñуміжні дані.", 97 "settings.account.deleteInfo" : "Якщо Вам більше не потрібний обліковий Ð·Ð°Ð¿Ð¸Ñ Franz, Ви можете його видалити Ñ– вÑÑ– Ñуміжні дані.",
55 "settings.account.headline" : "Ðкаунт", 98 "settings.account.headline" : "Ðкаунт",
56 "settings.account.headlineAccount" : "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ акаунт", 99 "settings.account.headlineAccount" : "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ акаунт",
@@ -65,22 +108,23 @@
65 "settings.account.successInfo" : "Ваші зміни були збережені", 108 "settings.account.successInfo" : "Ваші зміни були збережені",
66 "settings.account.tryReloadUserInfoRequest" : "Спробуйте ще раз", 109 "settings.account.tryReloadUserInfoRequest" : "Спробуйте ще раз",
67 "settings.account.userInfoRequestFailed" : "Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ інформацію кориÑтувача", 110 "settings.account.userInfoRequestFailed" : "Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ інформацію кориÑтувача",
68 "settings.app.buttonClearAllCache" : "Clear cache", 111 "settings.app.buttonClearAllCache" : "ОчиÑтити кеш",
69 "settings.app.buttonInstallUpdate" : "Перезавантажити Ñ– вÑтановити оновленнÑ", 112 "settings.app.buttonInstallUpdate" : "Перезавантажити Ñ– вÑтановити оновленнÑ",
70 "settings.app.buttonSearchForUpdate" : "Перевірити наÑвніÑÑ‚ÑŒ оновлень", 113 "settings.app.buttonSearchForUpdate" : "Перевірити наÑвніÑÑ‚ÑŒ оновлень",
71 "settings.app.cacheInfo" : "Franz cache is currently using {size} of disk space.", 114 "settings.app.cacheInfo" : "Кеш, Ñкий викориÑтовує Franz, займає {розмір} диÑку.",
72 "settings.app.currentVersion" : "Поточна верÑÑ–Ñ:", 115 "settings.app.currentVersion" : "Поточна верÑÑ–Ñ:",
73 "settings.app.form.autoLaunchInBackground" : "Відкрити у фоновому режимі", 116 "settings.app.form.autoLaunchInBackground" : "Відкрити у фоновому режимі",
74 "settings.app.form.autoLaunchOnStart" : "ЗапуÑкати Franz на початку", 117 "settings.app.form.autoLaunchOnStart" : "ЗапуÑкати Franz на початку",
75 "settings.app.form.beta" : "Включити бета-верÑÑ–Ñ—", 118 "settings.app.form.beta" : "Включити бета-верÑÑ–Ñ—",
76 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar", 119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
120 "settings.app.form.enableMenuBar" : "Відображати Franz в панелі головного меню",
77 "settings.app.form.enableSpellchecking" : "Увімкнути перевірку орфографії", 121 "settings.app.form.enableSpellchecking" : "Увімкнути перевірку орфографії",
78 "settings.app.form.enableSystemTray" : "Показувати Franz у ÑиÑтемному лотку", 122 "settings.app.form.enableSystemTray" : "Показувати Franz у ÑиÑтемному лотку",
79 "settings.app.form.hideDockIcon" : "Hide Franz icon in Dock", 123 "settings.app.form.hideDockIcon" : "Hide Franz icon in Dock",
80 "settings.app.form.language" : "Мова", 124 "settings.app.form.language" : "Мова",
81 "settings.app.form.minimizeToSystemTray" : "Мінімізувати Franz до ÑиÑтемного лотка", 125 "settings.app.form.minimizeToSystemTray" : "Мінімізувати Franz до ÑиÑтемного лотка",
82 "settings.app.form.runInBackground" : "Тримати Franz в фоні при закритті вікна", 126 "settings.app.form.runInBackground" : "Тримати Franz в фоні при закритті вікна",
83 "settings.app.form.showDisabledServices" : "Display disabled services tabs", 127 "settings.app.form.showDisabledServices" : "Показати вкладку вимкнених ÑервіÑів",
84 "settings.app.form.showMessagesBadgesWhenMuted" : "Показувати значок непрочитаних повідомлень коли ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ñ–", 128 "settings.app.form.showMessagesBadgesWhenMuted" : "Показувати значок непрочитаних повідомлень коли ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ñ–",
85 "settings.app.headline" : "ÐалаштуваннÑ", 129 "settings.app.headline" : "ÐалаштуваннÑ",
86 "settings.app.headlineAdvanced" : "Advanced", 130 "settings.app.headlineAdvanced" : "Advanced",
@@ -89,15 +133,15 @@
89 "settings.app.headlineLanguage" : "Мова", 133 "settings.app.headlineLanguage" : "Мова",
90 "settings.app.headlineUpdates" : "ОновленнÑ", 134 "settings.app.headlineUpdates" : "ОновленнÑ",
91 "settings.app.restartRequired" : "Зміни потребують перезапуÑку", 135 "settings.app.restartRequired" : "Зміни потребують перезапуÑку",
92 "settings.app.subheadlineCache" : "Cache", 136 "settings.app.subheadlineCache" : "Кеш",
93 "settings.app.translationHelp" : "Допоможіть переклаÑти Franz на Вашу мову.", 137 "settings.app.translationHelp" : "Допоможіть переклаÑти Franz на Вашу мову.",
94 "settings.app.updateStatusAvailable" : "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ñтупне, завантаженнÑ...", 138 "settings.app.updateStatusAvailable" : "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ñтупне, завантаженнÑ...",
95 "settings.app.updateStatusSearching" : "Шукає оновленнÑ", 139 "settings.app.updateStatusSearching" : "Шукає оновленнÑ",
96 "settings.app.updateStatusUpToDate" : "Ви викориÑтовуєте оÑтанню верÑÑ–ÑŽ Franz", 140 "settings.app.updateStatusUpToDate" : "Ви викориÑтовуєте оÑтанню верÑÑ–ÑŽ Franz",
97 "settings.invite.headline" : "Invite Friends", 141 "settings.invite.headline" : "ЗапроÑити друзів",
98 "settings.navigation.account" : "Ðкаунт", 142 "settings.navigation.account" : "Ðкаунт",
99 "settings.navigation.availableServices" : "ДоÑтупні ÑервіÑи", 143 "settings.navigation.availableServices" : "ДоÑтупні ÑервіÑи",
100 "settings.navigation.inviteFriends" : "Invite Friends", 144 "settings.navigation.inviteFriends" : "ЗапроÑити друзів",
101 "settings.navigation.logout" : "Вийти", 145 "settings.navigation.logout" : "Вийти",
102 "settings.navigation.settings" : "ÐалаштуваннÑ", 146 "settings.navigation.settings" : "ÐалаштуваннÑ",
103 "settings.navigation.yourServices" : "Ваші ÑервіÑи", 147 "settings.navigation.yourServices" : "Ваші ÑервіÑи",
@@ -126,10 +170,10 @@
126 "settings.service.form.enableService" : "Увімкнути ÑервіÑ", 170 "settings.service.form.enableService" : "Увімкнути ÑервіÑ",
127 "settings.service.form.headlineBadges" : "Unread message badges", 171 "settings.service.form.headlineBadges" : "Unread message badges",
128 "settings.service.form.headlineGeneral" : "Загальні", 172 "settings.service.form.headlineGeneral" : "Загальні",
129 "settings.service.form.headlineNotifications" : "Notifications", 173 "settings.service.form.headlineNotifications" : "СповіщеннÑ",
130 "settings.service.form.icon" : "Custom icon", 174 "settings.service.form.icon" : "Ð¡Ð²Ð¾Ñ Ñ–ÐºÐ¾Ð½ÐºÐ°",
131 "settings.service.form.iconDelete" : "Delete", 175 "settings.service.form.iconDelete" : "Видалити",
132 "settings.service.form.iconUpload" : "Drop your image, or click here", 176 "settings.service.form.iconUpload" : "Скиньте Ñвоє Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ натиÑніть тут",
133 "settings.service.form.indirectMessageInfo" : "Ви отримаєте ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ вÑÑ– нові Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð² каналі, а не лише про @username, @channel, @here, ...", 177 "settings.service.form.indirectMessageInfo" : "Ви отримаєте ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ вÑÑ– нові Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð² каналі, а не лише про @username, @channel, @here, ...",
134 "settings.service.form.indirectMessages" : "Показувати значок Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð²ÑÑ–Ñ… нових повідомлень", 178 "settings.service.form.indirectMessages" : "Показувати значок Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð²ÑÑ–Ñ… нових повідомлень",
135 "settings.service.form.isMutedInfo" : "When disabled, all notification sounds and audio playback are muted", 179 "settings.service.form.isMutedInfo" : "When disabled, all notification sounds and audio playback are muted",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "Увімкнути ÑповіщеннÑ", 241 "tabs.item.enableNotification" : "Увімкнути ÑповіщеннÑ",
198 "tabs.item.enableService" : "Увімкнути Ñлужбу", 242 "tabs.item.enableService" : "Увімкнути Ñлужбу",
199 "tabs.item.reload" : "Перезавантажити", 243 "tabs.item.reload" : "Перезавантажити",
244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
200 "welcome.loginButton" : "Увійдіть до Ñвого акаунту", 248 "welcome.loginButton" : "Увійдіть до Ñвого акаунту",
201 "welcome.signupButton" : "Створити безплатний акаунт", 249 "welcome.signupButton" : "Створити безплатний акаунт",
202 "welcome.slogan" : "Обмін повідомленнÑми, Ñкий працює Ð´Ð»Ñ Ð²Ð°Ñ" 250 "welcome.slogan" : "Обмін повідомленнÑми, Ñкий працює Ð´Ð»Ñ Ð²Ð°Ñ"
diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json
index 5feef50f2..3bbcc8288 100644
--- a/src/i18n/locales/zh-TW.json
+++ b/src/i18n/locales/zh-TW.json
@@ -16,7 +16,7 @@
16 "invite.name.label" : "å稱", 16 "invite.name.label" : "å稱",
17 "invite.skip.label" : "我想ç¨å¾Œå†åš", 17 "invite.skip.label" : "我想ç¨å¾Œå†åš",
18 "invite.submit.label" : "é€å‡ºé‚€è«‹", 18 "invite.submit.label" : "é€å‡ºé‚€è«‹",
19 "invite.successInfo" : "Invitations sent successfully", 19 "invite.successInfo" : "註冊請求已é€å‡º",
20 "login.email.label" : "é›»å­éƒµä»¶åœ°å€", 20 "login.email.label" : "é›»å­éƒµä»¶åœ°å€",
21 "login.headline" : "登入", 21 "login.headline" : "登入",
22 "login.invalidCredentials" : "é›»å­éƒµä»¶æˆ–密碼有誤", 22 "login.invalidCredentials" : "é›»å­éƒµä»¶æˆ–密碼有誤",
@@ -26,6 +26,49 @@
26 "login.serverLogout" : "您的登入期間已éŽæœŸï¼Œè«‹é‡æ–°ç™»éŒ„", 26 "login.serverLogout" : "您的登入期間已éŽæœŸï¼Œè«‹é‡æ–°ç™»éŒ„",
27 "login.submit.label" : "登入", 27 "login.submit.label" : "登入",
28 "login.tokenExpired" : "您的登入已éŽæœŸï¼Œè«‹é‡æ–°ç™»å…¥ã€‚", 28 "login.tokenExpired" : "您的登入已éŽæœŸï¼Œè«‹é‡æ–°ç™»å…¥ã€‚",
29 "menu.app.about" : "關於Franz",
30 "menu.app.hide" : "éš±è—",
31 "menu.app.hideOthers" : "éš±è—其他",
32 "menu.app.quit" : "çµæŸ",
33 "menu.app.settings" : "設定",
34 "menu.app.unhide" : "顯示",
35 "menu.edit" : "編輯",
36 "menu.edit.copy" : "æ‹·è²",
37 "menu.edit.cut" : "剪下",
38 "menu.edit.delete" : "刪除",
39 "menu.edit.emojiSymbols" : "貼紙和表情包",
40 "menu.edit.paste" : "貼上",
41 "menu.edit.pasteAndMatchStyle" : "貼上并匹é…樣å¼",
42 "menu.edit.redo" : "é‡ä¾†",
43 "menu.edit.selectAll" : "é¸æ“‡æ‰€æœ‰",
44 "menu.edit.speech" : "朗讀",
45 "menu.edit.startDictation" : "開始è½å¯«",
46 "menu.edit.startSpeaking" : "Start Speaking",
47 "menu.edit.stopSpeaking" : "Stop Speaking",
48 "menu.edit.undo" : "Undo",
49 "menu.file" : "File",
50 "menu.help" : "Help",
51 "menu.help.changelog" : "Changelog",
52 "menu.help.learnMore" : "Learn More",
53 "menu.help.privacy" : "éš±ç§è²æ˜Ž",
54 "menu.help.support" : "Support",
55 "menu.help.tos" : "Terms of Service",
56 "menu.services" : "Services",
57 "menu.services.addNewService" : "Add New Service...",
58 "menu.view" : "View",
59 "menu.view.enterFullScreen" : "Enter Full Screen",
60 "menu.view.exitFullScreen" : "Exit Full Screen",
61 "menu.view.reloadFranz" : "Reload Franz",
62 "menu.view.reloadService" : "Reload Service",
63 "menu.view.resetZoom" : "Actual Size",
64 "menu.view.toggleDevTools" : "Toggle Developer Tools",
65 "menu.view.toggleFullScreen" : "Toggle Full Screen",
66 "menu.view.toggleServiceDevTools" : "Toggle Service Developer Tools",
67 "menu.view.zoomIn" : "Zoom In",
68 "menu.view.zoomOut" : "Zoom Out",
69 "menu.window" : "Window",
70 "menu.window.close" : "Close",
71 "menu.window.minimize" : "Minimize",
29 "password.email.label" : "é›»å­éƒµä»¶åœ°å€", 72 "password.email.label" : "é›»å­éƒµä»¶åœ°å€",
30 "password.headline" : "é‡è¨­å¯†ç¢¼", 73 "password.headline" : "é‡è¨­å¯†ç¢¼",
31 "password.link.login" : "登入您的帳號", 74 "password.link.login" : "登入您的帳號",
@@ -73,6 +116,7 @@
73 "settings.app.form.autoLaunchInBackground" : "在背景開啟", 116 "settings.app.form.autoLaunchInBackground" : "在背景開啟",
74 "settings.app.form.autoLaunchOnStart" : "開機時啟動Franz", 117 "settings.app.form.autoLaunchOnStart" : "開機時啟動Franz",
75 "settings.app.form.beta" : "包å«æ¸¬è©¦ç‰ˆ", 118 "settings.app.form.beta" : "包å«æ¸¬è©¦ç‰ˆ",
119 "settings.app.form.enableGPUAcceleration" : "Enable GPU Acceleration",
76 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar", 120 "settings.app.form.enableMenuBar" : "Show Franz in Menu Bar",
77 "settings.app.form.enableSpellchecking" : "啟用拼字檢查", 121 "settings.app.form.enableSpellchecking" : "啟用拼字檢查",
78 "settings.app.form.enableSystemTray" : "顯示Franz在系統列", 122 "settings.app.form.enableSystemTray" : "顯示Franz在系統列",
@@ -128,7 +172,7 @@
128 "settings.service.form.headlineGeneral" : "一般", 172 "settings.service.form.headlineGeneral" : "一般",
129 "settings.service.form.headlineNotifications" : "Notifications", 173 "settings.service.form.headlineNotifications" : "Notifications",
130 "settings.service.form.icon" : "Custom icon", 174 "settings.service.form.icon" : "Custom icon",
131 "settings.service.form.iconDelete" : "Delete", 175 "settings.service.form.iconDelete" : "刪除",
132 "settings.service.form.iconUpload" : "Drop your image, or click here", 176 "settings.service.form.iconUpload" : "Drop your image, or click here",
133 "settings.service.form.indirectMessageInfo" : "您將收到有關頻é“中所有新消æ¯çš„通知,而ä¸åƒ…僅是@username,@channel,@here,...", 177 "settings.service.form.indirectMessageInfo" : "您將收到有關頻é“中所有新消æ¯çš„通知,而ä¸åƒ…僅是@username,@channel,@here,...",
134 "settings.service.form.indirectMessages" : "顯示所有新消æ¯çš„消æ¯æ¨™èªŒ", 178 "settings.service.form.indirectMessages" : "顯示所有新消æ¯çš„消æ¯æ¨™èªŒ",
@@ -197,6 +241,10 @@
197 "tabs.item.enableNotification" : "啟用通知", 241 "tabs.item.enableNotification" : "啟用通知",
198 "tabs.item.enableService" : "啟用æœå‹™", 242 "tabs.item.enableService" : "啟用æœå‹™",
199 "tabs.item.reload" : "é‡æ–°è¼‰å…¥", 243 "tabs.item.reload" : "é‡æ–°è¼‰å…¥",
244 "validation.email" : "{field} is not valid",
245 "validation.minLength" : "{field} should be at least {length} characters long",
246 "validation.required" : "{field} is required",
247 "validation.url" : "{field} is not a valid URL",
200 "welcome.loginButton" : "登入您的帳戶", 248 "welcome.loginButton" : "登入您的帳戶",
201 "welcome.signupButton" : "建立å…費帳號", 249 "welcome.signupButton" : "建立å…費帳號",
202 "welcome.slogan" : "為你通信" 250 "welcome.slogan" : "為你通信"
diff --git a/src/index.html b/src/index.html
index 9e5acd705..5ce7f6a96 100644
--- a/src/index.html
+++ b/src/index.html
@@ -11,10 +11,15 @@
11 <div class="dev-warning">DEV MODE</div> 11 <div class="dev-warning">DEV MODE</div>
12 <div id="root"></div> 12 <div id="root"></div>
13 <script> 13 <script>
14 document.querySelector('body').classList.add(process.platform); 14 document.querySelector('body').classList.add(process.env.OS_PLATFORM ? process.env.OS_PLATFORM : process.platform);
15 15
16 const { isDevMode } = require('./environment'); 16 const { isDevMode } = require('./environment');
17 if (isDevMode) { 17 if (isDevMode) {
18 const debugging = require('debug');
19 debugging.enable(process.env.DEBUG);
20
21 const debug = debugging('Index');
22
18 document.querySelector('body').classList.add('isDevMode'); 23 document.querySelector('body').classList.add('isDevMode');
19 24
20 (function() { 25 (function() {
@@ -25,7 +30,7 @@
25 document.body.appendChild(s); 30 document.body.appendChild(s);
26 31
27 s.onload = () => { 32 s.onload = () => {
28 console.log('livereload loaded'); 33 debug('livereload loaded');
29 const originalReloadBehaviour = window._onLiveReloadFileChanged; 34 const originalReloadBehaviour = window._onLiveReloadFileChanged;
30 35
31 window._onLiveReloadFileChanged = (file) => { 36 window._onLiveReloadFileChanged = (file) => {
@@ -33,10 +38,10 @@
33 originalReloadBehaviour(file); 38 originalReloadBehaviour(file);
34 } else { 39 } else {
35 if (file.path.includes('/build/webview/')) { 40 if (file.path.includes('/build/webview/')) {
36 console.log('Livereload: Reloading all webvies'); 41 debug('Livereload: Reloading all webvies');
37 const webviews = document.querySelectorAll('webview').forEach(webview => webview.reload()); 42 const webviews = document.querySelectorAll('webview').forEach(webview => webview.reload());
38 } else { 43 } else {
39 console.log('Livereload: skip reload as only main process files have changed'); 44 debug('Livereload: skip reload as only main process files have changed');
40 } 45 }
41 } 46 }
42 } 47 }
diff --git a/src/index.js b/src/index.js
index f82bb3590..7d906ad71 100644
--- a/src/index.js
+++ b/src/index.js
@@ -4,7 +4,7 @@ import path from 'path';
4 4
5import windowStateKeeper from 'electron-window-state'; 5import windowStateKeeper from 'electron-window-state';
6 6
7import { isDevMode, isWindows } from './environment'; 7import { isDevMode, isMac, isWindows, isLinux } from './environment';
8import ipcApi from './electron/ipc-api'; 8import ipcApi from './electron/ipc-api';
9import Tray from './lib/Tray'; 9import Tray from './lib/Tray';
10import Settings from './electron/Settings'; 10import Settings from './electron/Settings';
@@ -12,14 +12,21 @@ import handleDeepLink from './electron/deepLinking';
12import { appId } from './package.json'; // eslint-disable-line import/no-unresolved 12import { appId } from './package.json'; // eslint-disable-line import/no-unresolved
13import './electron/exception'; 13import './electron/exception';
14 14
15const debug = require('debug')('Franz:App');
16
15// Keep a global reference of the window object, if you don't, the window will 17// Keep a global reference of the window object, if you don't, the window will
16// be closed automatically when the JavaScript object is garbage collected. 18// be closed automatically when the JavaScript object is garbage collected.
17let mainWindow; 19let mainWindow;
18let willQuitApp = false; 20let willQuitApp = false;
19 21
22// DEV MODE: Save user data into FranzDev
23if (isDevMode) {
24 app.setPath('userData', path.join(app.getPath('appData'), 'FranzDev'));
25}
26
20// Ensure that the recipe directory exists 27// Ensure that the recipe directory exists
21fs.ensureDir(path.join(app.getPath('userData'), 'recipes'));
22fs.emptyDirSync(path.join(app.getPath('userData'), 'recipes', 'temp')); 28fs.emptyDirSync(path.join(app.getPath('userData'), 'recipes', 'temp'));
29fs.ensureFileSync(path.join(app.getPath('userData'), 'window-state.json'));
23 30
24// Set App ID for Windows 31// Set App ID for Windows
25if (isWindows) { 32if (isWindows) {
@@ -48,15 +55,21 @@ if (isSecondInstance) {
48 app.exit(); 55 app.exit();
49} 56}
50 57
51// Lets disable Hardware Acceleration until we have a better solution 58// Fix Unity indicator issue
52// to deal with the high-perf-gpu requirement of some services 59// https://github.com/electron/electron/issues/9046
53 60if (isLinux && ['Pantheon', 'Unity:Unity7'].indexOf(process.env.XDG_CURRENT_DESKTOP) !== -1) {
54// Disabled to test tweetdeck glitches 61 process.env.XDG_CURRENT_DESKTOP = 'Unity';
55// app.disableHardwareAcceleration(); 62}
56 63
57// Initialize Settings 64// Initialize Settings
58const settings = new Settings(); 65const settings = new Settings();
59 66
67// Disable GPU acceleration
68if (!settings.get('enableGPUAcceleration')) {
69 debug('Disable GPU Acceleration');
70 app.disableHardwareAcceleration();
71}
72
60const createWindow = () => { 73const createWindow = () => {
61 // Remember window size 74 // Remember window size
62 const mainWindowState = windowStateKeeper({ 75 const mainWindowState = windowStateKeeper({
@@ -72,9 +85,9 @@ const createWindow = () => {
72 height: mainWindowState.height, 85 height: mainWindowState.height,
73 minWidth: 600, 86 minWidth: 600,
74 minHeight: 500, 87 minHeight: 500,
75 titleBarStyle: 'hidden', 88 titleBarStyle: isMac ? 'hidden' : '',
76 backgroundColor: '#3498db', 89 frame: isLinux,
77 autoHideMenuBar: true, 90 backgroundColor: !settings.get('darkMode') ? '#3498db' : '#1E1E1E',
78 }); 91 });
79 92
80 // Initialize System Tray 93 // Initialize System Tray
@@ -107,7 +120,7 @@ const createWindow = () => {
107 mainWindow.hide(); 120 mainWindow.hide();
108 } 121 }
109 122
110 if (isWindows && settings.get('minimizeToSystemTray')) { 123 if (isWindows) {
111 mainWindow.setSkipTaskbar(true); 124 mainWindow.setSkipTaskbar(true);
112 } 125 }
113 } else { 126 } else {
diff --git a/src/lib/Menu.js b/src/lib/Menu.js
index 703060dc1..76b0f28d5 100644
--- a/src/lib/Menu.js
+++ b/src/lib/Menu.js
@@ -1,113 +1,477 @@
1import { remote, shell } from 'electron'; 1import { remote, shell } from 'electron';
2import { autorun, computed, observable, toJS } from 'mobx'; 2import { observable, autorun, computed } from 'mobx';
3import { defineMessages } from 'react-intl';
3 4
4import { isMac } from '../environment'; 5import { isMac, ctrlKey, cmdKey } from '../environment';
5 6
6const { app, Menu, dialog } = remote; 7const { app, Menu, dialog } = remote;
7 8
8const template = [ 9const menuItems = defineMessages({
10 edit: {
11 id: 'menu.edit',
12 defaultMessage: '!!!Edit',
13 },
14 undo: {
15 id: 'menu.edit.undo',
16 defaultMessage: '!!!Undo',
17 },
18 redo: {
19 id: 'menu.edit.redo',
20 defaultMessage: '!!!Redo',
21 },
22 cut: {
23 id: 'menu.edit.cut',
24 defaultMessage: '!!!Cut',
25 },
26 copy: {
27 id: 'menu.edit.copy',
28 defaultMessage: '!!!Copy',
29 },
30 paste: {
31 id: 'menu.edit.paste',
32 defaultMessage: '!!!Paste',
33 },
34 pasteAndMatchStyle: {
35 id: 'menu.edit.pasteAndMatchStyle',
36 defaultMessage: '!!!Paste And Match Style',
37 },
38 delete: {
39 id: 'menu.edit.delete',
40 defaultMessage: '!!!Delete',
41 },
42 selectAll: {
43 id: 'menu.edit.selectAll',
44 defaultMessage: '!!!Select All',
45 },
46 speech: {
47 id: 'menu.edit.speech',
48 defaultMessage: '!!!Speech',
49 },
50 startSpeaking: {
51 id: 'menu.edit.startSpeaking',
52 defaultMessage: '!!!Start Speaking',
53 },
54 stopSpeaking: {
55 id: 'menu.edit.stopSpeaking',
56 defaultMessage: '!!!Stop Speaking',
57 },
58 startDictation: {
59 id: 'menu.edit.startDictation',
60 defaultMessage: '!!!Start Dictation',
61 },
62 emojiSymbols: {
63 id: 'menu.edit.emojiSymbols',
64 defaultMessage: '!!!Emoji & Symbols',
65 },
66 resetZoom: {
67 id: 'menu.view.resetZoom',
68 defaultMessage: '!!!Actual Size',
69 },
70 zoomIn: {
71 id: 'menu.view.zoomIn',
72 defaultMessage: '!!!Zoom In',
73 },
74 zoomOut: {
75 id: 'menu.view.zoomOut',
76 defaultMessage: '!!!Zoom Out',
77 },
78 enterFullScreen: {
79 id: 'menu.view.enterFullScreen',
80 defaultMessage: '!!!Enter Full Screen',
81 },
82 exitFullScreen: {
83 id: 'menu.view.exitFullScreen',
84 defaultMessage: '!!!Exit Full Screen',
85 },
86 toggleFullScreen: {
87 id: 'menu.view.toggleFullScreen',
88 defaultMessage: '!!!Toggle Full Screen',
89 },
90 toggleDevTools: {
91 id: 'menu.view.toggleDevTools',
92 defaultMessage: '!!!Toggle Developer Tools',
93 },
94 toggleServiceDevTools: {
95 id: 'menu.view.toggleServiceDevTools',
96 defaultMessage: '!!!Toggle Service Developer Tools',
97 },
98 reloadService: {
99 id: 'menu.view.reloadService',
100 defaultMessage: '!!!Reload Service',
101 },
102 reloadFranz: {
103 id: 'menu.view.reloadFranz',
104 defaultMessage: '!!!Reload Franz',
105 },
106 minimize: {
107 id: 'menu.window.minimize',
108 defaultMessage: '!!!Minimize',
109 },
110 close: {
111 id: 'menu.window.close',
112 defaultMessage: '!!!Close',
113 },
114 learnMore: {
115 id: 'menu.help.learnMore',
116 defaultMessage: '!!!Learn More',
117 },
118 changelog: {
119 id: 'menu.help.changelog',
120 defaultMessage: '!!!Changelog',
121 },
122 support: {
123 id: 'menu.help.support',
124 defaultMessage: '!!!Support',
125 },
126 tos: {
127 id: 'menu.help.tos',
128 defaultMessage: '!!!Terms of Service',
129 },
130 privacy: {
131 id: 'menu.help.privacy',
132 defaultMessage: '!!!Privacy Statement',
133 },
134 file: {
135 id: 'menu.file',
136 defaultMessage: '!!!File',
137 },
138 view: {
139 id: 'menu.view',
140 defaultMessage: '!!!View',
141 },
142 services: {
143 id: 'menu.services',
144 defaultMessage: '!!!Services',
145 },
146 window: {
147 id: 'menu.window',
148 defaultMessage: '!!!Window',
149 },
150 help: {
151 id: 'menu.help',
152 defaultMessage: '!!!Help',
153 },
154 about: {
155 id: 'menu.app.about',
156 defaultMessage: '!!!About Franz',
157 },
158 settings: {
159 id: 'menu.app.settings',
160 defaultMessage: '!!!Settings',
161 },
162 hide: {
163 id: 'menu.app.hide',
164 defaultMessage: '!!!Hide',
165 },
166 hideOthers: {
167 id: 'menu.app.hideOthers',
168 defaultMessage: '!!!Hide Others',
169 },
170 unhide: {
171 id: 'menu.app.unhide',
172 defaultMessage: '!!!Unhide',
173 },
174 quit: {
175 id: 'menu.app.quit',
176 defaultMessage: '!!!Quit',
177 },
178 addNewService: {
179 id: 'menu.services.addNewService',
180 defaultMessage: '!!!Add New Service...',
181 },
182});
183
184function getActiveWebview() {
185 return window.franz.stores.services.active.webview;
186}
187
188const _templateFactory = intl => [
9 { 189 {
10 label: 'Edit', 190 label: intl.formatMessage(menuItems.edit),
11 submenu: [ 191 submenu: [
12 { 192 {
193 label: intl.formatMessage(menuItems.undo),
13 role: 'undo', 194 role: 'undo',
14 }, 195 },
15 { 196 {
197 label: intl.formatMessage(menuItems.redo),
16 role: 'redo', 198 role: 'redo',
17 }, 199 },
18 { 200 {
19 type: 'separator', 201 type: 'separator',
20 }, 202 },
21 { 203 {
22 role: 'cut', 204 label: intl.formatMessage(menuItems.cut),
205 accelerator: 'Cmd+X',
206 selector: 'cut:',
23 }, 207 },
24 { 208 {
25 label: 'Copy', 209 label: intl.formatMessage(menuItems.copy),
26 accelerator: 'Cmd+C', 210 accelerator: 'Cmd+C',
27 selector: 'copy:', 211 selector: 'copy:',
28 }, 212 },
29 { 213 {
30 label: 'Paste', 214 label: intl.formatMessage(menuItems.paste),
31 accelerator: 'Cmd+V', 215 accelerator: 'Cmd+V',
32 selector: 'paste:', 216 selector: 'paste:',
33 }, 217 },
34 { 218 {
35 role: 'pasteandmatchstyle', 219 label: intl.formatMessage(menuItems.pasteAndMatchStyle),
220 accelerator: 'Cmd+Shift+V',
221 selector: 'pasteAndMatchStyle:',
36 }, 222 },
37 { 223 {
224 label: intl.formatMessage(menuItems.delete),
38 role: 'delete', 225 role: 'delete',
39 }, 226 },
40 { 227 {
41 role: 'selectall', 228 label: intl.formatMessage(menuItems.selectAll),
229 accelerator: 'Cmd+A',
230 selector: 'selectAll:',
42 }, 231 },
43 ], 232 ],
44 }, 233 },
45 { 234 {
46 label: 'View', 235 label: intl.formatMessage(menuItems.view),
47 submenu: [ 236 submenu: [
48 { 237 {
49 type: 'separator', 238 type: 'separator',
50 }, 239 },
51 { 240 {
241 label: intl.formatMessage(menuItems.resetZoom),
52 role: 'resetzoom', 242 role: 'resetzoom',
53 }, 243 },
54 { 244 {
245 label: intl.formatMessage(menuItems.zoomIn),
246 // accelerator: 'Cmd+=',
55 role: 'zoomin', 247 role: 'zoomin',
56 accelerator: 'CommandOrControl+=',
57 }, 248 },
58 { 249 {
250 label: intl.formatMessage(menuItems.zoomOut),
59 role: 'zoomout', 251 role: 'zoomout',
60 }, 252 },
61 { 253 {
62 type: 'separator', 254 type: 'separator',
63 }, 255 },
64 { 256 {
257 label: app.mainWindow.isFullScreen() // label doesn't work, gets overridden by Electron
258 ? intl.formatMessage(menuItems.exitFullScreen)
259 : intl.formatMessage(menuItems.enterFullScreen),
65 role: 'togglefullscreen', 260 role: 'togglefullscreen',
66 }, 261 },
67 ], 262 ],
68 }, 263 },
69 { 264 {
70 label: 'Services', 265 label: intl.formatMessage(menuItems.services),
71 submenu: [], 266 submenu: [],
72 }, 267 },
73 { 268 {
269 label: intl.formatMessage(menuItems.window),
74 role: 'window', 270 role: 'window',
75 submenu: [ 271 submenu: [
76 { 272 {
273 label: intl.formatMessage(menuItems.minimize),
77 role: 'minimize', 274 role: 'minimize',
78 }, 275 },
79 { 276 {
277 label: intl.formatMessage(menuItems.close),
80 role: 'close', 278 role: 'close',
81 }, 279 },
82 ], 280 ],
83 }, 281 },
84 { 282 {
283 label: intl.formatMessage(menuItems.help),
85 role: 'help', 284 role: 'help',
86 submenu: [ 285 submenu: [
87 { 286 {
88 label: 'Learn More', 287 label: intl.formatMessage(menuItems.learnMore),
89 click() { shell.openExternal('http://meetfranz.com'); }, 288 click() { shell.openExternal('https://meetfranz.com'); },
90 }, 289 },
91 { 290 {
92 label: 'Changelog', 291 label: intl.formatMessage(menuItems.changelog),
93 click() { shell.openExternal('https://github.com/meetfranz/franz/blob/master/CHANGELOG.md'); }, 292 click() { shell.openExternal('https://github.com/meetfranz/franz/blob/master/CHANGELOG.md'); },
94 }, 293 },
95 { 294 {
96 type: 'separator', 295 type: 'separator',
97 }, 296 },
98 { 297 {
99 label: 'Support', 298 label: intl.formatMessage(menuItems.support),
100 click() { shell.openExternal('http://meetfranz.com/support'); }, 299 click() { shell.openExternal('https://meetfranz.com/support'); },
101 }, 300 },
102 { 301 {
103 type: 'separator', 302 type: 'separator',
104 }, 303 },
105 { 304 {
106 label: 'Terms of Service', 305 label: intl.formatMessage(menuItems.tos),
107 click() { shell.openExternal('https://meetfranz.com/terms'); }, 306 click() { shell.openExternal('https://meetfranz.com/terms'); },
108 }, 307 },
109 { 308 {
110 label: 'Privacy Statement', 309 label: intl.formatMessage(menuItems.privacy),
310 click() { shell.openExternal('https://meetfranz.com/privacy'); },
311 },
312 ],
313 },
314];
315
316const _titleBarTemplateFactory = intl => [
317 {
318 label: intl.formatMessage(menuItems.edit),
319 submenu: [
320 {
321 label: intl.formatMessage(menuItems.undo),
322 accelerator: `${ctrlKey}+Z`,
323 click() {
324 getActiveWebview().undo();
325 },
326 },
327 {
328 label: intl.formatMessage(menuItems.redo),
329 accelerator: `${ctrlKey}+Y`,
330 click() {
331 getActiveWebview().redo();
332 },
333 },
334 {
335 type: 'separator',
336 },
337 {
338 label: intl.formatMessage(menuItems.cut),
339 accelerator: `${ctrlKey}+X`,
340 click() {
341 getActiveWebview().cut();
342 },
343 },
344 {
345 label: intl.formatMessage(menuItems.copy),
346 accelerator: `${ctrlKey}+C`,
347 click() {
348 getActiveWebview().copy();
349 },
350 },
351 {
352 label: intl.formatMessage(menuItems.paste),
353 accelerator: `${ctrlKey}+V`,
354 click() {
355 getActiveWebview().paste();
356 },
357 },
358 {
359 label: intl.formatMessage(menuItems.pasteAndMatchStyle),
360 accelerator: `${ctrlKey}+Shift+V`,
361 click() {
362 getActiveWebview().pasteAndMatchStyle();
363 },
364 },
365 {
366 label: intl.formatMessage(menuItems.delete),
367 click() {
368 getActiveWebview().delete();
369 },
370 },
371 {
372 label: intl.formatMessage(menuItems.selectAll),
373 accelerator: `${ctrlKey}+A`,
374 click() {
375 getActiveWebview().selectAll();
376 },
377 },
378 ],
379 },
380 {
381 label: intl.formatMessage(menuItems.view),
382 submenu: [
383 {
384 type: 'separator',
385 },
386 {
387 label: intl.formatMessage(menuItems.resetZoom),
388 accelerator: `${ctrlKey}+0`,
389 click() {
390 getActiveWebview().setZoomLevel(0);
391 },
392 },
393 {
394 label: intl.formatMessage(menuItems.zoomIn),
395 accelerator: `${ctrlKey}+Plus`,
396 click() {
397 getActiveWebview().getZoomLevel((zoomLevel) => {
398 getActiveWebview().setZoomLevel(zoomLevel === 5 ? zoomLevel : zoomLevel + 1);
399 });
400 },
401 },
402 {
403 label: intl.formatMessage(menuItems.zoomOut),
404 accelerator: `${ctrlKey}+-`,
405 click() {
406 getActiveWebview().getZoomLevel((zoomLevel) => {
407 getActiveWebview().setZoomLevel(zoomLevel === -5 ? zoomLevel : zoomLevel - 1);
408 });
409 },
410 },
411 {
412 type: 'separator',
413 },
414 {
415 label: app.mainWindow.isFullScreen() // label doesn't work, gets overridden by Electron
416 ? intl.formatMessage(menuItems.exitFullScreen)
417 : intl.formatMessage(menuItems.enterFullScreen),
418 accelerator: 'F11',
419 click(menuItem, browserWindow) {
420 browserWindow.setFullScreen(!browserWindow.isFullScreen());
421 },
422 },
423 ],
424 },
425 {
426 label: intl.formatMessage(menuItems.services),
427 submenu: [],
428 },
429 {
430 label: intl.formatMessage(menuItems.window),
431 submenu: [
432 {
433 label: intl.formatMessage(menuItems.minimize),
434 accelerator: 'Ctrl+M',
435 click(menuItem, browserWindow) {
436 browserWindow.minimize();
437 },
438 },
439 {
440 label: intl.formatMessage(menuItems.close),
441 accelerator: 'Ctrl+W',
442 click(menuItem, browserWindow) {
443 browserWindow.close();
444 },
445 },
446 ],
447 },
448 {
449 label: '?',
450 submenu: [
451 {
452 label: intl.formatMessage(menuItems.learnMore),
453 click() { shell.openExternal('https://meetfranz.com'); },
454 },
455 {
456 label: intl.formatMessage(menuItems.changelog),
457 click() { shell.openExternal('https://github.com/meetfranz/franz/blob/master/CHANGELOG.md'); },
458 },
459 {
460 type: 'separator',
461 },
462 {
463 label: intl.formatMessage(menuItems.support),
464 click() { shell.openExternal('https://meetfranz.com/support'); },
465 },
466 {
467 type: 'separator',
468 },
469 {
470 label: intl.formatMessage(menuItems.tos),
471 click() { shell.openExternal('https://meetfranz.com/terms'); },
472 },
473 {
474 label: intl.formatMessage(menuItems.privacy),
111 click() { shell.openExternal('https://meetfranz.com/privacy'); }, 475 click() { shell.openExternal('https://meetfranz.com/privacy'); },
112 }, 476 },
113 ], 477 ],
@@ -115,7 +479,7 @@ const template = [
115]; 479];
116 480
117export default class FranzMenu { 481export default class FranzMenu {
118 @observable tpl = template; 482 @observable currentTemplate = [];
119 483
120 constructor(stores, actions) { 484 constructor(stores, actions) {
121 this.stores = stores; 485 this.stores = stores;
@@ -124,23 +488,45 @@ export default class FranzMenu {
124 autorun(this._build.bind(this)); 488 autorun(this._build.bind(this));
125 } 489 }
126 490
491 rebuild() {
492 this._build();
493 }
494
495 get template() {
496 return this.currentTemplate.toJS();
497 }
498
127 _build() { 499 _build() {
128 const tpl = toJS(this.tpl); 500 // console.log(window.franz);
501 const serviceTpl = Object.assign([], this.serviceTpl); // need to clone object so we don't modify computed (cached) object
502
503 if (window.franz === undefined) {
504 return;
505 }
506
507 const intl = window.franz.intl;
508 const tpl = isMac ? _templateFactory(intl) : _titleBarTemplateFactory(intl);
129 509
130 tpl[1].submenu.push({ 510 tpl[1].submenu.push({
131 role: 'toggledevtools', 511 type: 'separator',
132 }, { 512 }, {
133 label: 'Toggle Service Developer Tools', 513 label: intl.formatMessage(menuItems.toggleDevTools),
134 accelerator: 'CmdOrCtrl+Shift+Alt+i', 514 accelerator: `${cmdKey}+Alt+I`,
515 click: (menuItem, browserWindow) => {
516 browserWindow.webContents.toggleDevTools();
517 },
518 }, {
519 label: intl.formatMessage(menuItems.toggleServiceDevTools),
520 accelerator: `${cmdKey}+Shift+Alt+I`,
135 click: () => { 521 click: () => {
136 this.actions.service.openDevToolsForActiveService(); 522 this.actions.service.openDevToolsForActiveService();
137 }, 523 },
138 }); 524 });
139 525
140 tpl[1].submenu.unshift({ 526 tpl[1].submenu.unshift({
141 label: 'Reload Service', 527 label: intl.formatMessage(menuItems.reloadService),
142 id: 'reloadService', 528 id: 'reloadService', // TODO: needed?
143 accelerator: 'CmdOrCtrl+R', 529 accelerator: `${cmdKey}+R`,
144 click: () => { 530 click: () => {
145 if (this.stores.user.isLoggedIn 531 if (this.stores.user.isLoggedIn
146 && this.stores.services.enabled.length > 0) { 532 && this.stores.services.enabled.length > 0) {
@@ -150,93 +536,128 @@ export default class FranzMenu {
150 } 536 }
151 }, 537 },
152 }, { 538 }, {
153 label: 'Reload Franz', 539 label: intl.formatMessage(menuItems.reloadFranz),
154 accelerator: 'CmdOrCtrl+Shift+R', 540 accelerator: `${cmdKey}+Shift+R`,
155 click: () => { 541 click: () => {
156 window.location.reload(); 542 window.location.reload();
157 }, 543 },
158 }); 544 });
159 545
160 if (isMac) { 546 tpl.unshift({
161 tpl.unshift({ 547 label: isMac ? app.getName() : intl.formatMessage(menuItems.file),
162 label: app.getName(), 548 submenu: [
163 submenu: [ 549 {
164 { 550 label: intl.formatMessage(menuItems.about),
165 role: 'about', 551 role: 'about',
166 }, 552 },
167 { 553 {
168 type: 'separator', 554 type: 'separator',
169 }, 555 },
170 { 556 {
171 label: 'Settings', 557 label: intl.formatMessage(menuItems.settings),
172 accelerator: 'CmdOrCtrl+,', 558 accelerator: 'CmdOrCtrl+,',
173 click: () => { 559 click: () => {
174 this.actions.ui.openSettings({ path: 'app' }); 560 this.actions.ui.openSettings({ path: 'app' });
175 },
176 },
177 {
178 type: 'separator',
179 },
180 {
181 role: 'services',
182 submenu: [],
183 },
184 {
185 type: 'separator',
186 },
187 {
188 role: 'hide',
189 },
190 {
191 role: 'hideothers',
192 },
193 {
194 role: 'unhide',
195 },
196 {
197 type: 'separator',
198 }, 561 },
199 { 562 },
200 role: 'quit', 563 {
564 type: 'separator',
565 },
566 {
567 label: intl.formatMessage(menuItems.services),
568 role: 'services',
569 submenu: [],
570 },
571 {
572 type: 'separator',
573 },
574 {
575 label: intl.formatMessage(menuItems.hide),
576 role: 'hide',
577 },
578 {
579 label: intl.formatMessage(menuItems.hideOthers),
580 role: 'hideothers',
581 },
582 {
583 label: intl.formatMessage(menuItems.unhide),
584 role: 'unhide',
585 },
586 {
587 type: 'separator',
588 },
589 {
590 label: intl.formatMessage(menuItems.quit),
591 role: 'quit',
592 click: () => {
593 app.quit();
201 }, 594 },
202 ], 595 },
203 }); 596 ],
597 });
598
599 const about = {
600 label: intl.formatMessage(menuItems.about),
601 click: () => {
602 dialog.showMessageBox({
603 type: 'info',
604 title: 'Franz',
605 message: 'Franz',
606 detail: `Version: ${remote.app.getVersion()}\nRelease: ${process.versions.electron} / ${process.platform} / ${process.arch}`,
607 });
608 },
609 };
610
611 if (isMac) {
204 // Edit menu. 612 // Edit menu.
205 tpl[1].submenu.push( 613 tpl[1].submenu.push(
206 { 614 {
207 type: 'separator', 615 type: 'separator',
208 }, 616 },
209 { 617 {
210 label: 'Speech', 618 label: intl.formatMessage(menuItems.speech),
211 submenu: [ 619 submenu: [
212 { 620 {
621 label: intl.formatMessage(menuItems.startSpeaking),
213 role: 'startspeaking', 622 role: 'startspeaking',
214 }, 623 },
215 { 624 {
625 label: intl.formatMessage(menuItems.stopSpeaking),
216 role: 'stopspeaking', 626 role: 'stopspeaking',
217 }, 627 },
218 ], 628 ],
219 }, 629 },
220 ); 630 );
631
632 tpl[4].submenu.unshift(about, {
633 type: 'separator',
634 });
221 } else { 635 } else {
222 tpl[4].submenu.unshift({ 636 tpl[0].submenu = [
223 role: 'about', 637 {
224 click: () => { 638 label: intl.formatMessage(menuItems.settings),
225 dialog.showMessageBox({ 639 accelerator: 'Ctrl+P',
226 type: 'info', 640 click: () => {
227 title: 'Franz', 641 this.actions.ui.openSettings({ path: 'app' });
228 message: 'Franz', 642 },
229 detail: `Version: ${remote.app.getVersion()}\nRelease: ${process.versions.electron} / ${process.platform} / ${process.arch}`,
230 });
231 }, 643 },
232 }); 644 {
233 } 645 type: 'separator',
646 },
647 {
648 label: intl.formatMessage(menuItems.quit),
649 role: 'quit',
650 },
651 ];
234 652
235 const serviceTpl = this.serviceTpl; 653 tpl[5].submenu.push({
654 type: 'separator',
655 }, about);
656 }
236 657
237 serviceTpl.unshift({ 658 serviceTpl.unshift({
238 label: 'Add new Service', 659 label: intl.formatMessage(menuItems.addNewService),
239 accelerator: 'CmdOrCtrl+N', 660 accelerator: `${cmdKey}+N`,
240 click: () => { 661 click: () => {
241 this.actions.ui.openSettings({ path: 'recipes' }); 662 this.actions.ui.openSettings({ path: 'recipes' });
242 }, 663 },
@@ -245,9 +666,10 @@ export default class FranzMenu {
245 }); 666 });
246 667
247 if (serviceTpl.length > 0) { 668 if (serviceTpl.length > 0) {
248 tpl[isMac ? 3 : 2].submenu = toJS(this.serviceTpl); 669 tpl[3].submenu = serviceTpl;
249 } 670 }
250 671
672 this.currentTemplate = tpl;
251 const menu = Menu.buildFromTemplate(tpl); 673 const menu = Menu.buildFromTemplate(tpl);
252 Menu.setApplicationMenu(menu); 674 Menu.setApplicationMenu(menu);
253 } 675 }
@@ -258,7 +680,7 @@ export default class FranzMenu {
258 if (this.stores.user.isLoggedIn) { 680 if (this.stores.user.isLoggedIn) {
259 return services.map((service, i) => ({ 681 return services.map((service, i) => ({
260 label: this._getServiceName(service), 682 label: this._getServiceName(service),
261 accelerator: i <= 9 ? `CmdOrCtrl+${i + 1}` : null, 683 accelerator: i <= 9 ? `${cmdKey}+${i + 1}` : null,
262 type: 'radio', 684 type: 'radio',
263 checked: service.isActive, 685 checked: service.isActive,
264 click: () => { 686 click: () => {
diff --git a/src/lib/analytics.js b/src/lib/analytics.js
index b13bf8faa..8b9a44579 100644
--- a/src/lib/analytics.js
+++ b/src/lib/analytics.js
@@ -2,6 +2,8 @@ import { remote } from 'electron';
2import { GA_ID } from '../config'; 2import { GA_ID } from '../config';
3// import { isDevMode } from '../environment'; 3// import { isDevMode } from '../environment';
4 4
5const debug = require('debug')('Franz:Analytics');
6
5const { app } = remote; 7const { app } = remote;
6 8
7/* eslint-disable */ 9/* eslint-disable */
@@ -28,13 +30,13 @@ ga('send', 'App');
28export function gaPage(page) { 30export function gaPage(page) {
29 ga('send', 'pageview', page); 31 ga('send', 'pageview', page);
30 32
31 console.debug('GA track page', page); 33 debug('GA track page', page);
32} 34}
33 35
34export function gaEvent(category, action, label) { 36export function gaEvent(category, action, label) {
35 ga('send', 'event', category, action, label); 37 ga('send', 'event', category, action, label);
36 38
37 console.debug('GA track page', category, action); 39 debug('GA track page', category, action);
38} 40}
39 41
40setTimeout(() => { 42setTimeout(() => {
diff --git a/src/models/News.js b/src/models/News.js
index a96e6550f..caf1d70e5 100644
--- a/src/models/News.js
+++ b/src/models/News.js
@@ -1,10 +1,10 @@
1// @flow 1// @flow
2 2
3export default class News { 3export default class News {
4 id: string = ''; 4 id = '';
5 message: string = ''; 5 message = '';
6 type: string = 'primary'; 6 type = 'primary';
7 sticky: bool = false; 7 sticky = false;
8 8
9 constructor(data) { 9 constructor(data) {
10 if (!data.id) { 10 if (!data.id) {
diff --git a/src/models/Plan.js b/src/models/Plan.js
index e77353824..c7b4a0962 100644
--- a/src/models/Plan.js
+++ b/src/models/Plan.js
@@ -1,11 +1,11 @@
1// @flow 1// @flow
2 2
3export default class Plan { 3export default class Plan {
4 month: { 4 month = {
5 id: '', 5 id: '',
6 price: 0, 6 price: 0,
7 } 7 }
8 year: { 8 year = {
9 id: '', 9 id: '',
10 price: 0, 10 price: 0,
11 } 11 }
diff --git a/src/models/Recipe.js b/src/models/Recipe.js
index 1fc23ac89..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 = '';
@@ -32,8 +34,12 @@ export default class Recipe {
32 throw Error(`Recipe '${data.name}' requires Id`); 34 throw Error(`Recipe '${data.name}' requires Id`);
33 } 35 }
34 36
35 if (!semver.valid(data.version)) { 37 try {
36 throw Error(`Version ${data.version} of recipe '${data.name}' is not a valid semver version`); 38 if (!semver.valid(data.version)) {
39 throw Error(`Version ${data.version} of recipe '${data.name}' is not a valid semver version`);
40 }
41 } catch (e) {
42 console.warn(e.message);
37 } 43 }
38 44
39 this.id = data.id || this.id; 45 this.id = data.id || this.id;
@@ -69,4 +75,8 @@ export default class Recipe {
69 75
70 return []; 76 return [];
71 } 77 }
78
79 get hasDarkMode() {
80 return fs.pathExistsSync(path.join(this.path, 'darkmode.css'));
81 }
72} 82}
diff --git a/src/models/RecipePreview.js b/src/models/RecipePreview.js
index 525a5c4b5..7470d757a 100644
--- a/src/models/RecipePreview.js
+++ b/src/models/RecipePreview.js
@@ -1,10 +1,10 @@
1// @flow 1// @flow
2 2
3export default class RecipePreview { 3export default class RecipePreview {
4 id: string = ''; 4 id = '';
5 name: string = ''; 5 name = '';
6 icon: string = ''; // TODO: check if this isn't replaced by `icons` 6 icon = ''; // TODO: check if this isn't replaced by `icons`
7 featured: bool = false; 7 featured = false;
8 8
9 constructor(data) { 9 constructor(data) {
10 if (!data.id) { 10 if (!data.id) {
diff --git a/src/models/Service.js b/src/models/Service.js
index bafb3f564..1bab8bd68 100644
--- a/src/models/Service.js
+++ b/src/models/Service.js
@@ -7,7 +7,7 @@ export default class Service {
7 recipe = ''; 7 recipe = '';
8 webview = null; 8 webview = null;
9 timer = null; 9 timer = null;
10 events: {}; 10 events = {};
11 11
12 isAttached = false; 12 isAttached = false;
13 13
@@ -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/models/Settings.js b/src/models/Settings.js
index e39b63087..0e4c59057 100644
--- a/src/models/Settings.js
+++ b/src/models/Settings.js
@@ -2,19 +2,29 @@ import { observable, extendObservable } from 'mobx';
2import { DEFAULT_APP_SETTINGS } from '../config'; 2import { DEFAULT_APP_SETTINGS } from '../config';
3 3
4export default class Settings { 4export default class Settings {
5 @observable autoLaunchInBackground = DEFAULT_APP_SETTINGS.autoLaunchInBackground; 5 @observable app = DEFAULT_APP_SETTINGS
6 @observable runInBackground = DEFAULT_APP_SETTINGS.runInBackground;
7 @observable enableSystemTray = DEFAULT_APP_SETTINGS.enableSystemTray;
8 @observable minimizeToSystemTray = DEFAULT_APP_SETTINGS.minimizeToSystemTray;
9 @observable showDisabledServices = DEFAULT_APP_SETTINGS.showDisabledServices;
10 @observable showMessageBadgeWhenMuted = DEFAULT_APP_SETTINGS.showMessageBadgeWhenMuted;
11 @observable enableSpellchecking = DEFAULT_APP_SETTINGS.enableSpellchecking;
12 @observable locale = DEFAULT_APP_SETTINGS.locale;
13 @observable beta = DEFAULT_APP_SETTINGS.beta;
14 @observable isAppMuted = DEFAULT_APP_SETTINGS.isAppMuted;
15 6
16 constructor(data) { 7 @observable service = {
17 Object.assign(this, data); 8 activeService: '',
9 }
10
11 @observable group = {
12 collapsed: [],
13 disabled: [],
14 }
15
16 @observable stats = {
17 appStarts: 0,
18 }
19
20 @observable migration = {}
21
22 constructor({ app, service, group, stats, migration }) {
23 Object.assign(this.app, app);
24 Object.assign(this.service, service);
25 Object.assign(this.group, group);
26 Object.assign(this.stats, stats);
27 Object.assign(this.migration, migration);
18 } 28 }
19 29
20 update(data) { 30 update(data) {
diff --git a/src/models/User.js b/src/models/User.js
index 6fca78f90..3e4aa187d 100644
--- a/src/models/User.js
+++ b/src/models/User.js
@@ -1,4 +1,4 @@
1import { observable, computed } from 'mobx'; 1import { observable } from 'mobx';
2 2
3export default class User { 3export default class User {
4 id = null; 4 id = null;
@@ -15,14 +15,7 @@ export default class User {
15 @observable donor = {}; 15 @observable donor = {};
16 @observable isDonor = false; 16 @observable isDonor = false;
17 @observable isMiner = false; 17 @observable isMiner = false;
18 @observable isSSO = false; 18 @observable locale = false;
19 @observable company = {
20 name: 'Happle Apps',
21 contact: {
22 technical: 'technical@company.com',
23 default: 'default@company.com',
24 },
25 };
26 19
27 constructor(data) { 20 constructor(data) {
28 if (!data.id) { 21 if (!data.id) {
@@ -41,12 +34,6 @@ export default class User {
41 this.isDonor = data.isDonor || this.isDonor; 34 this.isDonor = data.isDonor || this.isDonor;
42 this.isSubscriptionOwner = data.isSubscriptionOwner || this.isSubscriptionOwner; 35 this.isSubscriptionOwner = data.isSubscriptionOwner || this.isSubscriptionOwner;
43 this.isMiner = data.isMiner || this.isMiner; 36 this.isMiner = data.isMiner || this.isMiner;
44 this.isSSO = data.isSSO || this.isSSO; 37 this.locale = data.locale || this.locale;
45 this.company = data.company || this.company;
46 }
47
48 @computed get isEnterprise() {
49 // return false
50 return this.company.name !== undefined;
51 } 38 }
52} 39}
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js
index 162422017..9ad4cd531 100644
--- a/src/stores/AppStore.js
+++ b/src/stores/AppStore.js
@@ -15,7 +15,11 @@ import { gaEvent } from '../lib/analytics';
15 15
16import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '../helpers/service-helpers.js'; 16import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '../helpers/service-helpers.js';
17 17
18const { app } = remote; 18const debug = require('debug')('Franz:AppStore');
19
20const { app, systemPreferences } = remote;
21
22const mainWindow = remote.getCurrentWindow();
19 23
20const defaultLocale = DEFAULT_APP_SETTINGS.locale; 24const defaultLocale = DEFAULT_APP_SETTINGS.locale;
21const autoLauncher = new AutoLaunch({ 25const autoLauncher = new AutoLaunch({
@@ -46,8 +50,12 @@ export default class AppStore extends Store {
46 50
47 @observable isSystemMuteOverridden = false; 51 @observable isSystemMuteOverridden = false;
48 52
53 @observable isSystemDarkModeEnabled = false;
54
49 @observable isClearingAllCache = false; 55 @observable isClearingAllCache = false;
50 56
57 @observable isFullScreen = mainWindow.isFullScreen();
58
51 constructor(...args) { 59 constructor(...args) {
52 super(...args); 60 super(...args);
53 61
@@ -80,6 +88,10 @@ export default class AppStore extends Store {
80 window.addEventListener('online', () => { this.isOnline = true; }); 88 window.addEventListener('online', () => { this.isOnline = true; });
81 window.addEventListener('offline', () => { this.isOnline = false; }); 89 window.addEventListener('offline', () => { this.isOnline = false; });
82 90
91 mainWindow.on('enter-full-screen', () => { this.isFullScreen = true; });
92 mainWindow.on('leave-full-screen', () => { this.isFullScreen = false; });
93
94
83 this.isOnline = navigator.onLine; 95 this.isOnline = navigator.onLine;
84 96
85 // Check if Franz should launch on start 97 // Check if Franz should launch on start
@@ -98,6 +110,10 @@ export default class AppStore extends Store {
98 ipcRenderer.on('autoUpdate', (event, data) => { 110 ipcRenderer.on('autoUpdate', (event, data) => {
99 if (data.available) { 111 if (data.available) {
100 this.updateStatus = this.updateStatusTypes.AVAILABLE; 112 this.updateStatus = this.updateStatusTypes.AVAILABLE;
113
114 if (isMac) {
115 app.dock.bounce();
116 }
101 } 117 }
102 118
103 if (data.available !== undefined && !data.available) { 119 if (data.available !== undefined && !data.available) {
@@ -124,19 +140,6 @@ export default class AppStore extends Store {
124 this.stores.router.push(data.url); 140 this.stores.router.push(data.url);
125 }); 141 });
126 142
127 // Reload all services after a healthy nap
128 // Alternative solution for powerMonitor as the resume event is not fired
129 // More information: https://github.com/electron/electron/issues/1615
130 const TIMEOUT = 5000;
131 let lastTime = (new Date()).getTime();
132 setInterval(() => {
133 const currentTime = (new Date()).getTime();
134 if (currentTime > (lastTime + TIMEOUT + 2000)) {
135 this._reactivateServices();
136 }
137 lastTime = currentTime;
138 }, TIMEOUT);
139
140 // Set active the next service 143 // Set active the next service
141 key( 144 key(
142 '⌘+pagedown, ctrl+pagedown, ⌘+alt+right, ctrl+tab', () => { 145 '⌘+pagedown, ctrl+pagedown, ⌘+alt+right, ctrl+tab', () => {
@@ -158,6 +161,8 @@ export default class AppStore extends Store {
158 this.locale = this._getDefaultLocale(); 161 this.locale = this._getDefaultLocale();
159 162
160 this._healthCheck(); 163 this._healthCheck();
164
165 this.isSystemDarkModeEnabled = systemPreferences.isDarkMode();
161 } 166 }
162 167
163 @computed get cacheSize() { 168 @computed get cacheSize() {
@@ -166,7 +171,7 @@ export default class AppStore extends Store {
166 171
167 // Actions 172 // Actions
168 @action _notify({ title, options, notificationId, serviceId = null }) { 173 @action _notify({ title, options, notificationId, serviceId = null }) {
169 if (this.stores.settings.all.isAppMuted) return; 174 if (this.stores.settings.all.app.isAppMuted) return;
170 175
171 const notification = new window.Notification(title, options); 176 const notification = new window.Notification(title, options);
172 notification.onclick = (e) => { 177 notification.onclick = (e) => {
@@ -179,8 +184,6 @@ export default class AppStore extends Store {
179 184
180 this.actions.service.setActive({ serviceId }); 185 this.actions.service.setActive({ serviceId });
181 186
182 const mainWindow = remote.getCurrentWindow();
183
184 if (isWindows) { 187 if (isWindows) {
185 mainWindow.restore(); 188 mainWindow.restore();
186 } else if (isLinux) { 189 } else if (isLinux) {
@@ -244,17 +247,18 @@ export default class AppStore extends Store {
244 } 247 }
245 248
246 @action _muteApp({ isMuted, overrideSystemMute = true }) { 249 @action _muteApp({ isMuted, overrideSystemMute = true }) {
247 this.isSystemMuteOverriden = overrideSystemMute; 250 this.isSystemMuteOverridden = overrideSystemMute;
248 251
249 this.actions.settings.update({ 252 this.actions.settings.update({
250 settings: { 253 type: 'app',
254 data: {
251 isAppMuted: isMuted, 255 isAppMuted: isMuted,
252 }, 256 },
253 }); 257 });
254 } 258 }
255 259
256 @action _toggleMuteApp() { 260 @action _toggleMuteApp() {
257 this._muteApp({ isMuted: !this.stores.settings.all.isAppMuted }); 261 this._muteApp({ isMuted: !this.stores.settings.all.app.isAppMuted });
258 } 262 }
259 263
260 @action async _clearAllCache() { 264 @action async _clearAllCache() {
@@ -288,13 +292,19 @@ export default class AppStore extends Store {
288 } 292 }
289 293
290 _setLocale() { 294 _setLocale() {
291 const locale = this.stores.settings.all.locale; 295 let locale;
296 if (this.stores.user.isLoggedIn) {
297 locale = this.stores.user.data.locale;
298 }
299
292 300
293 if (locale && Object.prototype.hasOwnProperty.call(locales, locale) && locale !== this.locale) { 301 if (locale && Object.prototype.hasOwnProperty.call(locales, locale) && locale !== this.locale) {
294 this.locale = locale; 302 this.locale = locale;
295 } else if (!locale) { 303 } else if (!locale) {
296 this.locale = this._getDefaultLocale(); 304 this.locale = this._getDefaultLocale();
297 } 305 }
306
307 debug(`Set locale to "${this.locale}"`);
298 } 308 }
299 309
300 _getDefaultLocale() { 310 _getDefaultLocale() {
@@ -336,8 +346,9 @@ export default class AppStore extends Store {
336 // Helpers 346 // Helpers
337 _appStartsCounter() { 347 _appStartsCounter() {
338 this.actions.settings.update({ 348 this.actions.settings.update({
339 settings: { 349 type: 'stats',
340 appStarts: (this.stores.settings.all.appStarts || 0) + 1, 350 data: {
351 appStarts: (this.stores.settings.all.stats.appStarts || 0) + 1,
341 }, 352 },
342 }); 353 });
343 } 354 }
@@ -345,7 +356,8 @@ export default class AppStore extends Store {
345 async _autoStart() { 356 async _autoStart() {
346 this.autoLaunchOnStart = await this._checkAutoStart(); 357 this.autoLaunchOnStart = await this._checkAutoStart();
347 358
348 if (this.stores.settings.all.appStarts === 1) { 359 if (this.stores.settings.all.stats.appStarts === 1) {
360 debug('Set app to launch on start');
349 this.actions.app.launchOnStartup({ 361 this.actions.app.launchOnStartup({
350 enable: true, 362 enable: true,
351 }); 363 });
@@ -356,19 +368,9 @@ export default class AppStore extends Store {
356 return autoLauncher.isEnabled() || false; 368 return autoLauncher.isEnabled() || false;
357 } 369 }
358 370
359 _reactivateServices(retryCount = 0) {
360 if (!this.isOnline) {
361 console.debug('reactivateServices: computer is offline, trying again in 5s, retries:', retryCount);
362 setTimeout(() => this._reactivateServices(retryCount + 1), 5000);
363 } else {
364 console.debug('reactivateServices: reload Franz');
365 window.location.reload();
366 }
367 }
368
369 _systemDND() { 371 _systemDND() {
370 const dnd = getDoNotDisturb(); 372 const dnd = getDoNotDisturb();
371 if (dnd === this.stores.settings.all.isAppMuted || !this.isSystemMuteOverriden) { 373 if (dnd !== this.stores.settings.all.app.isAppMuted && !this.isSystemMuteOverridden) {
372 this.actions.app.muteApp({ 374 this.actions.app.muteApp({
373 isMuted: dnd, 375 isMuted: dnd,
374 overrideSystemMute: false, 376 overrideSystemMute: false,
diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js
index a315d3b46..f788c347d 100644
--- a/src/stores/FeaturesStore.js
+++ b/src/stores/FeaturesStore.js
@@ -4,7 +4,7 @@ import Store from './lib/Store';
4import CachedRequest from './lib/CachedRequest'; 4import CachedRequest from './lib/CachedRequest';
5 5
6export default class FeaturesStore extends Store { 6export default class FeaturesStore extends Store {
7 @observable baseFeaturesRequest = new CachedRequest(this.api.features, 'base'); 7 @observable defaultFeaturesRequest = new CachedRequest(this.api.features, 'default');
8 @observable featuresRequest = new CachedRequest(this.api.features, 'features'); 8 @observable featuresRequest = new CachedRequest(this.api.features, 'features');
9 9
10 setup() { 10 setup() {
@@ -19,7 +19,7 @@ export default class FeaturesStore extends Store {
19 return this.featuresRequest.execute().result || {}; 19 return this.featuresRequest.execute().result || {};
20 } 20 }
21 21
22 return this.baseFeaturesRequest.execute().result || {}; 22 return this.defaultFeaturesRequest.execute().result || {};
23 } 23 }
24 24
25 _debugFeatures() { 25 _debugFeatures() {
@@ -30,7 +30,7 @@ export default class FeaturesStore extends Store {
30 if (this.stores.user.isLoggedIn) { 30 if (this.stores.user.isLoggedIn) {
31 this.featuresRequest.invalidate({ immediately: true }); 31 this.featuresRequest.invalidate({ immediately: true });
32 } else { 32 } else {
33 this.baseFeaturesRequest.invalidate({ immediately: true }); 33 this.defaultFeaturesRequest.invalidate({ immediately: true });
34 } 34 }
35 } 35 }
36} 36}
diff --git a/src/stores/RecipesStore.js b/src/stores/RecipesStore.js
index 67fee1d50..f2480bc8e 100644
--- a/src/stores/RecipesStore.js
+++ b/src/stores/RecipesStore.js
@@ -5,6 +5,8 @@ import CachedRequest from './lib/CachedRequest';
5import Request from './lib/Request'; 5import Request from './lib/Request';
6import { matchRoute } from '../helpers/routing-helpers'; 6import { matchRoute } from '../helpers/routing-helpers';
7 7
8const debug = require('debug')('Franz:RecipeStore');
9
8export default class RecipesStore extends Store { 10export default class RecipesStore extends Store {
9 @observable allRecipesRequest = new CachedRequest(this.api.recipes, 'all'); 11 @observable allRecipesRequest = new CachedRequest(this.api.recipes, 'all');
10 @observable installRecipeRequest = new Request(this.api.recipes, 'install'); 12 @observable installRecipeRequest = new Request(this.api.recipes, 'install');
@@ -34,7 +36,7 @@ export default class RecipesStore extends Store {
34 return activeRecipe; 36 return activeRecipe;
35 } 37 }
36 38
37 console.warn('Recipe not installed'); 39 debug(`Recipe ${match.id} not installed`);
38 } 40 }
39 41
40 return null; 42 return null;
@@ -54,10 +56,8 @@ export default class RecipesStore extends Store {
54 56
55 // Actions 57 // Actions
56 @action async _install({ recipeId }) { 58 @action async _install({ recipeId }) {
57 // console.log(this.installRecipeRequest._promise);
58 const recipe = await this.installRecipeRequest.execute(recipeId)._promise; 59 const recipe = await this.installRecipeRequest.execute(recipeId)._promise;
59 await this.allRecipesRequest.invalidate({ immediately: true })._promise; 60 await this.allRecipesRequest.invalidate({ immediately: true })._promise;
60 // console.log(this.installRecipeRequest._promise);
61 61
62 return recipe; 62 return recipe;
63 } 63 }
@@ -67,7 +67,7 @@ export default class RecipesStore extends Store {
67 const recipes = {}; 67 const recipes = {};
68 68
69 // Hackfix, reference this.all to fetch services 69 // Hackfix, reference this.all to fetch services
70 console.debug(`Check Recipe updates for ${this.all.map(recipe => recipe.id)}`); 70 debug(`Check Recipe updates for ${this.all.map(recipe => recipe.id)}`);
71 71
72 recipeIds.forEach((r) => { 72 recipeIds.forEach((r) => {
73 const recipe = this.one(r); 73 const recipe = this.one(r);
diff --git a/src/stores/RequestStore.js b/src/stores/RequestStore.js
index 4140ca362..bbfe6f6df 100644
--- a/src/stores/RequestStore.js
+++ b/src/stores/RequestStore.js
@@ -2,6 +2,8 @@ import { action, computed, observable } from 'mobx';
2 2
3import Store from './lib/Store'; 3import Store from './lib/Store';
4 4
5const debug = require('debug')('Franz:RequestsStore');
6
5export default class RequestStore extends Store { 7export default class RequestStore extends Store {
6 @observable userInfoRequest; 8 @observable userInfoRequest;
7 @observable servicesRequest; 9 @observable servicesRequest;
@@ -52,7 +54,7 @@ export default class RequestStore extends Store {
52 } 54 }
53 55
54 this._autoRetry(); 56 this._autoRetry();
55 console.debug(`Retry required requests delayed in ${(delay) / 1000}s`); 57 debug(`Retry required requests delayed in ${(delay) / 1000}s`);
56 }, delay); 58 }, delay);
57 } 59 }
58 } 60 }
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index c38d0d9ee..cdb2db142 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -1,8 +1,5 @@
1// import { remote } from 'electron'; 1import { action, reaction, computed, observable } from 'mobx';
2import { action, computed, observable } from 'mobx';
3import { debounce, remove } from 'lodash'; 2import { debounce, remove } from 'lodash';
4// import path from 'path';
5// import fs from 'fs-extra';
6 3
7import Store from './lib/Store'; 4import Store from './lib/Store';
8import Request from './lib/Request'; 5import Request from './lib/Request';
@@ -10,6 +7,8 @@ import CachedRequest from './lib/CachedRequest';
10import { matchRoute } from '../helpers/routing-helpers'; 7import { matchRoute } from '../helpers/routing-helpers';
11import { gaEvent } from '../lib/analytics'; 8import { gaEvent } from '../lib/analytics';
12 9
10const debug = require('debug')('Franz:ServiceStore');
11
13export default class ServicesStore extends Store { 12export default class ServicesStore extends Store {
14 @observable allServicesRequest = new CachedRequest(this.api.services, 'all'); 13 @observable allServicesRequest = new CachedRequest(this.api.services, 'all');
15 @observable createServiceRequest = new Request(this.api.services, 'create'); 14 @observable createServiceRequest = new Request(this.api.services, 'create');
@@ -61,13 +60,20 @@ export default class ServicesStore extends Store {
61 this._mapActiveServiceToServiceModelReaction.bind(this), 60 this._mapActiveServiceToServiceModelReaction.bind(this),
62 this._saveActiveService.bind(this), 61 this._saveActiveService.bind(this),
63 this._logoutReaction.bind(this), 62 this._logoutReaction.bind(this),
64 this._shareSettingsWithServiceProcess.bind(this),
65 ]); 63 ]);
66 64
67 // Just bind this 65 // Just bind this
68 this._initializeServiceRecipeInWebview.bind(this); 66 this._initializeServiceRecipeInWebview.bind(this);
69 } 67 }
70 68
69 setup() {
70 // Single key reactions
71 reaction(
72 () => this.stores.settings.all.app.enableSpellchecking,
73 () => this._shareSettingsWithServiceProcess(),
74 );
75 }
76
71 @computed get all() { 77 @computed get all() {
72 if (this.stores.user.isLoggedIn) { 78 if (this.stores.user.isLoggedIn) {
73 const services = this.allServicesRequest.execute().result; 79 const services = this.allServicesRequest.execute().result;
@@ -84,7 +90,13 @@ export default class ServicesStore extends Store {
84 } 90 }
85 91
86 @computed get allDisplayed() { 92 @computed get allDisplayed() {
87 return this.stores.settings.all.showDisabledServices ? this.all : this.enabled; 93 return this.stores.settings.all.app.showDisabledServices ? this.all : this.enabled;
94 }
95
96 // This is just used to avoid unnecessary rerendering of resource-heavy webviews
97 @computed get allDisplayedUnordered() {
98 const services = this.allServicesRequest.execute().result || [];
99 return this.stores.settings.all.app.showDisabledServices ? services : services.filter(service => service.isEnabled);
88 } 100 }
89 101
90 @computed get filtered() { 102 @computed get filtered() {
@@ -103,7 +115,7 @@ export default class ServicesStore extends Store {
103 return activeService; 115 return activeService;
104 } 116 }
105 117
106 console.warn('Service not available'); 118 debug('Service not available');
107 } 119 }
108 120
109 return null; 121 return null;
@@ -117,10 +129,10 @@ export default class ServicesStore extends Store {
117 const recipesStore = this.stores.recipes; 129 const recipesStore = this.stores.recipes;
118 130
119 if (recipesStore.isInstalled(recipeId)) { 131 if (recipesStore.isInstalled(recipeId)) {
120 console.debug('Recipe is installed'); 132 debug(`Recipe ${recipeId} is installed`);
121 this._redirectToAddServiceRoute(recipeId); 133 this._redirectToAddServiceRoute(recipeId);
122 } else { 134 } else {
123 console.warn('Recipe is not installed'); 135 debug(`Recipe ${recipeId} is not installed`);
124 // We access the RecipeStore action directly 136 // We access the RecipeStore action directly
125 // returns Promise instead of action 137 // returns Promise instead of action
126 await this.stores.recipes._install({ recipeId }); 138 await this.stores.recipes._install({ recipeId });
@@ -202,6 +214,14 @@ export default class ServicesStore extends Store {
202 await request._promise; 214 await request._promise;
203 this.actionStatus = request.result.status; 215 this.actionStatus = request.result.status;
204 216
217 if (service.isEnabled) {
218 this._sendIPCMessage({
219 serviceId,
220 channel: 'service-settings-update',
221 args: newData,
222 });
223 }
224
205 if (redirect) { 225 if (redirect) {
206 this.stores.router.push('/settings/services'); 226 this.stores.router.push('/settings/services');
207 gaEvent('Service', 'update', service.recipe.id); 227 gaEvent('Service', 'update', service.recipe.id);
@@ -326,7 +346,7 @@ export default class ServicesStore extends Store {
326 }); 346 });
327 } else if (channel === 'notification') { 347 } else if (channel === 'notification') {
328 const options = args[0].options; 348 const options = args[0].options;
329 if (service.recipe.hasNotificationSound || service.isMuted || this.stores.settings.all.isAppMuted) { 349 if (service.recipe.hasNotificationSound || service.isMuted || this.stores.settings.all.app.isAppMuted) {
330 Object.assign(options, { 350 Object.assign(options, {
331 silent: true, 351 silent: true,
332 }); 352 });
@@ -426,7 +446,7 @@ export default class ServicesStore extends Store {
426 } 446 }
427 447
428 @action _reorder({ oldIndex, newIndex }) { 448 @action _reorder({ oldIndex, newIndex }) {
429 const showDisabledServices = this.stores.settings.all.showDisabledServices; 449 const showDisabledServices = this.stores.settings.all.app.showDisabledServices;
430 const oldEnabledSortIndex = showDisabledServices ? oldIndex : this.all.indexOf(this.enabled[oldIndex]); 450 const oldEnabledSortIndex = showDisabledServices ? oldIndex : this.all.indexOf(this.enabled[oldIndex]);
431 const newEnabledSortIndex = showDisabledServices ? newIndex : this.all.indexOf(this.enabled[newIndex]); 451 const newEnabledSortIndex = showDisabledServices ? newIndex : this.all.indexOf(this.enabled[newIndex]);
432 452
@@ -487,7 +507,7 @@ export default class ServicesStore extends Store {
487 if (service) { 507 if (service) {
488 service.webview.openDevTools(); 508 service.webview.openDevTools();
489 } else { 509 } else {
490 console.warn('No service is active'); 510 debug('No service is active');
491 } 511 }
492 } 512 }
493 513
@@ -504,7 +524,8 @@ export default class ServicesStore extends Store {
504 524
505 if (service) { 525 if (service) {
506 this.actions.settings.update({ 526 this.actions.settings.update({
507 settings: { 527 type: 'service',
528 data: {
508 activeService: service.id, 529 activeService: service.id,
509 }, 530 },
510 }); 531 });
@@ -512,7 +533,7 @@ export default class ServicesStore extends Store {
512 } 533 }
513 534
514 _mapActiveServiceToServiceModelReaction() { 535 _mapActiveServiceToServiceModelReaction() {
515 const { activeService } = this.stores.settings.all; 536 const { activeService } = this.stores.settings.all.service;
516 if (this.allDisplayed.length) { 537 if (this.allDisplayed.length) {
517 this.allDisplayed.map(service => Object.assign(service, { 538 this.allDisplayed.map(service => Object.assign(service, {
518 isActive: activeService ? activeService === service.id : this.allDisplayed[0].id === service.id, 539 isActive: activeService ? activeService === service.id : this.allDisplayed[0].id === service.id,
@@ -521,7 +542,7 @@ export default class ServicesStore extends Store {
521 } 542 }
522 543
523 _getUnreadMessageCountReaction() { 544 _getUnreadMessageCountReaction() {
524 const showMessageBadgeWhenMuted = this.stores.settings.all.showMessageBadgeWhenMuted; 545 const showMessageBadgeWhenMuted = this.stores.settings.all.app.showMessageBadgeWhenMuted;
525 const showMessageBadgesEvenWhenMuted = this.stores.ui.showMessageBadgesEvenWhenMuted; 546 const showMessageBadgesEvenWhenMuted = this.stores.ui.showMessageBadgesEvenWhenMuted;
526 547
527 const unreadDirectMessageCount = this.allDisplayed 548 const unreadDirectMessageCount = this.allDisplayed
@@ -545,7 +566,10 @@ export default class ServicesStore extends Store {
545 566
546 _logoutReaction() { 567 _logoutReaction() {
547 if (!this.stores.user.isLoggedIn) { 568 if (!this.stores.user.isLoggedIn) {
548 this.actions.settings.remove({ key: 'activeService' }); 569 this.actions.settings.remove({
570 type: 'service',
571 key: 'activeService',
572 });
549 this.allServicesRequest.invalidate().reset(); 573 this.allServicesRequest.invalidate().reset();
550 } 574 }
551 } 575 }
@@ -553,7 +577,7 @@ export default class ServicesStore extends Store {
553 _shareSettingsWithServiceProcess() { 577 _shareSettingsWithServiceProcess() {
554 this.actions.service.sendIPCMessageToAllServices({ 578 this.actions.service.sendIPCMessageToAllServices({
555 channel: 'settings-update', 579 channel: 'settings-update',
556 args: this.stores.settings.all, 580 args: this.stores.settings.all.app,
557 }); 581 });
558 } 582 }
559 583
diff --git a/src/stores/SettingsStore.js b/src/stores/SettingsStore.js
index b7d803398..f1b067115 100644
--- a/src/stores/SettingsStore.js
+++ b/src/stores/SettingsStore.js
@@ -1,12 +1,19 @@
1import { ipcRenderer } from 'electron'; 1import { remote } from 'electron';
2import { action, computed } from 'mobx'; 2import { action, computed, observable } from 'mobx';
3import localStorage from 'mobx-localstorage'; 3import localStorage from 'mobx-localstorage';
4 4
5import Store from './lib/Store'; 5import Store from './lib/Store';
6import { gaEvent } from '../lib/analytics';
7import SettingsModel from '../models/Settings'; 6import SettingsModel from '../models/Settings';
7import Request from './lib/Request';
8import CachedRequest from './lib/CachedRequest';
9
10const { systemPreferences } = remote;
11const debug = require('debug')('Franz:SettingsStore');
8 12
9export default class SettingsStore extends Store { 13export default class SettingsStore extends Store {
14 @observable appSettingsRequest = new CachedRequest(this.api.local, 'getAppSettings');
15 @observable updateAppSettingsRequest = new Request(this.api.local, 'updateAppSettings');
16
10 constructor(...args) { 17 constructor(...args) {
11 super(...args); 18 super(...args);
12 19
@@ -15,36 +22,110 @@ export default class SettingsStore extends Store {
15 this.actions.settings.remove.listen(this._remove.bind(this)); 22 this.actions.settings.remove.listen(this._remove.bind(this));
16 } 23 }
17 24
18 setup() { 25 async setup() {
19 this._shareSettingsWithMainProcess(); 26 // We need to wait until `appSettingsRequest` has been executed once, otherwise we can't patch the result. If we don't wait we'd run into an issue with mobx not reacting to changes of previously not existing keys
27 await this.appSettingsRequest._promise;
28 await this._migrate();
20 } 29 }
21 30
22 @computed get all() { 31 @computed get all() {
23 return new SettingsModel(localStorage.getItem('app') || {}); 32 return new SettingsModel({
33 app: this.appSettingsRequest.execute().result || {},
34 service: localStorage.getItem('service') || {},
35 group: localStorage.getItem('group') || {},
36 stats: localStorage.getItem('stats') || {},
37 migration: localStorage.getItem('migration') || {},
38 });
24 } 39 }
25 40
26 @action async _update({ settings }) { 41 @action async _update({ type, data }) {
27 const appSettings = this.all; 42 const appSettings = this.all;
28 localStorage.setItem('app', Object.assign(appSettings, settings)); 43 if (type !== 'app') {
29 44 debug('Update settings', type, data, this.all);
30 // We need a little hack to wait until everything is patched 45 localStorage.setItem(type, Object.assign(appSettings[type], data));
31 setTimeout(() => this._shareSettingsWithMainProcess(), 0); 46 } else {
47 debug('Update settings on file system', type, data);
48 this.updateAppSettingsRequest.execute(data);
32 49
33 gaEvent('Settings', 'update'); 50 this.appSettingsRequest.patch((result) => {
51 if (!result) return;
52 Object.assign(result, data);
53 });
54 }
34 } 55 }
35 56
36 @action async _remove({ key }) { 57 @action async _remove({ type, key }) {
37 const appSettings = this.all; 58 if (type === 'app') return; // app keys can't be deleted
59
60 const appSettings = this.all[type];
38 if (Object.hasOwnProperty.call(appSettings, key)) { 61 if (Object.hasOwnProperty.call(appSettings, key)) {
39 delete appSettings[key]; 62 delete appSettings[key];
40 localStorage.setItem('app', appSettings);
41 }
42 63
43 this._shareSettingsWithMainProcess(); 64 this.actions.settings.update({
65 type,
66 data: appSettings,
67 });
68 }
44 } 69 }
45 70
46 // Reactions 71 // Helper
47 _shareSettingsWithMainProcess() { 72 async _migrate() {
48 ipcRenderer.send('settings', this.all); 73 const legacySettings = localStorage.getItem('app') || {};
74
75 if (!this.all.migration['5.0.0-beta.17-settings']) {
76 this.actions.settings.update({
77 type: 'app',
78 data: {
79 autoLaunchInBackground: legacySettings.autoLaunchInBackground,
80 runInBackground: legacySettings.runInBackground,
81 enableSystemTray: legacySettings.enableSystemTray,
82 minimizeToSystemTray: legacySettings.minimizeToSystemTray,
83 isAppMuted: legacySettings.isAppMuted,
84 enableGPUAcceleration: legacySettings.enableGPUAcceleration,
85 showMessageBadgeWhenMuted: legacySettings.showMessageBadgeWhenMuted,
86 showDisabledServices: legacySettings.showDisabledServices,
87 enableSpellchecking: legacySettings.enableSpellchecking,
88 },
89 });
90
91 this.actions.settings.update({
92 type: 'service',
93 data: {
94 activeService: legacySettings.activeService,
95 },
96 });
97
98 this.actions.settings.update({
99 type: 'migration',
100 data: {
101 '5.0.0-beta.17-settings': true,
102 },
103 });
104
105 localStorage.removeItem('app');
106
107 debug('Migrated settings to split stores');
108 }
109
110 // Enable dark mode once
111 if (!this.all.migration['5.0.0-beta.19-settings']) {
112 this.actions.settings.update({
113 type: 'app',
114 data: {
115 darkMode: systemPreferences.isDarkMode(),
116 },
117 });
118
119 this.actions.settings.update({
120 type: 'migration',
121 data: {
122 '5.0.0-beta.19-settings': true,
123 },
124 });
125
126 localStorage.removeItem('app');
127
128 debug('Set up dark mode');
129 }
49 } 130 }
50} 131}
diff --git a/src/stores/UIStore.js b/src/stores/UIStore.js
index 5e9cc9ba7..bee6c8bcf 100644
--- a/src/stores/UIStore.js
+++ b/src/stores/UIStore.js
@@ -17,7 +17,7 @@ export default class UIStore extends Store {
17 @computed get showMessageBadgesEvenWhenMuted() { 17 @computed get showMessageBadgesEvenWhenMuted() {
18 const settings = this.stores.settings.all; 18 const settings = this.stores.settings.all;
19 19
20 return (settings.isAppMuted && settings.showMessageBadgeWhenMuted) || !settings.isAppMuted; 20 return (settings.app.isAppMuted && settings.app.showMessageBadgeWhenMuted) || !settings.isAppMuted;
21 } 21 }
22 22
23 // Actions 23 // Actions
@@ -26,7 +26,7 @@ export default class UIStore extends Store {
26 this.stores.router.push(settingsPath); 26 this.stores.router.push(settingsPath);
27 } 27 }
28 28
29 @action _closeSettings(): void { 29 @action _closeSettings() {
30 this.stores.router.push('/'); 30 this.stores.router.push('/');
31 } 31 }
32 32
diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js
index 7dbbd955b..9d8ac5657 100644
--- a/src/stores/UserStore.js
+++ b/src/stores/UserStore.js
@@ -9,6 +9,8 @@ import Request from './lib/Request';
9import CachedRequest from './lib/CachedRequest'; 9import CachedRequest from './lib/CachedRequest';
10import { gaEvent } from '../lib/analytics'; 10import { gaEvent } from '../lib/analytics';
11 11
12const debug = require('debug')('Franz:UserStore');
13
12// TODO: split stores into UserStore and AuthStore 14// TODO: split stores into UserStore and AuthStore
13export default class UserStore extends Store { 15export default class UserStore extends Store {
14 BASE_ROUTE = '/auth'; 16 BASE_ROUTE = '/auth';
@@ -69,6 +71,11 @@ export default class UserStore extends Store {
69 ]); 71 ]);
70 } 72 }
71 73
74 setup() {
75 // Data migration
76 this._migrateUserLocale();
77 }
78
72 // Routes 79 // Routes
73 get loginRoute() { 80 get loginRoute() {
74 return this.LOGIN_ROUTE; 81 return this.LOGIN_ROUTE;
@@ -256,8 +263,10 @@ export default class UserStore extends Store {
256 263
257 // We need to set the beta flag for the SettingsStore 264 // We need to set the beta flag for the SettingsStore
258 this.actions.settings.update({ 265 this.actions.settings.update({
259 settings: { 266 type: 'app',
267 data: {
260 beta: data.beta, 268 beta: data.beta,
269 locale: data.locale,
261 }, 270 },
262 }); 271 });
263 } 272 }
@@ -292,4 +301,17 @@ export default class UserStore extends Store {
292 this.id = null; 301 this.id = null;
293 } 302 }
294 } 303 }
304
305 async _migrateUserLocale() {
306 await this.getUserInfoRequest._promise;
307
308 if (!this.data.locale) {
309 debug('Migrate "locale" to user data');
310 this.actions.user.update({
311 userData: {
312 locale: this.stores.app.locale,
313 },
314 });
315 }
316 }
295} 317}
diff --git a/src/styles/animations.scss b/src/styles/animations.scss
index 1e49af207..b121af7d2 100644
--- a/src/styles/animations.scss
+++ b/src/styles/animations.scss
@@ -1,49 +1,41 @@
1// FadeIn 1// FadeIn
2.fadeIn-appear { 2.fadeIn-appear { opacity: .01; }
3 opacity: 0.01;
4}
5 3
6.fadeIn-appear.fadeIn-appear-active { 4.fadeIn-appear.fadeIn-appear-active {
7 opacity: 1; 5 opacity: 1;
8 transition: opacity 0.5s ease-out; 6 transition: opacity .5s ease-out;
9} 7}
10 8
11.fadeIn-enter { 9.fadeIn-enter {
12 opacity: 0.01; 10 opacity: .01;
13 transition: opacity 0.5s ease-out; 11 transition: opacity .5s ease-out;
14} 12}
15 13
16.fadeIn-leave { 14.fadeIn-leave { opacity: 1; }
17 opacity: 1;
18}
19 15
20.fadeIn-leave.fadeIn-leave-active { 16.fadeIn-leave.fadeIn-leave-active {
21 opacity: 0.01; 17 opacity: .01;
22 transition: opacity 300ms ease-in; 18 transition: opacity 300ms ease-in;
23} 19}
24 20
25// FadeIn Fast 21// FadeIn Fast
26.fadeIn-fast-appear { 22.fadeIn-fast-appear { opacity: .01; }
27 opacity: 0.01;
28}
29 23
30.fadeIn-fast-appear.fadeIn-fast-appear-active { 24.fadeIn-fast-appear.fadeIn-fast-appear-active {
31 opacity: 1; 25 opacity: 1;
32 transition: opacity 0.25s ease-out; 26 transition: opacity .25s ease-out;
33} 27}
34 28
35.fadeIn-fast-enter { 29.fadeIn-fast-enter {
36 opacity: 0.01; 30 opacity: .01;
37 transition: opacity 0.25s ease-out; 31 transition: opacity .25s ease-out;
38} 32}
39 33
40.fadeIn-fast-leave { 34.fadeIn-fast-leave { opacity: 1; }
41 opacity: 1;
42}
43 35
44.fadeIn-fast-leave.fadeIn-fast-leave-active { 36.fadeIn-fast-leave.fadeIn-fast-leave-active {
45 opacity: 0.01; 37 opacity: .01;
46 transition: opacity 0.25s ease-in; 38 transition: opacity .25s ease-in;
47} 39}
48 40
49// Slide down 41// Slide down
@@ -54,37 +46,35 @@
54 46
55.slideDown-appear.slideDown-appear-active { 47.slideDown-appear.slideDown-appear-active {
56 max-height: 500px; 48 max-height: 500px;
57 transition: max-height 0.5s ease-out; 49 transition: max-height .5s ease-out;
58} 50}
59 51
60.slideDown-enter { 52.slideDown-enter {
61 max-height: 0; 53 max-height: 0;
62 transition: max-height 0.5s ease-out; 54 transition: max-height .5s ease-out;
63} 55}
64 56
65// Slide up 57// Slide up
66.slideUp-appear { 58.slideUp-appear {
67 transform: translateY(20px);
68 opacity: 0; 59 opacity: 0;
60 transform: translateY(20px);
69} 61}
70 62
71.slideUp-appear.slideUp-appear-active { 63.slideUp-appear.slideUp-appear-active {
72 transform: translateY(0px);
73 opacity: 1; 64 opacity: 1;
74 transition: all 0.3s ease-out; 65 transform: translateY(0px);
66 transition: all .3s ease-out;
75} 67}
76 68
77.slideUp-enter { 69.slideUp-enter {
78 transform: translateY(20px);
79 opacity: 0; 70 opacity: 0;
80 transition: all 0.3s ease-out; 71 transform: translateY(20px);
72 transition: all .3s ease-out;
81} 73}
82 74
83.slideUp-leave { 75.slideUp-leave { opacity: 1; }
84 opacity: 1;
85}
86 76
87.slideUp-leave.slideUp-leave-active { 77.slideUp-leave.slideUp-leave-active {
88 opacity: 0.01; 78 opacity: .01;
89 transition: opacity 300ms ease-in; 79 transition: opacity 300ms ease-in;
90} 80}
diff --git a/src/styles/auth.scss b/src/styles/auth.scss
index 9ad71867c..54e264dc6 100644
--- a/src/styles/auth.scss
+++ b/src/styles/auth.scss
@@ -1,144 +1,135 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3.theme__dark .auth {
4 background: $dark-theme-gray-darkest;
5
6 .auth__container {
7 background: $dark-theme-gray-darker;
8 box-shadow: 0 0 50px rgba($dark-theme-black, .2);
9 }
10
11 .auth__logo.auth__logo--sm {
12 border: 4px solid $dark-theme-black;
13 box-shadow: 0 0 6px rgba($dark-theme-black, .5);
14 }
15
16 .auth__links {
17 background: $dark-theme-gray-dark;
18
19 a { color: $dark-theme-text-color; }
20 }
21
22 .legal {
23 color: $dark-theme-text-color;
24
25 a { color: $dark-theme-gray-lightest; }
26 }
27}
28
3.auth { 29.auth {
30 background: $theme-brand-primary;
4 display: flex; 31 display: flex;
5 justify-content: center; 32 justify-content: center;
6 background: $theme-brand-primary;
7 33
8 .auth__layout { 34 .auth__layout {
9 width: 100%; 35 width: 100%;
10 &>div>span {
11 width: 100%;
12 }
13 // display: flex;
14 // align-items: center;
15 // justify-content: center;
16 // flex-direction: column;
17
18 // @media only screen and (max-height : 700px) {
19 // margin: 100px 0;
20 // }
21 36
22 &>div { 37 & > div {
38 align-items: center;
23 display: flex; 39 display: flex;
24 justify-content: center; 40 justify-content: center;
25 align-items: center;
26 41
27 &>span { 42 & > span {
28 position: absolute; 43 position: absolute;
44 width: 100%;
29 } 45 }
30 } 46 }
31 } 47 }
32 48
33 .auth__container { 49 .auth__container {
34 position: relative;
35 width: 350px;
36 height: auto;
37 margin: 40px auto 0 auto;
38 background: #FFF; 50 background: #FFF;
39 // padding: 20px;
40 border-radius: $theme-border-radius; 51 border-radius: $theme-border-radius;
41 box-shadow: 0 0 50px rgba(black, 0.2); 52 box-shadow: 0 0 50px rgba(black, .2);
53 height: auto;
54 margin: 40px auto 0;
55 position: relative;
56 width: 350px;
42 57
43 &.auth__container--signup { 58 &.auth__container--signup { width: 450px; }
44 width: 450px;
45 // margin-left: auto;
46 // margin-right: auto;
47 }
48 } 59 }
49 60
50 .auth__logo { 61 .auth__logo {
62 border-radius: $theme-border-radius;
51 display: block; 63 display: block;
52 width: 150px;
53 height: auto; 64 height: auto;
54 margin: -105px auto 20px auto; 65 margin: -105px auto 20px auto;
55 border-radius: $theme-border-radius; 66 width: 150px;
56 67
57 &.auth__logo--sm { 68 &.auth__logo--sm {
58 border: 4px solid #FFF; 69 border: 4px solid #FFF;
59 box-shadow: 0 0 6px rgba(black, 0.5);
60 border-radius: 100%; 70 border-radius: 100%;
71 box-shadow: 0 0 6px rgba(black, .5);
61 } 72 }
62 } 73 }
63 74
64 .auth__form { 75 .auth__form {
65 padding: 20px; 76 padding: 20px;
66 77
67 h1 { 78 h1 { text-align: center; }
68 text-align: center;
69 }
70 } 79 }
71 80
72 .auth__button { 81 .auth__button {
73 width: 100%; 82 width: 100%;
74 83
75 &.auth__button--skip { 84 &.auth__button--skip { margin: 10px auto 0; }
76 margin: 10px auto 0;
77 }
78 } 85 }
79 86
80 .auth__links { 87 .auth__links {
81 padding: 20px;
82 background: $theme-gray-lighter; 88 background: $theme-gray-lighter;
83 border-bottom-left-radius: $theme-border-radius; 89 border-bottom-left-radius: $theme-border-radius;
84 border-bottom-right-radius: $theme-border-radius; 90 border-bottom-right-radius: $theme-border-radius;
91 padding: 20px;
85 92
86 a { 93 a {
87 display: block; 94 display: block;
88 text-align: center;
89 color: $theme-gray; 95 color: $theme-gray;
90 margin-bottom: 8px; 96 margin-bottom: 8px;
97 text-align: center;
91 98
92 &:last-of-type { 99 &:last-of-type { margin-bottom: 0; }
93 margin-bottom: 0;
94 }
95 } 100 }
96 } 101 }
97 102
98 .auth__adlk { 103 .auth__adlk {
104 bottom: 15px;
99 position: absolute; 105 position: absolute;
100 right: 25px; 106 right: 25px;
101 bottom: 15px;
102 107
103 img { 108 img { width: 65px; }
104 width: 65px;
105 }
106 } 109 }
107 110
108 .auth__letter { 111 .auth__letter { margin-bottom: 30px; }
109 margin-bottom: 30px; 112 .scroll-container { z-index: 10; }
110 } 113 .info-bar { position: absolute; }
111
112 .scroll-container {
113 z-index: 10;
114 }
115
116 .info-bar {
117 position: absolute;
118 }
119 114
120 &__scroll-container { 115 &__scroll-container {
121 overflow: scroll;
122 width: 100%;
123 max-height: 100vh; 116 max-height: 100vh;
124 padding: 80px 0; 117 padding: 80px 0;
118 overflow: scroll;
119 width: 100%;
125 } 120 }
126 121
127 .available-services { 122 .available-services { margin-bottom: 15px; }
128 margin-bottom: 15px;
129 }
130 123
131 .unavailable-services { 124 .unavailable-services {
132 margin: 15px 0; 125 margin: 15px 0;
133 126
134 p { 127 p { text-transform: capitalize; }
135 text-transform: capitalize;
136 }
137 } 128 }
138 129
139 .legal { 130 .legal {
140 text-align: center;
141 margin-top: 20px;
142 color: $theme-gray-light; 131 color: $theme-gray-light;
132 margin-top: 20px;
133 text-align: center;
143 } 134 }
144} 135}
diff --git a/src/styles/badge.scss b/src/styles/badge.scss
index 18a653118..f9fac039a 100644
--- a/src/styles/badge.scss
+++ b/src/styles/badge.scss
@@ -1,11 +1,24 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3.theme__dark .badge {
4 background: $dark-theme-gray;
5 border-radius: $theme-border-radius-small;
6 color: $dark-theme-gray-lightest;
7
8 &.badge--primary,
9 &.badge--premium {
10 background: $theme-brand-primary;
11 color: $dark-theme-gray-smoke;
12 }
13}
14
15
3.badge { 16.badge {
4 font-size: 14px; 17 background: $theme-gray-lighter;
18 border-radius: $theme-border-radius;
5 display: inline-block; 19 display: inline-block;
20 font-size: 14px;
6 padding: 5px 10px; 21 padding: 5px 10px;
7 border-radius: $theme-border-radius;
8 background: $theme-gray-lighter;
9 22
10 &.badge--primary, 23 &.badge--primary,
11 &.badge--premium { 24 &.badge--premium {
diff --git a/src/styles/button.scss b/src/styles/button.scss
index 8d2adbbcc..a66345114 100644
--- a/src/styles/button.scss
+++ b/src/styles/button.scss
@@ -1,71 +1,94 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3.theme__dark .franz-form__button {
4 background: $theme-brand-primary;
5 color: $dark-theme-text-color;
6
7 &:hover { background: darken($theme-brand-primary, 5%); }
8 &:active { background: lighten($theme-brand-primary, 5%); }
9
10 &.franz-form__button--secondary {
11 background: $dark-theme-gray-lighter;
12 color: $dark-theme-text-color;
13
14 &:hover { background: lighten($dark-theme-gray-lighter, 10%); }
15 &:active { background: lighten($dark-theme-gray-lighter, 20%); }
16 }
17
18 &.franz-form__button--danger {
19 background: $theme-brand-danger;
20
21 &:hover { background: darken($theme-brand-danger, 5%); }
22 &:active { background: lighten($theme-brand-danger, 5%); }
23 }
24
25 &.franz-form__button--warning {
26 background: $theme-brand-warning;
27
28 &:hover { background: darken($theme-brand-warning, 5%); }
29 &:active { background: lighten($theme-brand-warning, 5%); }
30 }
31
32 &.franz-form__button--inverted {
33 border: 2px solid $theme-brand-primary;
34 color: $theme-brand-primary;
35
36 &:hover {
37 background: darken($theme-brand-primary, 5%);
38 color: $dark-theme-text-color;
39 }
40 }
41
42 &:disabled { opacity: .5; }
43}
44
3.franz-form__button { 45.franz-form__button {
4 position: relative;
5 background: $theme-brand-primary; 46 background: $theme-brand-primary;
47 border-radius: 3px;
6 display: block; 48 display: block;
7 padding: 10px 20px;
8 color: #FFF; 49 color: #FFF;
9 border-radius: 3px; 50 padding: 10px 20px;
10 transition: background 0.5s; 51 position: relative;
52 transition: background .5s;
11 text-align: center; 53 text-align: center;
12 54
13 &:hover { 55 &:hover { background: darken($theme-brand-primary, 5%) }
14 background: darken($theme-brand-primary, 5%);
15 }
16 56
17 &:active { 57 &:active {
18 transition: none;
19 background: lighten($theme-brand-primary, 5%); 58 background: lighten($theme-brand-primary, 5%);
59 transition: none;
20 } 60 }
21 61
22 &:disabled { 62 &:disabled { opacity: .2; }
23 opacity: 0.2;
24 }
25 63
26 &.franz-form__button--secondary { 64 &.franz-form__button--secondary {
27 background: $theme-gray-lighter; 65 background: $theme-gray-lighter;
28 color: $theme-gray; 66 color: $theme-gray;
29 67
30 &:hover { 68 &:hover { background: darken($theme-gray-lighter, 5%); }
31 background: darken($theme-gray-lighter, 5%); 69 &:active { background: lighten($theme-gray-lighter, 5%); }
32 }
33
34 &:active {
35 background: lighten($theme-gray-lighter, 5%);
36 }
37 } 70 }
38 71
39 &.franz-form__button--danger { 72 &.franz-form__button--danger {
40 background: $theme-brand-danger; 73 background: $theme-brand-danger;
41 74
42 &:hover { 75 &:hover { background: darken($theme-brand-danger, 5%); }
43 background: darken($theme-brand-danger, 5%); 76 &:active { background: lighten($theme-brand-danger, 5%); }
44 }
45
46 &:active {
47 background: lighten($theme-brand-danger, 5%);
48 }
49 } 77 }
50 78
51 &.franz-form__button--warning { 79 &.franz-form__button--warning {
52 background: $theme-brand-warning; 80 background: $theme-brand-warning;
53 81
54 &:hover { 82 &:hover { background: darken($theme-brand-warning, 5%); }
55 background: darken($theme-brand-warning, 5%); 83 &:active { background: lighten($theme-brand-warning, 5%); }
56 }
57
58 &:active {
59 background: lighten($theme-brand-warning, 5%);
60 }
61 } 84 }
62 85
63 &.franz-form__button--inverted { 86 &.franz-form__button--inverted {
64 background: none; 87 background: none;
65 padding: 10px 20px;
66 border: 2px solid $theme-brand-primary; 88 border: 2px solid $theme-brand-primary;
67 color: $theme-brand-primary; 89 color: $theme-brand-primary;
68 transition: background 0.5s, color 0.5s; 90 padding: 10px 20px;
91 transition: background .5s, color .5s;
69 92
70 &:hover { 93 &:hover {
71 background: darken($theme-brand-primary, 5%); 94 background: darken($theme-brand-primary, 5%);
@@ -74,11 +97,11 @@
74 } 97 }
75 98
76 .loader { 99 .loader {
100 display: inline-block;
101 height: 12px;
102 margin-right: 5px;
77 position: relative; 103 position: relative;
78 width: 20px; 104 width: 20px;
79 height: 12px;
80 z-index: 9999; 105 z-index: 9999;
81 display: inline-block;
82 margin-right: 5px;
83 } 106 }
84} 107}
diff --git a/src/styles/colors.scss b/src/styles/colors.scss
index 5d8302c28..4411a0e81 100644
--- a/src/styles/colors.scss
+++ b/src/styles/colors.scss
@@ -1,22 +1,38 @@
1$theme-brand-primary: #3498db; 1$theme-brand-primary: #3498db;
2$theme-brand-success: #5cb85c; 2$theme-brand-success: #5cb85c;
3$theme-brand-info: #5bc0de; 3$theme-brand-info: #5bc0de;
4$theme-brand-warning: #FF9F00; 4$theme-brand-warning: #FF9F00;
5$theme-brand-danger: #d9534f; 5$theme-brand-danger: #d9534f;
6 6
7$theme-gray-dark: #373a3c; 7$theme-gray-dark: #373a3c;
8$theme-gray: #55595c; 8$theme-gray: #55595c;
9$theme-gray-light: #818a91; 9$theme-gray-light: #818a91;
10$theme-gray-lighter: #eceeef; 10$theme-gray-lighter: #eceeef;
11$theme-gray-lightest: #f7f7f9; 11$theme-gray-lightest: #f7f7f9;
12 12
13$theme-border-radius: 6px; 13$theme-border-radius: 6px;
14$theme-border-radius-small: 3px; 14$theme-border-radius-small: 3px;
15 15
16$theme-sidebar-width: 68px; 16$theme-sidebar-width: 68px;
17 17
18$theme-text-color: $theme-gray-dark; 18$theme-text-color: $theme-gray-dark;
19 19
20$theme-transition-time: 0.5s; 20$theme-transition-time: .5s;
21 21
22$theme-inset-shadow: inset 0 2px 5px rgba(0,0,0,0.03); 22$theme-inset-shadow: inset 0 2px 5px rgba(0, 0, 0, .03);
23
24// Dark Theme
25$dark-theme-black: #1A1A1A;
26
27$dark-theme-gray-darkest: #1E1E1E;
28$dark-theme-gray-darker: #2D2F31;
29$dark-theme-gray-dark: #383A3B;
30
31$dark-theme-gray: #47494B;
32
33$dark-theme-gray-light: #515355;
34$dark-theme-gray-lighter: #8a8b8b;
35$dark-theme-gray-lightest: #FFF;
36
37$dark-theme-gray-smoke: #CED0D1;
38$dark-theme-text-color: #FFF;
diff --git a/src/styles/content-tabs.scss b/src/styles/content-tabs.scss
index 47dfea2c4..ca3820fb4 100644
--- a/src/styles/content-tabs.scss
+++ b/src/styles/content-tabs.scss
@@ -2,53 +2,43 @@
2 2
3.content-tabs { 3.content-tabs {
4 .content-tabs__tabs { 4 .content-tabs__tabs {
5 display: flex;
6 border-top-left-radius: $theme-border-radius-small; 5 border-top-left-radius: $theme-border-radius-small;
7 border-top-right-radius: $theme-border-radius-small; 6 border-top-right-radius: $theme-border-radius-small;
7 display: flex;
8 overflow: hidden; 8 overflow: hidden;
9 9
10 .content-tabs__item { 10 .content-tabs__item {
11 padding: 10px;
12 flex: 1;
13 // border: 1px solid $theme-gray-lightest;
14 color: $theme-gray-dark;
15 background: linear-gradient($theme-gray-lightest 80%, darken($theme-gray-lightest, 3%)); 11 background: linear-gradient($theme-gray-lightest 80%, darken($theme-gray-lightest, 3%));
16 border-right: 1px solid $theme-gray-lighter; 12 border-right: 1px solid $theme-gray-lighter;
13 color: $theme-gray-dark;
14 flex: 1;
15 padding: 10px;
17 transition: background $theme-transition-time; 16 transition: background $theme-transition-time;
18 17
19 &:last-of-type { 18 &:last-of-type { border-right: 0; }
20 border-right: 0;
21 }
22 19
23 &.is-active { 20 &.is-active {
24 background: $theme-brand-primary; 21 background: $theme-brand-primary;
25 color: #FFF;
26 box-shadow: none; 22 box-shadow: none;
23 color: #FFF;
27 } 24 }
28 } 25 }
29 } 26 }
30 27
31 .content-tabs__content { 28 .content-tabs__content {
32 padding: 20px 20px; 29 background: $theme-gray-lightest;
33 border-bottom-left-radius: $theme-border-radius-small; 30 border-bottom-left-radius: $theme-border-radius-small;
34 border-bottom-right-radius: $theme-border-radius-small; 31 border-bottom-right-radius: $theme-border-radius-small;
35 background: $theme-gray-lightest; 32 padding: 20px 20px;
36 33
37 .content-tabs__item { 34 .content-tabs__item {
38 top: 0;
39 display: none; 35 display: none;
36 top: 0;
40 37
41 &.is-active { 38 &.is-active { display: block; }
42 display: block;
43 }
44 }
45
46 .franz-form__input-wrapper {
47 background: #FFF;
48 } 39 }
49 40
50 .franz-form__field:last-of-type { 41 .franz-form__input-wrapper { background: #FFF; }
51 margin-bottom: 0; 42 .franz-form__field:last-of-type { margin-bottom: 0; }
52 }
53 } 43 }
54} 44}
diff --git a/src/styles/image-upload.scss b/src/styles/image-upload.scss
index 06176a7af..31300c227 100644
--- a/src/styles/image-upload.scss
+++ b/src/styles/image-upload.scss
@@ -1,74 +1,89 @@
1.theme__dark {
2 .image-upload {
3 background: $dark-theme-gray-darker;
4 border: 1px solid $dark-theme-gray-light;
5 color: $dark-theme-gray-lighter;
6
7 &__action {
8 &-background { background: rgba($dark-theme-black, .7); }
9
10 button {
11 color: $dark-theme-gray-lightest;
12
13 .mdi { color: $dark-theme-gray-lightest; }
14 }
15 }
16 }
17
18 .image-upload-wrapper .mdi { color: $dark-theme-gray-light; }
19}
20
1.image-upload { 21.image-upload {
2 position: absolute; 22 background: $theme-gray-lightest;
3 width: 140px;
4 height: 140px;
5 border: 1px solid $theme-gray-lighter; 23 border: 1px solid $theme-gray-lighter;
6 border-radius: $theme-border-radius-small; 24 border-radius: $theme-border-radius-small;
7 background: $theme-gray-lightest; 25 height: 140px;
8 overflow: hidden;
9 margin-top: 5px; 26 margin-top: 5px;
27 overflow: hidden;
28 position: relative;
29 width: 140px;
10 30
11 &__preview, 31 &__preview,
12 &__action { 32 &__action {
13 position: absolute;
14 top: 0;
15 left: 0; 33 left: 0;
34 position: absolute;
16 right: 0; 35 right: 0;
36 top: 0;
17 } 37 }
18 38
19 &__preview { 39 &__preview {
20 z-index: 1;
21 background-size: cover;
22 background-size: 100%;
23 background-repeat: no-repeat;
24 background-position: center center; 40 background-position: center center;
41 background-repeat: no-repeat;
42 background-size: cover;
25 border-radius: 3px; 43 border-radius: 3px;
44 z-index: 1;
26 } 45 }
27 46
28 &__action { 47 &__action {
29 position: relative;
30 z-index: 10;
31 opacity: 0;
32 transition: opacity 0.5s;
33 display: flex; 48 display: flex;
34 justify-content: center; 49 justify-content: center;
50 opacity: 0;
51 position: relative;
52 transition: opacity .5s;
53 z-index: 10;
35 54
36 &-background { 55 &-background {
37 position: absolute; 56 background: rgba($theme-gray, .7);
38 top: 0; 57 bottom: 0;
39 left: 0; 58 left: 0;
59 position: absolute;
40 right: 0; 60 right: 0;
41 bottom: 0; 61 top: 0;
42 background: rgba($theme-gray, 0.7);
43 z-index: 10; 62 z-index: 10;
44 } 63 }
45 64
46 button { 65 button {
66 color: #FFF;
47 position: relative; 67 position: relative;
48 z-index: 100; 68 z-index: 100;
49 color: #FFF;
50 69
51 .mdi { 70 .mdi { color: #FFF; }
52 color: #FFF;
53 }
54 } 71 }
55 } 72 }
56 73
57 &__dropzone { 74 &__dropzone {
58 text-align: center; 75 align-items: center;
59 border-radius: 5px; 76 border-radius: 5px;
60 padding: 10px;
61 display: flex; 77 display: flex;
62 align-items: center;
63 justify-content: center;
64 flex-direction: column; 78 flex-direction: column;
79 justify-content: center;
80 padding: 10px;
81 text-align: center;
65 } 82 }
66 83
67 &__dropzone, 84 &__dropzone,
68 button { 85 button {
69 .mdi { 86 .mdi { margin-bottom: 5px; }
70 margin-bottom: 5px;
71 }
72 87
73 p { 88 p {
74 font-size: 10px; 89 font-size: 10px;
@@ -76,16 +91,10 @@
76 } 91 }
77 } 92 }
78 93
79 &:hover { 94 &:hover .image-upload__action { opacity: 1; }
80 .image-upload__action {
81 opacity: 1;
82 }
83 }
84} 95}
85 96
86.image-upload-wrapper { 97.image-upload-wrapper .mdi {
87 .mdi { 98 color: $theme-gray-light;
88 font-size: 40px; 99 font-size: 40px;
89 color: $theme-gray-light; 100}
90 }
91} \ No newline at end of file
diff --git a/src/styles/info-bar.scss b/src/styles/info-bar.scss
index b6d1e84e2..fb4917358 100644
--- a/src/styles/info-bar.scss
+++ b/src/styles/info-bar.scss
@@ -1,83 +1,68 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3.info-bar { 3.info-bar {
4 width: 100%; 4 align-items: center;
5 height: 50px;
6 background: $theme-brand-primary; 5 background: $theme-brand-primary;
6 box-shadow: 0 0 8px rgba(black, .2);
7 display: flex; 7 display: flex;
8 align-items: center; 8 height: 50px;
9 justify-content: center; 9 justify-content: center;
10 padding: 0 20px; 10 padding: 0 20px;
11 position: relative; 11 position: relative;
12 // bottom: 0; 12 width: 100%;
13 z-index: 100; 13 z-index: 100;
14 box-shadow: 0 0 8px rgba(black, 0.2);
15 14
16 .info-bar__content { 15 .info-bar__content {
17 height: auto; 16 height: auto;
18 17
19 .mdi { 18 .mdi { margin-right: 5px; }
20 margin-right: 5px;
21 }
22 } 19 }
23 20
24 .info-bar__close { 21 .info-bar__close {
22 color: #FFF;
25 position: absolute; 23 position: absolute;
26 right: 10px; 24 right: 10px;
27 color: #FFF;
28 } 25 }
29 26
30 .info-bar__cta { 27 .info-bar__cta {
31 color: #FFF;
32 padding: 3px 8px;
33 border-radius: $theme-border-radius-small;
34 border-color: #FFF; 28 border-color: #FFF;
35 border-width: 2px; 29 border-radius: $theme-border-radius-small;
36 border-style: solid; 30 border-style: solid;
31 border-width: 2px;
32 color: #FFF;
37 margin-left: 15px; 33 margin-left: 15px;
34 padding: 3px 8px;
38 35
39 .loader { 36 .loader {
37 display: inline-block;
38 height: 12px;
39 margin-right: 5px;
40 position: relative; 40 position: relative;
41 width: 20px; 41 width: 20px;
42 height: 12px;
43 z-index: 9999; 42 z-index: 9999;
44 display: inline-block;
45 margin-right: 5px;
46 } 43 }
47 } 44 }
48 45
49 a { 46 &.info-bar--bottom { order: 10; }
50 // text-decoration: underline;
51 }
52
53 &.info-bar--bottom {
54 order: 10;
55 }
56 47
57 &.info-bar--primary { 48 &.info-bar--primary {
58 background: $theme-brand-primary; 49 background: $theme-brand-primary;
59 color: #FFF; 50 color: #FFF;
60 51
61 a { 52 a { color: #FFF; }
62 color: #FFF;
63 }
64 } 53 }
65 54
66 &.info-bar--warning { 55 &.info-bar--warning {
67 background: $theme-brand-warning; 56 background: $theme-brand-warning;
68 color: #FFF; 57 color: #FFF;
69 58
70 a { 59 a { color: #FFF; }
71 color: #FFF;
72 }
73 } 60 }
74 61
75 &.info-bar--danger { 62 &.info-bar--danger {
76 background: $theme-brand-danger; 63 background: $theme-brand-danger;
77 color: #FFF; 64 color: #FFF;
78 65
79 a { 66 a { color: #FFF; }
80 color: #FFF;
81 }
82 } 67 }
83} 68}
diff --git a/src/styles/infobox.scss b/src/styles/infobox.scss
index 7ab094058..e287e5be7 100644
--- a/src/styles/infobox.scss
+++ b/src/styles/infobox.scss
@@ -1,20 +1,16 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3.infobox { 3.infobox {
4 height: auto; 4 align-items: center;
5 padding: 15px 20px;
6 margin-bottom: 30px;
7 border-radius: $theme-border-radius-small; 5 border-radius: $theme-border-radius-small;
8 display: flex; 6 display: flex;
9 align-items: center; 7 height: auto;
8 margin-bottom: 30px;
9 padding: 15px 20px;
10 10
11 a { 11 a { color: #FFF; }
12 color: #FFF;
13 }
14 12
15 .infobox__content { 13 .infobox__content { flex: 1; }
16 flex: 1;
17 }
18 14
19 &.infobox--success { 15 &.infobox--success {
20 background: $theme-brand-success; 16 background: $theme-brand-success;
@@ -36,26 +32,24 @@
36 color: #FFF; 32 color: #FFF;
37 } 33 }
38 34
39 .mdi { 35 .mdi { margin-right: 10px; }
40 margin-right: 10px;
41 }
42 36
43 .infobox__cta { 37 .infobox__cta {
44 color: #FFF;
45 padding: 3px 8px;
46 border-radius: $theme-border-radius-small;
47 border-color: #FFF; 38 border-color: #FFF;
48 border-width: 2px; 39 border-radius: $theme-border-radius-small;
49 border-style: solid; 40 border-style: solid;
41 border-width: 2px;
42 color: #FFF;
50 margin-left: 15px; 43 margin-left: 15px;
44 padding: 3px 8px;
51 45
52 .loader { 46 .loader {
47 display: inline-block;
48 height: 12px;
49 margin-right: 5px;
53 position: relative; 50 position: relative;
54 width: 20px; 51 width: 20px;
55 height: 12px;
56 z-index: 9999; 52 z-index: 9999;
57 display: inline-block;
58 margin-right: 5px;
59 } 53 }
60 } 54 }
61 55
diff --git a/src/styles/input.scss b/src/styles/input.scss
index 7042f56e8..687bcac64 100644
--- a/src/styles/input.scss
+++ b/src/styles/input.scss
@@ -1,6 +1,34 @@
1@import './config.scss'; 1@import './config.scss';
2@import './mixins.scss'; 2@import './mixins.scss';
3 3
4.theme__dark .franz-form {
5 .franz-form__label { color: $dark-theme-text-color; }
6
7 .franz-form__input-wrapper {
8 background: $dark-theme-gray-dark;
9 border: 1px solid $dark-theme-gray-light;
10 }
11
12 .franz-form__input {
13 color: $dark-theme-gray-lightest;
14
15 &::placeholder { color: $dark-theme-gray-lighter; }
16 }
17
18 .franz-form__input-prefix,
19 .franz-form__input-suffix {
20 background: $dark-theme-gray;
21 color: $dark-theme-gray-lighter;
22 }
23
24 .franz-form__input-modifier {
25 border-left: 1px solid $dark-theme-gray-light;
26 color: $dark-theme-gray-lighter;
27 }
28
29 .franz-form__password-score { background: $dark-theme-gray-dark; }
30}
31
4.franz-form { 32.franz-form {
5 .franz-form__field { 33 .franz-form__field {
6 display: flex; 34 display: flex;
@@ -9,19 +37,12 @@
9 margin-bottom: 20px; 37 margin-bottom: 20px;
10 38
11 &.has-error { 39 &.has-error {
12 .franz-form__input-wrapper { 40 .franz-form__input-wrapper,
13 border-color: $theme-brand-danger; 41 .franz-form__input-modifier { border-color: $theme-brand-danger; }
14 }
15
16 .franz-form__input-modifier {
17 border-color: $theme-brand-danger;
18 }
19 } 42 }
20 } 43 }
21 44
22 .franz-form__label { 45 .franz-form__label { @include formLabel(); }
23 @include formLabel();
24 }
25 46
26 .franz-form__error { 47 .franz-form__error {
27 color: $theme-brand-danger; 48 color: $theme-brand-danger;
@@ -30,74 +51,60 @@
30 } 51 }
31 52
32 .franz-form__input-wrapper { 53 .franz-form__input-wrapper {
33 display: flex;
34 width: 100%;
35 order: 1;
36 border-radius: $theme-border-radius-small;
37 background: $theme-gray-lightest; 54 background: $theme-gray-lightest;
38 border: 1px solid $theme-gray-lighter; 55 border: 1px solid $theme-gray-lighter;
56 border-radius: $theme-border-radius-small;
57 display: flex;
39 flex-wrap: wrap; 58 flex-wrap: wrap;
59 order: 1;
60 width: 100%;
40 } 61 }
41 62
42 .franz-form__input { 63 .franz-form__input {
43 flex: 1;
44 border: 0;
45 background: none; 64 background: none;
46 width: 100%; 65 border: 0;
47 padding: 8px;
48 // font-size: 18px;
49 color: $theme-gray; 66 color: $theme-gray;
67 flex: 1;
68 padding: 8px;
69 width: 100%;
50 70
51 &::placeholder { 71 &::placeholder { color: lighten($theme-gray-light, 10%); }
52 color: lighten($theme-gray-light, 10%);
53 }
54 } 72 }
55 73
56 .franz-form__input-prefix, 74 .franz-form__input-prefix,
57 .franz-form__input-suffix { 75 .franz-form__input-suffix {
58 padding: 0 10px;
59 background: $theme-gray-lighter; 76 background: $theme-gray-lighter;
60 color: $theme-gray-light; 77 color: $theme-gray-light;
61 line-height: 35px; 78 line-height: 35px;
79 padding: 0 10px;
62 } 80 }
63 81
64 .franz-form__input-modifier { 82 .franz-form__input-modifier {
65 padding: 0 20px;
66 border-left: 1px solid $theme-gray-lighter; 83 border-left: 1px solid $theme-gray-lighter;
67 color: $theme-gray-light; 84 color: $theme-gray-light;
68 font-size: 20px; 85 font-size: 20px;
86 padding: 0 20px;
69 } 87 }
70 88
71 .franz-form__password-score { 89 .franz-form__password-score {
72 background: $theme-gray-lighter; 90 background: $theme-gray-lighter;
73 height: 5px;
74 flex-basis: 100%;
75 border-bottom-left-radius: 3px; 91 border-bottom-left-radius: 3px;
76 border-bottom-right-radius: 3px; 92 border-bottom-right-radius: 3px;
93 flex-basis: 100%;
94 height: 5px;
77 95
78 meter { 96 meter {
79 width: 100%;
80 height: 100%;
81 display: block;
82 border-bottom-left-radius: 3px; 97 border-bottom-left-radius: 3px;
83 border-bottom-right-radius: 3px; 98 border-bottom-right-radius: 3px;
99 display: block;
100 height: 100%;
84 overflow: hidden; 101 overflow: hidden;
102 width: 100%;
85 103
86 &::-webkit-meter-bar { 104 &::-webkit-meter-bar { background: none; }
87 background: none; 105 &::-webkit-meter-even-less-good-value { background: $theme-brand-danger; }
88 } 106 &::-webkit-meter-suboptimum-value { background: $theme-brand-warning; }
89 107 &::-webkit-meter-optimum-value { background: $theme-brand-success; }
90 &::-webkit-meter-even-less-good-value {
91 background: $theme-brand-danger;
92 }
93
94 &::-webkit-meter-suboptimum-value {
95 background: $theme-brand-warning;
96 }
97
98 &::-webkit-meter-optimum-value {
99 background: $theme-brand-success;
100 }
101 } 108 }
102 } 109 }
103} 110}
diff --git a/src/styles/invite.scss b/src/styles/invite.scss
index bfb1a4b6b..594224f62 100644
--- a/src/styles/invite.scss
+++ b/src/styles/invite.scss
@@ -1,15 +1,8 @@
1.invite__form { 1.invite__form {
2 /* play with values to see different layouts */
3 // display: flex;
4 align-items: center; 2 align-items: center;
5 align-self: center; 3 align-self: center;
6 justify-content: center; 4 justify-content: center;
7} 5}
8 6
9.invite__embed { 7.invite__embed { text-align: center; }
10 text-align: center; 8.invite__embed--button { width: 100%; }
11}
12
13.invite__embed--button {
14 width: 100%;
15} \ No newline at end of file
diff --git a/src/styles/layout.scss b/src/styles/layout.scss
index afdd7dec7..ebf468cf0 100644
--- a/src/styles/layout.scss
+++ b/src/styles/layout.scss
@@ -1,12 +1,36 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3html { 3html { overflow: hidden; }
4 overflow: hidden; 4
5.theme__dark .app {
6 .sidebar {
7 background: $dark-theme-gray-darker;
8 box-shadow: 0 0 5px 0 $dark-theme-black;
9 color: $theme-text-color;
10
11 .sidebar__add-service {
12 color: $dark-theme-gray-lighter;
13 background: $dark-theme-gray;
14 }
15
16 .sidebar__button {
17 color: $dark-theme-gray-lighter;
18 font-size: 22px;
19
20 &:hover,
21 &:active { color: $dark-theme-gray-smoke; }
22 &.is-muted { color: $dark-theme-gray; }
23 }
24 }
25
26 .app-loader .app-loader__title { color: $dark-theme-gray-lightest; }
5} 27}
6 28
7.app { 29.app {
8 display: flex; 30 display: flex;
9 flex-direction: row; 31 flex-direction: column;
32
33 .app__content { display: flex; }
10 34
11 .app__service { 35 .app__service {
12 display: flex; 36 display: flex;
@@ -15,130 +39,100 @@ html {
15 } 39 }
16} 40}
17 41
42.electron-app-title-bar { z-index: 99999999; }
43
18.window-draggable { 44.window-draggable {
19 position: absolute;
20 width: 100%;
21 top: 0px;
22 left: 0px;
23 height: 35px; 45 height: 35px;
46 left: 0;
24 pointer-events: none; 47 pointer-events: none;
25 -webkit-app-region: drag; 48 position: absolute;
49 top: 0;
50 width: 100%;
26 z-index: 9999; 51 z-index: 9999;
52 -webkit-app-region: drag;
27} 53}
28 54
29.darwin { 55.darwin .sidebar { padding-top: 23px; }
30 .sidebar {
31 padding-top: 23px;
32 }
33}
34 56
35.sidebar { 57.sidebar {
36 display: flex;
37 flex-direction: column;
38 align-items: center; 58 align-items: center;
39 width: $theme-sidebar-width;
40 background: $theme-gray-lightest; 59 background: $theme-gray-lightest;
41 box-shadow: 1px 0 10px rgba(0,0,0,0.08); 60 box-shadow: 1px 0 10px rgba(0, 0, 0, .08);
42 z-index: 200;
43 text-align: center;
44 color: $theme-text-color; 61 color: $theme-text-color;
62 display: flex;
63 flex-direction: column;
45 padding-bottom: 10px; 64 padding-bottom: 10px;
65 text-align: center;
66 width: $theme-sidebar-width;
67 z-index: 200;
46 68
47 .sidebar__add-service { 69 .sidebar__add-service {
48 width: 32px; 70 color: $theme-gray-light;
49 height: 32px;
50 background: $theme-gray-lighter; 71 background: $theme-gray-lighter;
51 border-radius: $theme-border-radius-small; 72 border-radius: $theme-border-radius-small;
73 height: 32px;
52 margin: 10px auto; 74 margin: 10px auto;
53 color: $theme-gray-light; 75 width: 32px;
54 } 76 }
55 77
56 .sidebar__button { 78 .sidebar__button {
57 width: $theme-sidebar-width; 79 color: $theme-gray-light;
58 padding: 7px 0;
59 font-size: 24px; 80 font-size: 24px;
81 padding: 7px 0;
60 position: relative; 82 position: relative;
61 color: $theme-gray-light; 83 width: $theme-sidebar-width;
62
63 &:hover {
64 color: darken($theme-gray-light, 10%);
65 }
66
67 &:active {
68 color: lighten($theme-gray-light, 10%);
69 }
70
71 &.is-muted {
72 color: $theme-brand-primary;
73 }
74 84
75 &--new-service { 85 &:hover,
76 padding-bottom: 6px; 86 &:active { color: lighten($theme-gray-light, 10%); }
77 } 87 &.is-muted { color: $theme-brand-primary; }
88 &--new-service { padding-bottom: 6px; }
78 } 89 }
79 90
80 & > div { 91 & > div {
81 display: flex; 92 display: flex;
82 overflow-y: scroll; 93 overflow-y: scroll;
83 94
84 &::-webkit-scrollbar { 95 &::-webkit-scrollbar { display: none; }
85 display: none;
86 }
87 } 96 }
88} 97}
89 98
90.grid { 99.grid .grid__row {
91 .grid__row { 100 display: flex;
92 display: flex; 101 flex-direction: row;
93 flex-direction: row;
94
95 &>* {
96 margin-right: 20px;
97 }
98 102
99 & :last-child { 103 & > * { margin-right: 20px; }
100 margin-right: 0; 104 & :last-child { margin-right: 0; }
101 }
102 }
103} 105}
104 106
105.app-loader { 107.app-loader {
108 align-items: center;
106 display: flex; 109 display: flex;
107 justify-content: center; 110 justify-content: center;
108 align-items: center;
109 111
110 .app-loader__title { 112 .app-loader__title {
111 color: #FFF; 113 color: #FFF;
112 font-size: 40px; 114 font-size: 40px;
113 } 115 }
114 116
115 &>span { 117 & > span { height: auto; }
116 height: auto;
117 }
118}
119
120.dev-warning {
121 display: none;
122} 118}
123 119
124.isDevMode { 120.dev-warning { display: none; }
125 .dev-warning { 121
126 display: block; 122.isDevMode .dev-warning {
127 position: fixed; 123 border-radius: 3px;
128 background: $theme-brand-warning; 124 background: $theme-brand-warning;
129 width: auto; 125 color: #FFF;
130 height: auto; 126 display: block;
131 top: 5px; 127 font-size: 10px;
132 right: 5px; 128 height: auto;
133 padding: 4px; 129 padding: 4px;
134 font-size: 10px; 130 position: fixed;
135 color: #FFF; 131 right: 5px;
136 z-index: 999999999; 132 top: 5px;
137 border-radius: 3px; 133 transition: opacity .5s ease;
138 transition: opacity 0.5s ease; 134 width: auto;
139 135 z-index: 999999999;
140 &:hover { 136
141 opacity: 0; 137 &:hover { opacity: 0; }
142 }
143 }
144} 138}
diff --git a/src/styles/main.scss b/src/styles/main.scss
index 446bdca14..784a04d3d 100644
--- a/src/styles/main.scss
+++ b/src/styles/main.scss
@@ -4,6 +4,7 @@ $mdi-font-path: '../node_modules/mdi/fonts';
4} 4}
5 5
6@import './node_modules/mdi/scss/materialdesignicons.scss'; 6@import './node_modules/mdi/scss/materialdesignicons.scss';
7@import './node_modules/electron-react-titlebar/assets/style';
7 8
8// modules 9// modules
9@import './reset.scss'; 10@import './reset.scss';
@@ -28,6 +29,7 @@ $mdi-font-path: '../node_modules/mdi/fonts';
28@import './subscription-popup.scss'; 29@import './subscription-popup.scss';
29@import './content-tabs.scss'; 30@import './content-tabs.scss';
30@import './invite.scss'; 31@import './invite.scss';
32@import './title-bar.scss';
31 33
32// form 34// form
33@import './input.scss'; 35@import './input.scss';
diff --git a/src/styles/mixins.scss b/src/styles/mixins.scss
index c9b1bc988..06efb475a 100644
--- a/src/styles/mixins.scss
+++ b/src/styles/mixins.scss
@@ -1,9 +1,9 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3@mixin formLabel { 3@mixin formLabel {
4 width: 100%;
5 color: $theme-gray-light; 4 color: $theme-gray-light;
6 display: block; 5 display: block;
7 margin-bottom: 5px; 6 margin-bottom: 5px;
8 order: 0; 7 order: 0;
8 width: 100%;
9} 9}
diff --git a/src/styles/radio.scss b/src/styles/radio.scss
index 644478cd6..87d401215 100644
--- a/src/styles/radio.scss
+++ b/src/styles/radio.scss
@@ -1,34 +1,39 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3.franz-form { 3.theme__dark .franz-form .franz-form__radio {
4 .franz-form__radio-wrapper { 4 border: 1px solid $dark-theme-gray-lighter;
5 display: flex; 5 color: $dark-theme-gray-lightest;
6
7 &.is-selected {
8 background: $dark-theme-gray-lighter;
9 border: 1px solid $dark-theme-gray-lighter;
10 color: $dark-theme-gray-smoke;
6 } 11 }
12}
13
14
15.franz-form {
16 .franz-form__radio-wrapper { display: flex; }
7 17
8 .franz-form__radio { 18 .franz-form__radio {
9 // background: $theme-gray-lightest;
10 border: 2px solid $theme-gray-lighter; 19 border: 2px solid $theme-gray-lighter;
20 border-radius: $theme-border-radius-small;
21 box-shadow: $theme-inset-shadow;
11 color: $theme-gray; 22 color: $theme-gray;
12 padding: 11px; 23 flex: 1;
13 margin-right: 20px; 24 margin-right: 20px;
25 padding: 11px;
14 text-align: center; 26 text-align: center;
15 border-radius: $theme-border-radius-small;
16 flex: 1;
17 box-shadow: $theme-inset-shadow;
18 transition: background $theme-transition-time; 27 transition: background $theme-transition-time;
19 28
20 &:last-of-type { 29 &:last-of-type { margin-right: 0; }
21 margin-right: 0;
22 }
23 30
24 &.is-selected { 31 &.is-selected {
25 border: 2px solid $theme-brand-primary;
26 background: #FFF; 32 background: #FFF;
33 border: 2px solid $theme-brand-primary;
27 color: $theme-brand-primary; 34 color: $theme-brand-primary;
28 } 35 }
29 36
30 input { 37 input { display: none; }
31 display: none;
32 }
33 } 38 }
34} 39}
diff --git a/src/styles/recipes.scss b/src/styles/recipes.scss
index 1b519a5e5..84222e1fe 100644
--- a/src/styles/recipes.scss
+++ b/src/styles/recipes.scss
@@ -1,16 +1,22 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3.theme__dark .recipe-teaser {
4 background-color: $dark-theme-gray-dark;
5
6 &:hover { background-color: $dark-theme-gray; }
7}
8
3.recipes { 9.recipes {
4 .recipes__list { 10 .recipes__list {
11 align-content: flex-start;
5 display: flex; 12 display: flex;
6 flex-flow: row wrap; 13 flex-flow: row wrap;
7 align-content: flex-start;
8 min-height: 70%;
9 height: auto; 14 height: auto;
15 min-height: 70%;
10 16
11 &.recipes__list--disabled { 17 &.recipes__list--disabled {
12 opacity: 0.3;
13 filter: grayscale(100%); 18 filter: grayscale(100%);
19 opacity: .3;
14 pointer-events: none; 20 pointer-events: none;
15 } 21 }
16 } 22 }
@@ -19,58 +25,48 @@
19 height: auto; 25 height: auto;
20 margin-bottom: 35px; 26 margin-bottom: 35px;
21 27
22 .badge { 28 .badge { margin-right: 10px; }
23 margin-right: 10px;
24 }
25 29
26 &.recipes__navigation--disabled { 30 &.recipes__navigation--disabled {
27 opacity: 0.3;
28 filter: grayscale(100%); 31 filter: grayscale(100%);
32 opacity: .3;
29 pointer-events: none; 33 pointer-events: none;
30 } 34 }
31 } 35 }
32 36
33 &__service-request { 37 &__service-request { float: right; }
34 float: right;
35 }
36} 38}
37 39
38.recipe-teaser { 40.recipe-teaser {
39 position: relative; 41 background-color: $theme-gray-lightest;
40 width: calc(25% - 20px); 42 border-radius: $theme-border-radius;
41 height: 120px; 43 height: 120px;
42 margin: 0 20px 20px 0; 44 margin: 0 20px 20px 0;
43 border-radius: $theme-border-radius;
44 background-color: $theme-gray-lightest;
45 transition: background $theme-transition-time;
46 overflow: hidden; 45 overflow: hidden;
46 position: relative;
47 transition: background $theme-transition-time;
48 width: calc(25% - 20px);
47 49
48 &:hover { 50 &:hover { background-color: $theme-gray-lighter; }
49 background-color: $theme-gray-lighter;
50 }
51 51
52 .recipe-teaser__icon { 52 .recipe-teaser__icon {
53 width: 50px;
54 margin-bottom: 10px; 53 margin-bottom: 10px;
54 width: 50px;
55 } 55 }
56 56
57 .recipe-teaser__label { 57 .recipe-teaser__label { display: block; }
58 display: block;
59 }
60 58
61 h2 { 59 h2 { z-index: 10; }
62 z-index: 10;
63 }
64 60
65 &__dev-badge { 61 &__dev-badge {
66 position: absolute;
67 top: 5px;
68 right: -13px;
69 width: 50px;
70 background: $theme-brand-warning; 62 background: $theme-brand-warning;
63 box-shadow: 0 0 4px rgba(black, .2);
71 color: #FFF; 64 color: #FFF;
72 font-size: 10px; 65 font-size: 10px;
66 position: absolute;
67 right: -13px;
68 top: 5px;
73 transform: rotateZ(45deg); 69 transform: rotateZ(45deg);
74 box-shadow: 0 0 4px rgba(black, 0.2); 70 width: 50px;
75 } 71 }
76} 72}
diff --git a/src/styles/reset.scss b/src/styles/reset.scss
index 21763f44f..80328dcef 100644
--- a/src/styles/reset.scss
+++ b/src/styles/reset.scss
@@ -16,63 +16,62 @@ article, aside, canvas, details, embed,
16figure, figcaption, footer, header, hgroup, 16figure, figcaption, footer, header, hgroup,
17menu, nav, output, ruby, section, summary, 17menu, nav, output, ruby, section, summary,
18time, mark, audio, video { 18time, mark, audio, video {
19 margin: 0;
20 padding: 0;
21 border: 0; 19 border: 0;
22 font-size: 100%;
23 font: inherit; 20 font: inherit;
21 font-size: 100%;
22 margin: 0;
23 padding: 0;
24} 24}
25/* HTML5 display-role reset for older browsers */ 25
26article, aside, details, figcaption, figure, 26article, aside, details, figcaption, figure,
27footer, header, hgroup, menu, nav, section { 27footer, header, hgroup, menu, nav, section { display: block; }
28 display: block; 28
29} 29ol,
30body { 30ul { list-style: none; }
31 line-height: 1; 31
32} 32blockquote,
33ol, ul { 33q {
34 list-style: none;
35}
36blockquote, q {
37 quotes: none; 34 quotes: none;
35
36 &::before,
37 &::after {
38 content: '';
39 content: none;
40 }
38} 41}
39blockquote:before, blockquote:after, q:before, q:after { 42
40 content: '';
41 content: none;
42}
43table { 43table {
44 border-collapse: collapse; 44 border-collapse: collapse;
45 border-spacing: 0; 45 border-spacing: 0;
46} 46}
47 47
48/* Buttons should not have any special style applied by default */
49button { 48button {
50 background: none; 49 background: none;
51 border: none; 50 border: none;
52 padding: 0; 51 padding: 0;
53}
54 52
55button:focus { 53 &:focus { outline: 0; }
56 outline: 0; 54 .theme__dark & { color: $dark-theme-gray-smoke; }
57} 55}
58 56
59html { 57html {
60 /* base for rem / 1rem = 10px */
61 font-size: 62.5%; 58 font-size: 62.5%;
62 font-family: 'Open Sans'; 59 font-family: 'Open Sans';
63} 60}
64 61
65body { 62body {
66 /* default font size = 14px */
67 font-size: 1.4rem;
68 color: $theme-gray-dark; 63 color: $theme-gray-dark;
64 font-size: 1.4rem;
65 line-height: 1;
66
67 .theme__dark { color: $dark-theme-gray-smoke; }
69} 68}
70 69
71* { 70* {
72 -webkit-font-smoothing: antialiased;
73 box-sizing: border-box; 71 box-sizing: border-box;
74 font-size: 1.4rem;
75 font-family: 'Open Sans'; 72 font-family: 'Open Sans';
73 font-size: 1.4rem;
74 -webkit-font-smoothing: antialiased;
76 -webkit-user-select: none; 75 -webkit-user-select: none;
77} 76}
78 77
@@ -82,14 +81,6 @@ html, body, div {
82 box-sizing: border-box; 81 box-sizing: border-box;
83} 82}
84 83
85*:focus { 84*:focus { outline: none; }
86 outline: none; 85img { pointer-events: none; }
87} 86a { cursor: default; }
88
89img {
90 pointer-events: none;
91}
92
93a {
94 cursor: default;
95}
diff --git a/src/styles/searchInput.scss b/src/styles/searchInput.scss
index 633a31e09..32b9da065 100644
--- a/src/styles/searchInput.scss
+++ b/src/styles/searchInput.scss
@@ -1,20 +1,32 @@
1@import './config.scss';
2@import './mixins.scss';
3
4.theme__dark .search-input {
5 @extend %headline__dark;
6 background: $dark-theme-gray-dark;
7 border: 1px solid $dark-theme-gray-light;
8 border-radius: $theme-border-radius;
9 color: $dark-theme-gray-lightest;
10
11 input { color: $dark-theme-gray-lightest; }
12}
13
1.search-input { 14.search-input {
2 width: 100%; 15 @extend %headline;
3 height: auto;
4 display: flex;
5 align-items: center; 16 align-items: center;
6 padding: 0 10px;
7 border-radius: 30px;
8 background: $theme-gray-lightest; 17 background: $theme-gray-lightest;
9 padding: 5px 10px; 18 border-radius: 30px;
10 @extend %headline;
11 color: $theme-gray-light; 19 color: $theme-gray-light;
20 display: flex;
21 height: auto;
22 padding: 5px 10px;
23 width: 100%;
12 24
13 input { 25 input {
14 padding-left: 10px;
15 background: none; 26 background: none;
16 border: 0; 27 border: 0;
17 flex: 1;
18 color: $theme-gray-light; 28 color: $theme-gray-light;
29 flex: 1;
30 padding-left: 10px;
19 } 31 }
20} 32}
diff --git a/src/styles/select.scss b/src/styles/select.scss
index 965b4321a..ed0fc0fc2 100644
--- a/src/styles/select.scss
+++ b/src/styles/select.scss
@@ -3,17 +3,21 @@
3 3
4$toggle: "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICBpZD0ic3ZnMiIKICAgdmlld0JveD0iMCAwIDM1Ljk3MDk4MyAyMy4wOTE1MTgiCiAgIGhlaWdodD0iNi41MTY5Mzk2bW0iCiAgIHdpZHRoPSIxMC4xNTE4MTFtbSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczQiIC8+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjAyLjAxNDUxLC00MDcuMTIyMjUpIgogICAgIGlkPSJsYXllcjEiPgogICAgPHRleHQKICAgICAgIGlkPSJ0ZXh0MzMzNiIKICAgICAgIHk9IjYyOS41MDUwNyIKICAgICAgIHg9IjI5MS40Mjg1NiIKICAgICAgIHN0eWxlPSJmb250LXN0eWxlOm5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zaXplOjQwcHg7bGluZS1oZWlnaHQ6MTI1JTtmb250LWZhbWlseTpzYW5zLXNlcmlmO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNjI5LjUwNTA3IgogICAgICAgICB4PSIyOTEuNDI4NTYiCiAgICAgICAgIGlkPSJ0c3BhbjMzMzgiPjwvdHNwYW4+PC90ZXh0PgogICAgPGcKICAgICAgIGlkPSJ0ZXh0MzM0MCIKICAgICAgIHN0eWxlPSJmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2ZvbnQtc2l6ZTo0MHB4O2xpbmUtaGVpZ2h0OjEyNSU7Zm9udC1mYW1pbHk6Rm9udEF3ZXNvbWU7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpGb250QXdlc29tZTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIj4KICAgICAgPHBhdGgKICAgICAgICAgaWQ9InBhdGgzMzQ1IgogICAgICAgICBzdHlsZT0iZmlsbDojMzMzMzMzO2ZpbGwtb3BhY2l0eToxIgogICAgICAgICBkPSJtIDIzNy41NjY5Niw0MTMuMjU1MDcgYyAwLjU1ODA0LC0wLjU1ODA0IDAuNTU4MDQsLTEuNDczMjIgMCwtMi4wMzEyNSBsIC0zLjcwNTM1LC0zLjY4MzA0IGMgLTAuNTU4MDQsLTAuNTU4MDQgLTEuNDUwOSwtMC41NTgwNCAtMi4wMDg5MywwIEwgMjIwLDQxOS4zOTM0NiAyMDguMTQ3MzIsNDA3LjU0MDc4IGMgLTAuNTU4MDMsLTAuNTU4MDQgLTEuNDUwODksLTAuNTU4MDQgLTIuMDA4OTMsMCBsIC0zLjcwNTM1LDMuNjgzMDQgYyAtMC41NTgwNCwwLjU1ODAzIC0wLjU1ODA0LDEuNDczMjEgMCwyLjAzMTI1IGwgMTYuNTYyNSwxNi41NDAxNyBjIDAuNTU4MDMsMC41NTgwNCAxLjQ1MDg5LDAuNTU4MDQgMi4wMDg5MiwwIGwgMTYuNTYyNSwtMTYuNTQwMTcgeiIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo="; 4$toggle: "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICBpZD0ic3ZnMiIKICAgdmlld0JveD0iMCAwIDM1Ljk3MDk4MyAyMy4wOTE1MTgiCiAgIGhlaWdodD0iNi41MTY5Mzk2bW0iCiAgIHdpZHRoPSIxMC4xNTE4MTFtbSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczQiIC8+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjAyLjAxNDUxLC00MDcuMTIyMjUpIgogICAgIGlkPSJsYXllcjEiPgogICAgPHRleHQKICAgICAgIGlkPSJ0ZXh0MzMzNiIKICAgICAgIHk9IjYyOS41MDUwNyIKICAgICAgIHg9IjI5MS40Mjg1NiIKICAgICAgIHN0eWxlPSJmb250LXN0eWxlOm5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zaXplOjQwcHg7bGluZS1oZWlnaHQ6MTI1JTtmb250LWZhbWlseTpzYW5zLXNlcmlmO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNjI5LjUwNTA3IgogICAgICAgICB4PSIyOTEuNDI4NTYiCiAgICAgICAgIGlkPSJ0c3BhbjMzMzgiPjwvdHNwYW4+PC90ZXh0PgogICAgPGcKICAgICAgIGlkPSJ0ZXh0MzM0MCIKICAgICAgIHN0eWxlPSJmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2ZvbnQtc2l6ZTo0MHB4O2xpbmUtaGVpZ2h0OjEyNSU7Zm9udC1mYW1pbHk6Rm9udEF3ZXNvbWU7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpGb250QXdlc29tZTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIj4KICAgICAgPHBhdGgKICAgICAgICAgaWQ9InBhdGgzMzQ1IgogICAgICAgICBzdHlsZT0iZmlsbDojMzMzMzMzO2ZpbGwtb3BhY2l0eToxIgogICAgICAgICBkPSJtIDIzNy41NjY5Niw0MTMuMjU1MDcgYyAwLjU1ODA0LC0wLjU1ODA0IDAuNTU4MDQsLTEuNDczMjIgMCwtMi4wMzEyNSBsIC0zLjcwNTM1LC0zLjY4MzA0IGMgLTAuNTU4MDQsLTAuNTU4MDQgLTEuNDUwOSwtMC41NTgwNCAtMi4wMDg5MywwIEwgMjIwLDQxOS4zOTM0NiAyMDguMTQ3MzIsNDA3LjU0MDc4IGMgLTAuNTU4MDMsLTAuNTU4MDQgLTEuNDUwODksLTAuNTU4MDQgLTIuMDA4OTMsMCBsIC0zLjcwNTM1LDMuNjgzMDQgYyAtMC41NTgwNCwwLjU1ODAzIC0wLjU1ODA0LDEuNDczMjEgMCwyLjAzMTI1IGwgMTYuNTYyNSwxNi41NDAxNyBjIDAuNTU4MDMsMC41NTgwNCAxLjQ1MDg5LDAuNTU4MDQgMi4wMDg5MiwwIGwgMTYuNTYyNSwtMTYuNTQwMTcgeiIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=";
5 5
6.franz-form { 6.theme__dark .franz-form .franz-form__select {
7 .franz-form__select { 7 background-color: $dark-theme-gray-dark;
8 -webkit-appearance: none; 8 border: 1px solid $dark-theme-gray-light;
9 min-width: 200px; 9 color: $dark-theme-gray-lightest;
10 padding: 10px; 10}
11 background-color: $theme-gray-lightest; 11
12 background-position: right center; 12.franz-form .franz-form__select {
13 background-repeat: no-repeat; 13 background-color: $theme-gray-lightest;
14 background-size: 1ex; 14 background-image: url(data:image/svg+xml;base64,#{$toggle});
15 background-origin: content-box; 15 background-origin: content-box;
16 background-image: url(data:image/svg+xml;base64,#{$toggle}); 16 background-position: right center;
17 border: 1px solid $theme-gray-lighter; 17 background-repeat: no-repeat;
18 } 18 background-size: 1ex;
19 border: 1px solid $theme-gray-lighter;
20 min-width: 200px;
21 padding: 10px;
22 -webkit-appearance: none;
19} 23}
diff --git a/src/styles/service-table.scss b/src/styles/service-table.scss
index 66d5ac941..f2090685b 100644
--- a/src/styles/service-table.scss
+++ b/src/styles/service-table.scss
@@ -1,62 +1,63 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3.theme__dark .service-table {
4 .service-table__icon.has-custom-icon { border: 1px solid $dark-theme-gray-dark; }
5 .service-table__column-info .mdi { color: $dark-theme-gray-lightest; }
6
7 .service-table__row {
8 border-bottom: 1px solid $dark-theme-gray-darker;
9
10 &:hover { background: $dark-theme-gray-darker; }
11 &.service-table__row--disabled { color: $dark-theme-gray; }
12 }
13}
14
3.service-table { 15.service-table {
4 width: 100%; 16 width: 100%;
5 17
6 .service-table__toggle { 18 .service-table__toggle {
7 width: 60px; 19 width: 60px;
8 20
9 .franz-form__field { 21 .franz-form__field { margin-bottom: 0; }
10 margin-bottom: 0;
11 }
12 } 22 }
13 23
14 .service-table__icon { 24 .service-table__icon {
15 width: 35px; 25 width: 35px;
16 26
17 &.has-custom-icon { 27 &.has-custom-icon {
18 border-radius: $theme-border-radius;
19 border: 1px solid $theme-gray-lighter; 28 border: 1px solid $theme-gray-lighter;
29 border-radius: $theme-border-radius;
20 width: 37px; 30 width: 37px;
21 } 31 }
22 } 32 }
23 33
24 .service-table__column-icon { 34 .service-table__column-icon,
25 width: 40px; 35 .service-table__column-action { width: 40px }
26 }
27
28 .service-table__column-action {
29 width: 40px
30 }
31 36
32 .service-table__column-info { 37 .service-table__column-info {
33 width: 40px; 38 width: 40px;
34 39
35 .mdi { 40 .mdi {
41 color: $theme-gray-light;
36 display: block; 42 display: block;
37 font-size: 18px; 43 font-size: 18px;
38 color: $theme-gray-light;
39 } 44 }
40 } 45 }
41 46
42 .service-table__row { 47 .service-table__row {
43 border-bottom: 1px solid $theme-gray-lightest; 48 border-bottom: 1px solid $theme-gray-lightest;
44 49
45 &:hover { 50 &:hover { background: $theme-gray-lightest; }
46 background: $theme-gray-lightest;
47 }
48 51
49 &.service-table__row--disabled { 52 &.service-table__row--disabled {
50 color: $theme-gray-light; 53 color: $theme-gray-light;
51 54
52 .service-table__column-icon { 55 .service-table__column-icon {
53 filter: grayscale(100%); 56 filter: grayscale(100%);
54 opacity: 0.5; 57 opacity: .5;
55 } 58 }
56 } 59 }
57 } 60 }
58 61
59 td { 62 td { padding: 10px; }
60 padding: 10px;
61 }
62} 63}
diff --git a/src/styles/services.scss b/src/styles/services.scss
index 9f6cfc772..0e559501c 100644
--- a/src/styles/services.scss
+++ b/src/styles/services.scss
@@ -1,30 +1,46 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3.theme__dark .services {
4 background: $dark-theme-gray-darkest;
5
6 .services__webview-wrapper { background: $dark-theme-gray-darkest; }
7
8 .services__webview,
9 .services__info-layer {
10 webview { background: $dark-theme-gray-darkest; }
11 }
12
13 .services__no-service,
14 .services__info-layer {
15 background: $dark-theme-gray-darkest;
16
17 h1 { color: $dark-theme-gray-lightest; }
18 }
19}
20
3.services { 21.services {
22 background: #FFF;
4 flex: 1; 23 flex: 1;
5 height: 100%; 24 height: 100%;
6 position: relative;
7 overflow: hidden;
8 background: #FFF;
9 order: 5; 25 order: 5;
26 overflow: hidden;
27 position: relative;
10 28
11 .services__webview-wrapper { 29 .services__webview-wrapper { background: $theme-gray-lighter; }
12 background: $theme-gray-lighter;
13 }
14 30
15 .services__webview, 31 .services__webview,
16 .services__info-layer { 32 .services__info-layer {
33 left: 0;
17 position: absolute; 34 position: absolute;
18 width: 100%;
19 top: 0; 35 top: 0;
20 left: 0; 36 width: 100%;
21 z-index: 0; 37 z-index: 0;
22 38
23 webview { 39 webview {
24 display: inline-flex;
25 width: 0px;
26 height: 0px;
27 background: $theme-gray-lighter; 40 background: $theme-gray-lighter;
41 display: inline-flex;
42 height: 0;
43 width: 0;
28 } 44 }
29 45
30 &.is-active { 46 &.is-active {
@@ -32,36 +48,30 @@
32 48
33 webview { 49 webview {
34 flex: 0 1; 50 flex: 0 1;
35 width: 100%;
36 height: 100%; 51 height: 100%;
52 width: 100%;
37 } 53 }
38 } 54 }
39 55
40 &--force-repaint { 56 &--force-repaint webview { z-index: 5; }
41 webview {
42 z-index: 5;
43 }
44 }
45 } 57 }
46 58
47 .services__no-service, 59 .services__no-service,
48 .services__info-layer { 60 .services__info-layer {
61 align-items: center;
62 background: $theme-gray-lighter;
49 display: flex; 63 display: flex;
50 flex-direction: column; 64 flex-direction: column;
51 justify-content: center; 65 justify-content: center;
52 align-items: center;
53 text-align: center; 66 text-align: center;
54 background: $theme-gray-lighter;
55 67
56 h1 { 68 h1 {
57 margin: 25px 0 40px;
58 color: $theme-gray-dark; 69 color: $theme-gray-dark;
70 margin: 25px 0 40px;
59 } 71 }
60 72
61 a.button, 73 a.button,
62 button { 74 button { margin: 40px 0 20px; }
63 margin: 40px 0 20px;
64 }
65 } 75 }
66 76
67 .services__info-layer { 77 .services__info-layer {
diff --git a/src/styles/settings.scss b/src/styles/settings.scss
index 2182c9b5f..5e7e35fd8 100644
--- a/src/styles/settings.scss
+++ b/src/styles/settings.scss
@@ -1,130 +1,215 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3%headline { 3%headline {
4 color: $theme-gray-light;
4 font-size: 20px; 5 font-size: 20px;
5 font-weight: 400; 6 font-weight: 400;
6 letter-spacing: -1px; 7 letter-spacing: -1px;
7 color: $theme-gray-light;
8 8
9 a { 9 a { color: $theme-gray-light; }
10 color: $theme-gray-light; 10}
11
12%headline__dark {
13 color: $dark-theme-gray-lightest;
14
15 a { color: $dark-theme-gray-lightest; }
16}
17
18.theme__dark {
19 .settings-wrapper { background: rgba($dark-theme-black, .8); }
20
21 .settings {
22 background: $dark-theme-gray-darkest;
23 box-shadow: 0 20px 50px rgba($dark-theme-black, .5);
24
25 .settings__header {
26 background: $dark-theme-gray-darker;
27
28 h1,
29 .settings__header-item { @extend %headline__dark; }
30
31 .separator { border-right: 1px solid $dark-theme-gray-dark; }
32 .mdi { color: $dark-theme-gray-lightest; }
33 }
34
35 .settings__body::-webkit-scrollbar-thumb { background: $dark-theme-gray; }
36
37 .settings__close {
38 background: $dark-theme-gray-darker;
39 border-left: none;
40 color: $dark-theme-gray-lightest;
41
42 &:hover { background: darken($dark-theme-gray-darker, 5%); }
43 }
44
45 &__settings-group h3 { color: $dark-theme-gray-lightest; }
46
47 .settings__message {
48 border-top: 1px solid $theme-gray-lighter;
49 color: $dark-theme-gray-lightest;
50
51 .mdi { color: $dark-theme-gray-lightest; }
52 }
53
54 .settings__help { color: $dark-theme-gray-lightest; }
55
56 .settings__controls {
57 background: $dark-theme-gray-darker;
58
59 .franz-form__button.franz-form__button--secondary { background: $theme-gray-light; }
60 }
61
62 .account {
63 .account__box { background: $dark-theme-gray-darker; }
64
65 .invoices {
66 td { border-bottom: 1px solid $dark-theme-gray-darker; }
67 .invoices__action button { color: $theme-brand-primary; }
68 }
69 }
70
71 .premium-info {
72 background: $dark-theme-gray-darker;
73 border: 2px solid $theme-brand-primary;
74 }
75 .legal { color: $theme-gray-light; }
76 }
77
78 .settings-navigation {
79 background: $dark-theme-gray-darker;
80 border-right: 1px solid $dark-theme-gray-dark;
81
82 .settings-navigation__link {
83 color: $dark-theme-gray-lightest;
84
85 .badge {
86 background: $dark-theme-gray-lighter;
87 color: $dark-theme-gray-smoke;
88 }
89
90 &:hover {
91 background: darken($dark-theme-gray-darker, 5%);
92
93 .badge {
94 background: $dark-theme-gray-lighter;
95 color: $dark-theme-gray-smoke;
96 }
97 }
98
99 &.is-active {
100 background: $dark-theme-gray;
101 color: $dark-theme-gray-smoke;
102
103 .badge {
104 background: $dark-theme-gray-lighter;
105 color: $dark-theme-gray-smoke;
106 }
107 }
108 }
109
110 .settings-navigation__action-badge { background: $theme-brand-danger; }
11 } 111 }
12} 112}
13 113
14.settings-wrapper { 114.settings-wrapper {
15 background: rgba(black, 0.5); 115 align-items: center;
16 position: absolute; 116 background: rgba(black, .5);
17 width: 100%; 117 display: flex;
18 height: 100%; 118 height: 100%;
19 top: 0;
20 left: 0; 119 left: 0;
21 z-index: 9998;
22 display: flex;
23 justify-content: center; 120 justify-content: center;
24 align-items: center;
25 padding: 25px; 121 padding: 25px;
122 position: absolute;
123 top: 0;
124 width: 100%;
125 z-index: 9998;
26 126
27 .settings-wrapper__action { 127 .settings-wrapper__action {
28 position: absolute;
29 width: 100%;
30 height: 100%; 128 height: 100%;
31 top: 0;
32 left: 0; 129 left: 0;
130 position: absolute;
131 top: 0;
132 width: 100%;
33 } 133 }
34} 134}
35 135
36.settings { 136.settings {
37 position: relative; 137 background: #FFF;
138 border-radius: $theme-border-radius;
139 box-shadow: 0 20px 50px rgba(black, .5);
38 display: flex; 140 display: flex;
39 height: 100%; 141 height: 100%;
40 width: 100%; 142 max-height: 720px;
41 max-width: 900px; 143 max-width: 900px;
42 min-height: 400px; 144 min-height: 400px;
43 max-height: 720px;
44 z-index: 9999;
45 background: #FFF;
46 border-radius: $theme-border-radius;
47 box-shadow: 0 20px 50px rgba(black, 0.5);
48 overflow: hidden; 145 overflow: hidden;
49 // margin-top: -10%; 146 position: relative;
147 width: 100%;
148 z-index: 9999;
50 149
51 .settings__main { 150 .settings__main {
52 flex: 1;
53 display: flex; 151 display: flex;
152 flex: 1;
54 flex-direction: column; 153 flex-direction: column;
55 height: auto; 154 height: auto;
56 } 155 }
57 156
58 .settings__header { 157 .settings__header {
59 display: flex;
60 align-items: center; 158 align-items: center;
61 width: calc(100% - 60px); 159 background: $theme-gray-lighter;
160 display: flex;
62 height: 50px; 161 height: 50px;
63 padding: 0 40px; 162 padding: 0 40px;
64 background: $theme-gray-lighter; 163 width: calc(100% - 60px);
65 164
66 h1 { 165 h1 {
67 @extend %headline; 166 @extend %headline;
68 margin: 0; 167 margin: 0;
69 } 168 }
70 169
71 .settings__header-item { 170 .settings__header-item { @extend %headline; }
72 @extend %headline;
73 }
74 171
75 .separator { 172 .separator {
173 border-right: 1px solid darken($theme-gray-lighter, 10%);
76 height: 100%; 174 height: 100%;
77 margin: 0 15px; 175 margin: 0 15px;
78 border-right: 1px solid darken($theme-gray-lighter, 10%);
79 transform: skew(15deg) rotate(2deg); 176 transform: skew(15deg) rotate(2deg);
80 } 177 }
81 178
82 .mdi { 179 .mdi { color: $theme-gray-light; }
83 color: $theme-gray-light;
84 }
85 } 180 }
86 181
87 .settings__body { 182 .settings__body {
88 flex: 1; 183 flex: 1;
89 padding: 25px 15px 15px 25px;
90 margin: 15px; 184 margin: 15px;
91 overflow-y: scroll; 185 overflow-y: scroll;
186 padding: 25px 15px 15px 25px;
92 187
93 &::-webkit-scrollbar { 188 &::-webkit-scrollbar { width: 8px; }
94 width: 8px;
95 }
96 189
97 /* Track */ 190 /* Track */
98 &::-webkit-scrollbar-track { 191 &::-webkit-scrollbar-track {
99 -webkit-border-radius: 10px;
100 border-radius: 10px;
101 background: none; 192 background: none;
193 border-radius: 10px;
194 -webkit-border-radius: 10px;
102 } 195 }
103 196
104 /* Handle */ 197 /* Handle */
105 &::-webkit-scrollbar-thumb { 198 &::-webkit-scrollbar-thumb {
106 -webkit-border-radius: 10px;
107 border-radius: 10px;
108 background: $theme-gray-lighter; 199 background: $theme-gray-lighter;
200 border-radius: 10px;
201 -webkit-border-radius: 10px;
109 } 202 }
110 203
111 &::-webkit-scrollbar-thumb:window-inactive { 204 &::-webkit-scrollbar-thumb:window-inactive { background: none; }
112 background: none; 205 .service-flex-grid { display: flex; }
113 } 206 .service-name { flex: 1px; }
114
115 .service-flex-grid {
116 display: flex;
117 }
118
119 .service-name {
120 flex: 1px;
121 }
122 207
123 .service-icon { 208 .service-icon {
124 width: 140px;
125 float: right; 209 float: right;
126 margin-top: 30px;
127 margin-left: 40px; 210 margin-left: 40px;
211 margin-top: 30px;
212 width: 140px;
128 213
129 label { 214 label {
130 font-weight: bold; 215 font-weight: bold;
@@ -134,51 +219,45 @@
134 } 219 }
135 220
136 .settings__close { 221 .settings__close {
137 position: absolute;
138 right: 0;
139 background: $theme-gray-lighter; 222 background: $theme-gray-lighter;
140 height: 50px;
141 padding: 0 20px;
142 font-size: 20px;
143 border-left: 1px solid darken($theme-gray-lighter, 5%); 223 border-left: 1px solid darken($theme-gray-lighter, 5%);
144 color: $theme-gray-light; 224 color: $theme-gray-light;
225 font-size: 20px;
226 height: 50px;
227 padding: 0 20px;
228 position: absolute;
229 right: 0;
145 transition: background $theme-transition-time; 230 transition: background $theme-transition-time;
146 231
147 &:hover { 232 &:hover { background: darken($theme-gray-lighter, 5%); }
148 background: darken($theme-gray-lighter, 5%);
149 }
150 } 233 }
151 234
152 .search-input { 235 .search-input { margin-bottom: 30px; }
153 margin-bottom: 30px;
154 }
155 236
156 &__options { 237 &__options {
157 margin-top: 20px;
158 flex: 1; 238 flex: 1;
239 margin-top: 20px;
159 } 240 }
160 241
161 &__settings-group { 242 &__settings-group {
162 margin-top: 10px; 243 margin-top: 10px;
163 244
164 h3 { 245 h3 {
246 color: $theme-gray-light;
165 font-weight: bold; 247 font-weight: bold;
248 letter-spacing: -.1px;
166 margin: 25px 0 15px; 249 margin: 25px 0 15px;
167 color: $theme-gray-light;
168 letter-spacing: -0.1px;
169 250
170 &:first-of-type { 251 &:first-of-type { margin-top: 0; }
171 margin-top: 0;
172 }
173 } 252 }
174 } 253 }
175 254
176 .settings__message { 255 .settings__message {
256 border-top: 1px solid $theme-gray-lighter;
257 color: $theme-gray-light;
177 display: flex; 258 display: flex;
178 margin-top: 40px; 259 margin-top: 40px;
179 padding-top: 15px; 260 padding-top: 15px;
180 border-top: 1px solid $theme-gray-lighter;
181 color: $theme-gray-light;
182 261
183 .mdi { 262 .mdi {
184 color: $theme-gray-light; 263 color: $theme-gray-light;
@@ -188,69 +267,53 @@
188 } 267 }
189 268
190 .settings__help { 269 .settings__help {
191 margin: -10px 0 20px 55px;;
192 font-size: 12px;
193 color: $theme-gray-light; 270 color: $theme-gray-light;
271 font-size: 12px;
272 margin: -10px 0 20px 55px;;
194 } 273 }
195 274
196 .settings__controls { 275 .settings__controls {
276 background: $theme-gray-lighter;
197 display: flex; 277 display: flex;
278 height: auto;
198 justify-content: space-between; 279 justify-content: space-between;
199 padding: 10px 20px; 280 padding: 10px 20px;
200 height: auto;
201 background: $theme-gray-lighter;
202 281
203 .franz-form__button { 282 .franz-form__button {
204 &[type='submit'] { 283 &[type='submit'] { margin-left: auto; }
205 margin-left: auto; 284 &.franz-form__button--secondary { background: $theme-gray-light; }
206 }
207
208 &.franz-form__button--secondary {
209 background: $theme-gray-light;
210 }
211 } 285 }
212 } 286 }
213 287
214 .settings__delete-button { 288 .settings__delete-button { right: 0; }
215 right: 0;
216 }
217 289
218 .settings__empty-state { 290 .settings__empty-state {
219 width: 100%; 291 align-items: center;
292 align-self: center;
220 height: auto; 293 height: auto;
221 min-height: 70%; 294 min-height: 70%;
222 text-align: center; 295 text-align: center;
223 align-self: center; 296 width: 100%;
224 // margin-top: -20px;
225 align-items: center;
226 297
227 a.button { 298 a.button { margin-top: 40px; }
228 margin-top: 40px;
229 }
230 } 299 }
231 300
232 .account { 301 .account {
233 height: auto; 302 height: auto;
234 // padding: 20px;
235 303
236 .account__box { 304 .account__box {
305 align-items: center;
237 background: $theme-gray-lightest; 306 background: $theme-gray-lightest;
238 border-radius: $theme-border-radius; 307 border-radius: $theme-border-radius;
239 padding: 20px;
240 margin-bottom: 40px; 308 margin-bottom: 40px;
241 align-items: center; 309 padding: 20px;
242
243 &.account__box--flex {
244 display: flex;
245 }
246 310
247 &.account__box--last { 311 &.account__box--flex { display: flex; }
248 margin-bottom: 0; 312 &.account__box--last { margin-bottom: 0; }
249 }
250 313
251 .auth__button { 314 .auth__button {
252 width: 100%;
253 margin-top: 10px; 315 margin-top: 10px;
316 width: 100%;
254 } 317 }
255 } 318 }
256 319
@@ -258,57 +321,40 @@
258 margin-right: 20px; 321 margin-right: 20px;
259 position: relative; 322 position: relative;
260 323
261 .emoji img { 324 .emoji img { width: 30px; }
262 width: 30px;
263 }
264 } 325 }
265 326
266 .account__avatar-premium { 327 .account__avatar-premium {
328 font-size: 26px;
267 position: absolute; 329 position: absolute;
268 top: 2px;
269 right: 2px; 330 right: 2px;
270 font-size: 26px; 331 top: 2px;
271 } 332 }
272 333
273 .account__info { 334 .account__info {
274 flex: 1; 335 flex: 1;
275 336
276 h2 { 337 h2 { margin-bottom: 5px; }
277 margin-bottom: 5px; 338 .badge { margin-top: 5px; }
278 }
279
280 .badge {
281 margin-top: 5px;
282 }
283 } 339 }
284 340
285 .account__subscription { 341 .account__subscription {
286 display: flex;
287 align-items: center; 342 align-items: center;
343 display: flex;
288 344
289 .badge { 345 .badge { margin-left: 10px; }
290 margin-left: 10px;
291 }
292 }
293
294 .account__subscription-button {
295 margin-left: auto;
296 }
297
298 .franz-form__button {
299 white-space: nowrap;
300 } 346 }
301 347
302 div { 348 .account__subscription-button { margin-left: auto; }
303 height: auto; 349 .franz-form__button { white-space: nowrap; }
304 } 350 div { height: auto; }
305 351
306 .invoices { 352 .invoices {
307 width: 100%; 353 width: 100%;
308 354
309 td { 355 td {
310 padding: 15px 0;
311 border-bottom: 1px solid $theme-gray-lighter; 356 border-bottom: 1px solid $theme-gray-lighter;
357 padding: 15px 0;
312 } 358 }
313 359
314 tr:last-of-type td { 360 tr:last-of-type td {
@@ -319,9 +365,7 @@
319 .invoices__action { 365 .invoices__action {
320 text-align: right; 366 text-align: right;
321 367
322 button { 368 button { color: $theme-brand-primary; }
323 color: $theme-brand-primary;
324 }
325 } 369 }
326 } 370 }
327 } 371 }
@@ -331,15 +375,13 @@
331 font-size: 40px; 375 font-size: 40px;
332 margin-bottom: 20px; 376 margin-bottom: 20px;
333 377
334 img { 378 img { width: 40px; }
335 width: 40px;
336 }
337 } 379 }
338 380
339 .premium-info { 381 .premium-info {
340 background: lighten($theme-brand-primary, 40%); 382 background: lighten($theme-brand-primary, 40%);
341 padding: 20px;
342 border-radius: $theme-border-radius; 383 border-radius: $theme-border-radius;
384 padding: 20px;
343 } 385 }
344 386
345 .content-tabs .premium-info { 387 .content-tabs .premium-info {
@@ -348,35 +390,33 @@
348 } 390 }
349 391
350 .legal { 392 .legal {
351 text-align: center;
352 margin-top: 20px;
353 color: $theme-gray-light; 393 color: $theme-gray-light;
394 margin-top: 20px;
395 text-align: center;
354 } 396 }
355} 397}
356 398
357.settings-navigation { 399.settings-navigation {
358 width: 200px;
359 height: auto;
360 background: $theme-gray-lightest;
361 display: flex; 400 display: flex;
401 background: $theme-gray-lightest;
362 flex-direction: column; 402 flex-direction: column;
403 height: auto;
404 width: 200px;
363 405
364 .settings-navigation__link { 406 .settings-navigation__link {
365 display: flex;
366 align-items: center; 407 align-items: center;
367 height: 50px;
368 flex-shrink: 0;
369 text-decoration: none;
370 color: $theme-text-color; 408 color: $theme-text-color;
409 display: flex;
410 flex-shrink: 0;
411 height: 50px;
371 padding: 0 20px; 412 padding: 0 20px;
413 text-decoration: none;
372 transition: background $theme-transition-time, color $theme-transition-time; 414 transition: background $theme-transition-time, color $theme-transition-time;
373 415
374 &:hover { 416 &:hover {
375 background: darken($theme-gray-lightest, 5%); 417 background: darken($theme-gray-lightest, 5%);
376 418
377 .badge { 419 .badge { background: #FFF; }
378 background: #FFF;
379 }
380 } 420 }
381 421
382 &.is-active { 422 &.is-active {
@@ -390,22 +430,20 @@
390 } 430 }
391 } 431 }
392 432
393 .settings-navigation__expander { 433 .settings-navigation__expander { flex: 1; }
394 flex: 1;
395 }
396 434
397 .badge { 435 .badge {
398 transition: background $theme-transition-time, color $theme-transition-time;
399 display: initial; 436 display: initial;
400 margin-left: 5px; 437 margin-left: 5px;
438 transition: background $theme-transition-time, color $theme-transition-time;
401 } 439 }
402 440
403 .settings-navigation__action-badge { 441 .settings-navigation__action-badge {
404 display: inline-block;
405 width: 7px;
406 height: 7px;
407 background: $theme-brand-danger; 442 background: $theme-brand-danger;
408 border-radius: 100%; 443 border-radius: 100%;
444 display: inline-block;
445 height: 7px;
409 margin-left: 5px; 446 margin-left: 5px;
447 width: 7px;
410 } 448 }
411} 449}
diff --git a/src/styles/status-bar-target-url.scss b/src/styles/status-bar-target-url.scss
index bc7438be9..36f69df28 100644
--- a/src/styles/status-bar-target-url.scss
+++ b/src/styles/status-bar-target-url.scss
@@ -1,14 +1,14 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3.status-bar-target-url { 3.status-bar-target-url {
4 height: auto;
5 background: $theme-gray-lighter; 4 background: $theme-gray-lighter;
6 padding: 4px; 5 border-top-left-radius: 5px;
7 position: absolute;
8 box-shadow: 0 0 8px rgba(black, 0.2);
9 font-size: 12px;
10 color: $theme-gray-dark;
11 bottom: 0; 6 bottom: 0;
7 box-shadow: 0 0 8px rgba(black, .2);
8 color: $theme-gray-dark;
9 font-size: 12px;
10 height: auto;
12 right: 0; 11 right: 0;
13 border-top-left-radius: 5px; 12 padding: 4px;
13 position: absolute;
14} 14}
diff --git a/src/styles/subscription-popup.scss b/src/styles/subscription-popup.scss
index b6f232fcb..fb4795d6c 100644
--- a/src/styles/subscription-popup.scss
+++ b/src/styles/subscription-popup.scss
@@ -1,20 +1,15 @@
1.subscription-popup { 1.subscription-popup {
2 height: 100%; 2 height: 100%;
3 3
4 &__content { 4 &__content { height: calc(100% - 60px); }
5 height: calc(100% - 60px); 5 &__webview { height: 100%; }
6 }
7
8 &__webview {
9 height: 100%;
10 }
11 6
12 &__toolbar { 7 &__toolbar {
13 height: 60px;
14 background: $theme-gray-lightest; 8 background: $theme-gray-lightest;
9 border-top: 1px solid $theme-gray-lighter;
15 display: flex; 10 display: flex;
11 height: 60px;
16 justify-content: space-between; 12 justify-content: space-between;
17 padding: 10px; 13 padding: 10px;
18 border-top: 1px solid $theme-gray-lighter;
19 } 14 }
20} 15}
diff --git a/src/styles/subscription.scss b/src/styles/subscription.scss
index 8bfb68d23..70fb41cde 100644
--- a/src/styles/subscription.scss
+++ b/src/styles/subscription.scss
@@ -3,63 +3,47 @@
3 margin: 10px 0; 3 margin: 10px 0;
4 4
5 li { 5 li {
6 height: 30px;
7 align-items: center; 6 align-items: center;
8 display: flex; 7 display: flex;
8 height: 30px;
9 9
10 &:before { 10 &:before {
11 content: "ðŸ‘"; 11 content: "ðŸ‘";
12 margin-right: 10px; 12 margin-right: 10px;
13 } 13 }
14 14
15 .badge { 15 .badge { margin-left: 10px; }
16 margin-left: 10px;
17 }
18 } 16 }
19 } 17 }
20 18
21 .subscription__premium-info { 19 .subscription__premium-info { margin: 15px 0 25px; }
22 margin: 15px 0 25px;
23 }
24} 20}
25 21
26.paymentTiers { 22.paymentTiers .franz-form__radio-wrapper {
27 .franz-form__radio-wrapper { 23 flex-flow: wrap;
28 flex-flow: wrap;
29 24
30 .franz-form__radio { 25 .franz-form__radio {
31 width: 32%; 26 flex: initial;
32 flex: initial; 27 margin-right: 2%;
33 margin-right: 2%; 28 width: 32%;
34 29
35 &:nth-child(3) { 30 &:nth-child(3) { margin-right: 0; }
36 margin-right: 0;
37 }
38 31
39 &:nth-child(4) { 32 &:nth-child(4) {
40 margin-right: 0; 33 margin-right: 0;
41 margin-top: 2%; 34 margin-top: 2%;
42 width: 100%; 35 width: 100%;
43 }
44 } 36 }
45 } 37 }
46} 38}
47 39
48.settings { 40.settings .paymentTiers .franz-form__radio-wrapper .franz-form__radio {
49 .paymentTiers { 41 width: 49%;
50 .franz-form__radio-wrapper {
51 .franz-form__radio {
52 width: 49%;
53 42
54 &:nth-child(2) { 43 &:nth-child(2) { margin-right: 0; }
55 margin-right: 0;
56 }
57 44
58 &:nth-child(3) { 45 &:nth-child(3) {
59 margin-top: 2%; 46 margin-top: 2%;
60 width: 100%; 47 width: 100%;
61 }
62 }
63 }
64 } 48 }
65} 49}
diff --git a/src/styles/tabs.scss b/src/styles/tabs.scss
index ac48aabd6..cbd833f04 100644
--- a/src/styles/tabs.scss
+++ b/src/styles/tabs.scss
@@ -1,104 +1,88 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3.theme__dark .tab-item {
4 &.is-active {
5 background: $dark-theme-gray;
6
7 .tab-item__icon { margin-left: -4px; }
8 }
9
10 &.is-disabled .tab-item__icon { filter: grayscale(100%) opacity(.2); }
11 .tab-item__icon { width: 34px; }
12}
13
3.tabs { 14.tabs {
4 display: flex; 15 display: flex;
5 // flex: 1;
6 flex-direction: column; 16 flex-direction: column;
7 flex-shrink: 1; 17 flex-shrink: 1;
8 // align-items: center;
9 // height: auto;
10 18
11 .placeholder { 19 .placeholder {
12 width: 100%;
13 height: 40px; 20 height: 40px;
21 width: 100%;
14 } 22 }
15} 23}
16 24
17.tab-item { 25.tab-item {
18 display: flex;
19 justify-content: center;
20 align-items: center; 26 align-items: center;
21 position: relative; 27 display: flex;
22 width: $theme-sidebar-width;
23 height: 65px; 28 height: 65px;
29 justify-content: center;
24 min-height: 50px; 30 min-height: 50px;
31 position: relative;
25 transition: background $theme-transition-time; 32 transition: background $theme-transition-time;
33 width: $theme-sidebar-width;
26 34
27 &.is-active { 35 &.is-active {
28 border-left: 4px solid $theme-brand-primary;
29 background: lighten($theme-brand-primary, 35%); 36 background: lighten($theme-brand-primary, 35%);
37 border-left: 4px solid $theme-brand-primary;
30 38
31 .tab-item__icon { 39 .tab-item__icon { margin-left: -4px; }
32 margin-left: -4px;
33 }
34 }
35
36 &.is-disabled {
37 .tab-item__icon {
38 filter: grayscale(100%) opacity(0.2);
39 }
40 }
41
42 &.has-custom-icon {
43 .tab-item__icon {
44 border-radius: $theme-border-radius;
45 // border: 1px solid $theme-gray-lighter;
46 // width: 32px;
47 }
48 } 40 }
49 41
50 &:active { 42 &.is-disabled .tab-item__icon { filter: grayscale(100%) opacity(0.2); }
51 .tab-item__icon { 43 &.has-custom-icon .tab-item__icon { border-radius: $theme-border-radius; }
52 opacity: 0.7; 44 &:active .tab-item__icon { opacity: .7; }
53 }
54 }
55 45
56 .tab-item__icon { 46 .tab-item__icon {
57 width: 30px;
58 height: auto; 47 height: auto;
48 width: 30px;
59 } 49 }
60 50
61 .tab-item__message-count { 51 .tab-item__message-count {
62 min-width: 17px; 52 align-items: center;
63 min-height: 17px;
64 background: $theme-brand-danger; 53 background: $theme-brand-danger;
65 color: #FFF;
66 border-radius: 20px; 54 border-radius: 20px;
67 padding: 0px 5px;
68 font-size: 11px;
69 position: absolute;
70 right: 8px;
71 bottom: 8px; 55 bottom: 8px;
56 color: #FFF;
72 display: flex; 57 display: flex;
58 font-size: 11px;
73 justify-content: center; 59 justify-content: center;
74 align-items: center; 60 min-height: 17px;
61 min-width: 17px;
62 padding: 0px 5px;
63 position: absolute;
64 right: 8px;
75 65
76 &.is-indirect { 66 &.is-indirect { padding-top: 0; }
77 padding-top: 0px;
78 }
79 } 67 }
80 68
81 .tab-item__info-badge { 69 .tab-item__info-badge {
82 width: 17px; 70 align-items: center;
83 height: 17px;
84 background: $theme-gray-light; 71 background: $theme-gray-light;
85 color: $theme-gray-lighter; 72 bottom: 8px;
86 border-radius: 20px; 73 border-radius: 20px;
87 padding: 0px 5px; 74 color: $theme-gray-lighter;
75 display: flex;
88 font-size: 11px; 76 font-size: 11px;
77 height: 17px;
78 justify-content: center;
79 padding: 0px 5px;
89 position: absolute; 80 position: absolute;
90 right: 8px; 81 right: 8px;
91 bottom: 8px; 82 width: 17px;
92 display: flex;
93 justify-content: center;
94 align-items: center;
95 83
96 &.is-indirect { 84 &.is-indirect { padding-top: 0; }
97 padding-top: 0px;
98 }
99 } 85 }
100 86
101 &.is-reordering { 87 &.is-reordering { z-index: 99999; }
102 z-index: 99999;
103 }
104} 88}
diff --git a/src/styles/title-bar.scss b/src/styles/title-bar.scss
new file mode 100644
index 000000000..885eb94c4
--- /dev/null
+++ b/src/styles/title-bar.scss
@@ -0,0 +1,80 @@
1@import './config.scss';
2
3.theme__dark #electron-app-title-bar {
4 background: $dark-theme-gray-darker;
5
6 .toolbar-dropdown {
7 &.open > .toolbar-button > button {
8 background: $dark-theme-gray-light;
9 color: $dark-theme-gray-lightest;
10 }
11
12 &:not(.open) {
13 .menu-item .menu-label { opacity: 1; }
14 > .toolbar-button > button:hover {
15 background: $dark-theme-gray-darkest;
16 }
17 }
18 }
19
20 #app-menu-bar #foldout-container .foldout {
21 color: $dark-theme-gray-lightest;
22
23 .menu-pane {
24 background: $dark-theme-gray-light;
25
26 .menu-item {
27 .accelerator {
28 color: lighten($dark-theme-gray-light, 20%);
29 }
30 }
31
32 hr {
33 border-color: $dark-theme-gray-lighter;
34 }
35 }
36 }
37
38 .list .ReactVirtualized__Grid {
39 background: $dark-theme-gray-light;
40 }
41}
42
43#electron-app-title-bar {
44 background: $theme-gray-lightest;
45 border-bottom: 0;
46 box-shadow: 0 0 8px rgba(black, .1);
47
48 span { line-height: normal; }
49
50 div { height: auto; }
51
52 .toolbar-dropdown {
53 &.open { box-shadow: 0 0 8px rgba(black, 0.1); }
54
55 &:not(.open) {
56 .menu-item .menu-label { opacity: 1; }
57 > .toolbar-button > button:hover { background: $theme-brand-primary; }
58 }
59 }
60
61 .list-item {
62 .menu-item {
63 border-radius: $theme-border-radius-small;
64 margin: 4px;
65 }
66
67 &.selected,
68 &.selected:focus {
69 background: none;
70
71 .menu-item { background: $theme-brand-primary; }
72 }
73 }
74
75 .menu-pane {
76 border-bottom-left-radius: $theme-border-radius-small;
77 border-bottom-right-radius: $theme-border-radius-small;
78 box-shadow: 0 0 10px rgba(black, .5);
79 }
80}
diff --git a/src/styles/toggle.scss b/src/styles/toggle.scss
index 5b47e6495..0ce0c3379 100644
--- a/src/styles/toggle.scss
+++ b/src/styles/toggle.scss
@@ -4,44 +4,46 @@ $toggle-size: 14px;
4$toggle-width: 40px; 4$toggle-width: 40px;
5$toggle-button-size: 22px; 5$toggle-button-size: 22px;
6 6
7.franz-form { 7.theme__dark .franz-form .franz-form__toggle-wrapper .franz-form__toggle {
8 .franz-form__toggle-wrapper { 8 background: $dark-theme-gray;
9 display: flex; 9 border-radius: $toggle-size / 2;
10 flex-direction: row;
11 10
12 .franz-form__label { 11 .franz-form__toggle-button {
13 margin-left: 20px; 12 background: $dark-theme-gray-lighter;
13 box-shadow: 0 1px 4px rgba($dark-theme-black, .3);
14 }
15}
16
17.franz-form .franz-form__toggle-wrapper {
18 display: flex;
19 flex-direction: row;
20
21 .franz-form__label { margin-left: 20px; }
22
23 .franz-form__toggle {
24 background: $theme-gray-lighter;
25 border-radius: $theme-border-radius;
26 height: $toggle-size;
27 position: relative;
28 width: $toggle-width;
29
30 .franz-form__toggle-button {
31 background: $theme-gray-light;
32 border-radius: 100%;
33 box-shadow: 0 1px 4px rgba(0, 0, 0, .3);
34 height: $toggle-size - 2;
35 left: 1px;
36 top: 1px;
37 position: absolute;
38 transition: all .5s;
39 width: $toggle-size - 2;
14 } 40 }
15 41
16 .franz-form__toggle { 42 &.is-active .franz-form__toggle-button {
17 width: $toggle-width; 43 background: $theme-brand-primary;
18 height: $toggle-size; 44 left: $toggle-width - $toggle-size - 3;;
19 position: relative;
20 background: $theme-gray-lighter;
21 border-radius: $theme-border-radius;
22
23 .franz-form__toggle-button {
24 position: absolute;
25 left: 0;
26 top: -($toggle-button-size - $toggle-size) / 2;
27 width: $toggle-button-size;
28 height: $toggle-button-size;
29 background: $theme-gray-light;
30 border-radius: 100%;
31 transition: all 0.5s;
32 box-shadow: 0 1px 4px rgba(0,0,0,0.3);
33 }
34
35 &.is-active {
36 .franz-form__toggle-button {
37 left: $toggle-width - $toggle-button-size;
38 background: $theme-brand-primary;
39 }
40 }
41
42 input {
43 display: none;
44 }
45 } 45 }
46
47 input { display: none; }
46 } 48 }
47} 49}
diff --git a/src/styles/tooltip.scss b/src/styles/tooltip.scss
index 1194e7fbb..5700e994c 100644
--- a/src/styles/tooltip.scss
+++ b/src/styles/tooltip.scss
@@ -1,4 +1,4 @@
1.__react_component_tooltip { 1.__react_component_tooltip {
2 padding: 10px !important;
3 height: auto; 2 height: auto;
3 padding: 10px !important;
4} 4}
diff --git a/src/styles/type.scss b/src/styles/type.scss
index cacbec482..135d32da0 100644
--- a/src/styles/type.scss
+++ b/src/styles/type.scss
@@ -1,6 +1,12 @@
1@import './config.scss'; 1@import './config.scss';
2@import './mixins.scss'; 2@import './mixins.scss';
3 3
4.theme__dark {
5 a { color: $dark-theme-gray-smoke; }
6 .label { color: $dark-theme-gray-lightest; }
7 .footnote { color: $dark-theme-gray-lightest; }
8}
9
4h1 { 10h1 {
5 font-size: 30px; 11 font-size: 30px;
6 font-weight: 300; 12 font-weight: 300;
@@ -15,38 +21,32 @@ h2 {
15 margin-bottom: 25px; 21 margin-bottom: 25px;
16 margin-top: 55px; 22 margin-top: 55px;
17 23
18 &:first-of-type { 24 &:first-of-type { margin-top: 0; }
19 margin-top: 0;
20 }
21} 25}
22 26
23p { 27p {
24 margin-bottom: 10px; 28 margin-bottom: 10px;
25 line-height: 1.7rem; 29 line-height: 1.7rem;
26 30
27 &:last-of-type { 31 &:last-of-type { margin-bottom: 0; }
28 margin-bottom: 0;
29 }
30} 32}
31 33
32strong { 34strong { font-weight: bold; }
33 font-weight: bold;
34}
35 35
36a { 36a {
37 text-decoration: none;
38 color: $theme-text-color; 37 color: $theme-text-color;
38 text-decoration: none;
39 39
40 &.button { 40 &.button {
41 position: relative;
42 background: none; 41 background: none;
43 display: inline-block;
44 padding: 10px 20px;
45 border: 2px solid $theme-brand-primary; 42 border: 2px solid $theme-brand-primary;
46 color: $theme-brand-primary;
47 border-radius: 3px; 43 border-radius: 3px;
48 transition: background 0.5s, color 0.5s; 44 color: $theme-brand-primary;
45 display: inline-block;
46 padding: 10px 20px;
47 position: relative;
49 text-align: center; 48 text-align: center;
49 transition: background .5s, color .5s;
50 50
51 &:hover { 51 &:hover {
52 background: darken($theme-brand-primary, 5%); 52 background: darken($theme-brand-primary, 5%);
@@ -54,25 +54,19 @@ a {
54 } 54 }
55 } 55 }
56 56
57 &.link { 57 &.link { color: $theme-brand-primary; }
58 color: $theme-brand-primary;
59 }
60} 58}
61 59
62.error-message, .error-message:last-of-type { 60.error-message, .error-message:last-of-type {
63 margin: 10px 0;
64 color: $theme-brand-danger; 61 color: $theme-brand-danger;
62 margin: 10px 0;
65} 63}
66 64
67.center { 65.center { text-align: center; }
68 text-align: center;
69}
70 66
71.label { 67.label { @include formLabel(); }
72 @include formLabel();
73}
74 68
75.footnote { 69.footnote {
76 font-size: 12px;
77 color: $theme-gray-light; 70 color: $theme-gray-light;
78} \ No newline at end of file 71 font-size: 12px;
72}
diff --git a/src/styles/util.scss b/src/styles/util.scss
index 3faad8db3..cc93f79ab 100644
--- a/src/styles/util.scss
+++ b/src/styles/util.scss
@@ -1,16 +1,16 @@
1.scroll-container { 1.scroll-container {
2 height: 100%;
3 flex: 1; 2 flex: 1;
4 overflow-y: scroll; 3 height: 100%;
5 overflow-x: hidden; 4 overflow-x: hidden;
5 overflow-y: scroll;
6} 6}
7 7
8.loader { 8.loader {
9 position: relative;
10 z-index: 9999;
11 display: block; 9 display: block;
12 width: 100%;
13 height: 40px; 10 height: 40px;
11 position: relative;
12 width: 100%;
13 z-index: 9999;
14} 14}
15 15
16.align-middle { 16.align-middle {
diff --git a/src/styles/welcome.scss b/src/styles/welcome.scss
index a12069ba4..b3d6515b1 100644
--- a/src/styles/welcome.scss
+++ b/src/styles/welcome.scss
@@ -1,92 +1,84 @@
1.auth { 1.auth .welcome {
2 .welcome { 2 &__content {
3 3 align-items: center;
4 &__content { 4 color: #FFF;
5 display: flex; 5 display: flex;
6 align-items: center; 6 justify-content: center;
7 justify-content: center; 7 }
8 color: #FFF;
9 }
10
11 &__logo {
12 width: 100px;
13 }
14 8
15 &__text { 9 &__logo { width: 100px; }
16 margin-left: 40px;
17 padding-left: 40px;
18 border-left: 1px solid #FFF;
19 10
20 h1 { 11 &__text {
21 font-size: 60px; 12 border-left: 1px solid #FFF;
22 letter-spacing: -0.4rem; 13 margin-left: 40px;
23 margin-bottom: 5px; 14 padding-left: 40px;
24 }
25 15
26 h2 { 16 h1 {
27 margin-left: 2px; 17 font-size: 60px;
28 margin-bottom: 0; 18 letter-spacing: -.4rem;
29 } 19 margin-bottom: 5px;
30 } 20 }
31 21
32 &__services { 22 h2 {
33 width: 100%; 23 margin-bottom: 0;
34 max-width: 800px; 24 margin-left: 2px;
35 height: 100%;
36 max-height: 600px;
37 margin-left: -450px;
38 } 25 }
26 }
39 27
40 &__buttons { 28 &__services {
41 display: block; 29 height: 100%;
42 margin-top: 100px; 30 margin-left: -450px;
43 text-align: center; 31 max-height: 600px;
32 max-width: 800px;
33 width: 100%;
34 }
44 35
45 .button:first-of-type { 36 &__buttons {
46 margin-right: 25px; 37 display: block;
47 } 38 margin-top: 100px;
48 } 39 text-align: center;
49 40
50 .button { 41 .button:first-of-type { margin-right: 25px; }
51 border-color: #FFF; 42 }
52 color: #FFF;
53 43
54 &:hover { 44 .button {
55 background: #FFF; 45 border-color: #FFF;
56 color: $theme-brand-primary; 46 color: #FFF;
57 }
58 47
59 &__inverted { 48 &:hover {
60 background: #FFF; 49 background: #FFF;
61 color: $theme-brand-primary; 50 color: $theme-brand-primary;
62 }
63 &__inverted:hover {
64 background: none;
65 color: #FFF;
66 }
67 } 51 }
68 52
69 &__featured-services { 53 &__inverted {
70 text-align: center;
71 width: 480px;
72 margin: 80px auto 0 auto;
73 display: flex;
74 align-items: center;
75 flex-wrap: wrap;
76 background: #FFF; 54 background: #FFF;
77 border-radius: 6px; 55 color: $theme-brand-primary;
78 padding: 20px 20px 5px;
79 } 56 }
80 57
81 &__featured-service { 58 &__inverted:hover {
82 width: 35px; 59 background: none;
83 height: 35px; 60 color: #FFF;
84 margin: 0 10px 15px;
85 transition: 0.5s filter, 0.5s opacity;
86
87 img {
88 width: 35px;
89 }
90 } 61 }
91 } 62 }
63
64 &__featured-services {
65 align-items: center;
66 background: #FFF;
67 border-radius: 6px;
68 display: flex;
69 flex-wrap: wrap;
70 margin: 80px auto 0 auto;
71 padding: 20px 20px 5px;
72 text-align: center;
73 width: 480px;
74 }
75
76 &__featured-service {
77 margin: 0 10px 15px;
78 height: 35px;
79 transition: .5s filter, .5s opacity;
80 width: 35px;
81
82 img { width: 35px; }
83 }
92} 84}
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 d9e021e6d..e6fdc4efd 100644
--- a/src/webview/plugin.js
+++ b/src/webview/plugin.js
@@ -6,8 +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
12const debug = require('debug')('Franz:Plugin');
13
14window.franzSettings = {};
15let serviceData;
16
11ipcRenderer.on('initializeRecipe', (e, data) => { 17ipcRenderer.on('initializeRecipe', (e, data) => {
12 const modulePath = path.join(data.recipe.path, 'webview.js'); 18 const modulePath = path.join(data.recipe.path, 'webview.js');
13 // Delete module from cache 19 // Delete module from cache
@@ -15,8 +21,16 @@ ipcRenderer.on('initializeRecipe', (e, data) => {
15 try { 21 try {
16 // eslint-disable-next-line 22 // eslint-disable-next-line
17 require(modulePath)(new RecipeWebview(), data); 23 require(modulePath)(new RecipeWebview(), data);
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 }
18 } catch (err) { 32 } catch (err) {
19 console.error(err); 33 debug('Recipe initialization failed', err);
20 } 34 }
21}); 35});
22 36
@@ -30,11 +44,27 @@ new ContextMenuListener((info) => { // eslint-disable-line
30}); 44});
31 45
32ipcRenderer.on('settings-update', (e, data) => { 46ipcRenderer.on('settings-update', (e, data) => {
33 console.log('settings-update', data); 47 debug('Settings update received', data);
48
34 spellchecker.toggleSpellchecker(data.enableSpellchecking); 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 }
35}); 65});
36 66
37// initSpellche 67// initSpellchecker
38 68
39document.addEventListener('DOMContentLoaded', () => { 69document.addEventListener('DOMContentLoaded', () => {
40 ipcRenderer.sendToHost('hello'); 70 ipcRenderer.sendToHost('hello');
@@ -44,7 +74,7 @@ document.addEventListener('DOMContentLoaded', () => {
44const originalWindowOpen = window.open; 74const originalWindowOpen = window.open;
45 75
46window.open = (url, frameName, features) => { 76window.open = (url, frameName, features) => {
47 // We need to differentiate if the link should be opened in a popup or in the systems default browser 77 // We need to differentiate if the link should be opened in a popup or in the systems default browser
48 if (!frameName && !features) { 78 if (!frameName && !features) {
49 return ipcRenderer.sendToHost('new-window', url); 79 return ipcRenderer.sendToHost('new-window', url);
50 } 80 }