aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.eslintrc46
-rw-r--r--docs/example-feature/index.js34
-rw-r--r--packages/theme/src/themes/dark/index.ts7
-rw-r--r--packages/theme/src/themes/default/index.ts7
-rw-r--r--src/actions/index.js2
-rw-r--r--src/components/AppUpdateInfoBar.js24
-rw-r--r--src/components/auth/AuthLayout.js7
-rw-r--r--src/components/layout/AppLayout.js4
-rw-r--r--src/config.ts28
-rw-r--r--src/containers/auth/AuthLayoutContainer.js23
-rw-r--r--src/containers/layout/AppLayoutContainer.js48
-rw-r--r--src/features/announcements/actions.js10
-rw-r--r--src/features/announcements/api.js35
-rw-r--r--src/features/announcements/components/AnnouncementScreen.js300
-rw-r--r--src/features/announcements/constants.js5
-rw-r--r--src/features/announcements/index.js29
-rw-r--r--src/features/announcements/store.js148
-rw-r--r--src/features/todos/store.js79
-rw-r--r--src/features/workspaces/components/WorkspacesDashboard.js14
-rw-r--r--src/i18n/locales/defaultMessages.json331
-rw-r--r--src/i18n/locales/en-US.json4
-rw-r--r--src/i18n/messages/src/components/AppUpdateInfoBar.json12
-rw-r--r--src/i18n/messages/src/features/announcements/components/AnnouncementScreen.json15
-rw-r--r--src/i18n/messages/src/lib/Menu.json301
-rw-r--r--src/internal-server/app/Controllers/Http/StaticController.js15
-rw-r--r--src/internal-server/start/routes.js17
-rw-r--r--src/lib/Menu.js472
-rw-r--r--src/routes.js31
-rw-r--r--src/stores/AppStore.js53
-rw-r--r--src/stores/FeaturesStore.js24
-rw-r--r--src/stores/index.ts4
31 files changed, 797 insertions, 1332 deletions
diff --git a/.eslintrc b/.eslintrc
index 3aae0e438..47811e7be 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -2,20 +2,35 @@
2 "root": true, 2 "root": true,
3 "parser": "@babel/eslint-parser", 3 "parser": "@babel/eslint-parser",
4 "extends": "eslint-config-airbnb", 4 "extends": "eslint-config-airbnb",
5 "plugins": ["jest"], 5 "plugins": [
6 "jest"
7 ],
6 "overrides": [ 8 "overrides": [
7 { 9 {
8 "files": ["**/*.ts", "**/*.tsx"], 10 "files": [
9 "env": { "browser": true, "es6": true, "node": true }, 11 "**/*.ts",
10 "extends": ["airbnb-typescript"], 12 "**/*.tsx"
13 ],
14 "env": {
15 "browser": true,
16 "es6": true,
17 "node": true
18 },
19 "extends": [
20 "airbnb-typescript"
21 ],
11 "parser": "@typescript-eslint/parser", 22 "parser": "@typescript-eslint/parser",
12 "parserOptions": { 23 "parserOptions": {
13 "ecmaFeatures": { "jsx": true }, 24 "ecmaFeatures": {
25 "jsx": true
26 },
14 "ecmaVersion": 2018, 27 "ecmaVersion": 2018,
15 "sourceType": "module", 28 "sourceType": "module",
16 "project": "./tsconfig.json" 29 "project": "./tsconfig.json"
17 }, 30 },
18 "plugins": ["@typescript-eslint"], 31 "plugins": [
32 "@typescript-eslint"
33 ],
19 "rules": { 34 "rules": {
20 // eslint 35 // eslint
21 "arrow-parens": 0, 36 "arrow-parens": 0,
@@ -64,9 +79,13 @@
64 "jsx-a11y/label-has-for": [ 79 "jsx-a11y/label-has-for": [
65 2, 80 2,
66 { 81 {
67 "components": ["Label"], 82 "components": [
83 "Label"
84 ],
68 "required": { 85 "required": {
69 "every": ["id"] 86 "every": [
87 "id"
88 ]
70 }, 89 },
71 "allowChildren": false 90 "allowChildren": false
72 } 91 }
@@ -104,7 +123,15 @@
104 "function-paren-newline": 0, 123 "function-paren-newline": 0,
105 "max-len": 0, 124 "max-len": 0,
106 "no-await-in-loop": 1, 125 "no-await-in-loop": 1,
107 "no-console": [1, { "allow": ["warn", "error"] }], 126 "no-console": [
127 1,
128 {
129 "allow": [
130 "warn",
131 "error"
132 ]
133 }
134 ],
108 "no-param-reassign": 1, 135 "no-param-reassign": 1,
109 "no-restricted-syntax": 0, 136 "no-restricted-syntax": 0,
110 "no-underscore-dangle": 0, 137 "no-underscore-dangle": 0,
@@ -121,6 +148,7 @@
121 "react/destructuring-assignment": 0, 148 "react/destructuring-assignment": 0,
122 "react/jsx-curly-newline": 0, 149 "react/jsx-curly-newline": 0,
123 "react/jsx-filename-extension": 1, 150 "react/jsx-filename-extension": 1,
151 "react/jsx-one-expression-per-line": 0,
124 "react/jsx-no-bind": 1, 152 "react/jsx-no-bind": 1,
125 "react/jsx-props-no-spreading": 0, 153 "react/jsx-props-no-spreading": 0,
126 "react/prefer-stateless-function": 1, 154 "react/prefer-stateless-function": 1,
diff --git a/docs/example-feature/index.js b/docs/example-feature/index.js
deleted file mode 100644
index 0ce068e26..000000000
--- a/docs/example-feature/index.js
+++ /dev/null
@@ -1,34 +0,0 @@
1import { reaction, runInAction } from 'mobx';
2import { ExampleFeatureStore } from './store';
3import state, { resetState } from './state';
4import api from './api';
5
6const debug = require('debug')('Ferdi:feature:EXAMPLE_FEATURE');
7
8let store = null;
9
10export default function initAnnouncements(stores, actions) {
11 const { features } = stores;
12
13 // Toggle workspace feature
14 reaction(
15 () => (
16 features.features.isExampleFeatureEnabled
17 ),
18 (isEnabled) => {
19 if (isEnabled) {
20 debug('Initializing `EXAMPLE_FEATURE` feature');
21 store = new ExampleFeatureStore(stores, api, actions, state);
22 store.initialize();
23 runInAction(() => { state.isFeatureActive = true; });
24 } else if (store) {
25 debug('Disabling `EXAMPLE_FEATURE` feature');
26 runInAction(() => { state.isFeatureActive = false; });
27 store.teardown();
28 store = null;
29 resetState(); // Reset state to default
30 }
31 },
32 { fireImmediately: true },
33 );
34}
diff --git a/packages/theme/src/themes/dark/index.ts b/packages/theme/src/themes/dark/index.ts
index 7d7bab399..87a68cb5d 100644
--- a/packages/theme/src/themes/dark/index.ts
+++ b/packages/theme/src/themes/dark/index.ts
@@ -153,13 +153,6 @@ export default (brandPrimary: string) => {
153 }, 153 },
154 }), 154 }),
155 155
156 // Announcements
157 announcements: merge({}, defaultStyles.announcements, {
158 spotlight: {
159 background: legacyStyles.darkThemeGrayDark,
160 },
161 }),
162
163 // Todos 156 // Todos
164 todos: merge({}, defaultStyles.todos, { 157 todos: merge({}, defaultStyles.todos, {
165 todosLayer: { 158 todosLayer: {
diff --git a/packages/theme/src/themes/default/index.ts b/packages/theme/src/themes/default/index.ts
index 21017bbe0..7f69e22a5 100644
--- a/packages/theme/src/themes/default/index.ts
+++ b/packages/theme/src/themes/default/index.ts
@@ -228,13 +228,6 @@ export default (brandPrimary: string) => {
228 }, 228 },
229 }, 229 },
230 230
231 // Announcements
232 announcements: {
233 spotlight: {
234 background: legacyStyles.themeGrayLightest,
235 },
236 },
237
238 // Todos 231 // Todos
239 todos: { 232 todos: {
240 todosLayer: { 233 todosLayer: {
diff --git a/src/actions/index.js b/src/actions/index.js
index ed8283d6c..aecdac675 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -10,7 +10,6 @@ import user from './user';
10import news from './news'; 10import news from './news';
11import settings from './settings'; 11import settings from './settings';
12import requests from './requests'; 12import requests from './requests';
13import announcements from '../features/announcements/actions';
14import workspaces from '../features/workspaces/actions'; 13import workspaces from '../features/workspaces/actions';
15import todos from '../features/todos/actions'; 14import todos from '../features/todos/actions';
16 15
@@ -28,7 +27,6 @@ const actions = {
28 27
29export default Object.assign( 28export default Object.assign(
30 defineActions(actions, PropTypes.checkPropTypes), 29 defineActions(actions, PropTypes.checkPropTypes),
31 { announcements },
32 { workspaces }, 30 { workspaces },
33 { todos }, 31 { todos },
34); 32);
diff --git a/src/components/AppUpdateInfoBar.js b/src/components/AppUpdateInfoBar.js
index f51fe029b..30804262a 100644
--- a/src/components/AppUpdateInfoBar.js
+++ b/src/components/AppUpdateInfoBar.js
@@ -2,8 +2,9 @@ import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { defineMessages, intlShape } from 'react-intl'; 3import { defineMessages, intlShape } from 'react-intl';
4 4
5import { announcementActions } from '../features/announcements/actions';
6import InfoBar from './ui/InfoBar'; 5import InfoBar from './ui/InfoBar';
6import { GITHUB_FERDI_URL } from '../config';
7import { openExternalUrl } from '../helpers/url-helpers';
7 8
8const messages = defineMessages({ 9const messages = defineMessages({
9 updateAvailable: { 10 updateAvailable: {
@@ -23,25 +24,16 @@ const messages = defineMessages({
23class AppUpdateInfoBar extends Component { 24class AppUpdateInfoBar extends Component {
24 static propTypes = { 25 static propTypes = {
25 onInstallUpdate: PropTypes.func.isRequired, 26 onInstallUpdate: PropTypes.func.isRequired,
26 nextAppReleaseVersion: PropTypes.string,
27 onHide: PropTypes.func.isRequired, 27 onHide: PropTypes.func.isRequired,
28 }; 28 };
29 29
30 static defaultProps = {
31 nextAppReleaseVersion: null,
32 };
33
34 static contextTypes = { 30 static contextTypes = {
35 intl: intlShape, 31 intl: intlShape,
36 }; 32 };
37 33
38 render() { 34 render() {
39 const { intl } = this.context; 35 const { intl } = this.context;
40 const { 36 const { onInstallUpdate, onHide } = this.props;
41 onInstallUpdate,
42 nextAppReleaseVersion,
43 onHide,
44 } = this.props;
45 37
46 return ( 38 return (
47 <InfoBar 39 <InfoBar
@@ -51,12 +43,16 @@ class AppUpdateInfoBar extends Component {
51 onHide={onHide} 43 onHide={onHide}
52 > 44 >
53 <span className="mdi mdi-information" /> 45 <span className="mdi mdi-information" />
54 {intl.formatMessage(messages.updateAvailable)} 46 {intl.formatMessage(messages.updateAvailable)}{' '}
55 {' '}
56 <button 47 <button
57 className="info-bar__inline-button" 48 className="info-bar__inline-button"
58 type="button" 49 type="button"
59 onClick={() => announcementActions.show({ targetVersion: nextAppReleaseVersion })} 50 onClick={() =>
51 openExternalUrl(
52 `${GITHUB_FERDI_URL}/ferdi/blob/develop/CHANGELOG.md`,
53 true,
54 )
55 }
60 > 56 >
61 <u>{intl.formatMessage(messages.changelog)}</u> 57 <u>{intl.formatMessage(messages.changelog)}</u>
62 </button> 58 </button>
diff --git a/src/components/auth/AuthLayout.js b/src/components/auth/AuthLayout.js
index c0e6b982b..8235932c2 100644
--- a/src/components/auth/AuthLayout.js
+++ b/src/components/auth/AuthLayout.js
@@ -28,7 +28,6 @@ class AuthLayout extends Component {
28 retryHealthCheck: PropTypes.func.isRequired, 28 retryHealthCheck: PropTypes.func.isRequired,
29 isHealthCheckLoading: PropTypes.bool.isRequired, 29 isHealthCheckLoading: PropTypes.bool.isRequired,
30 isFullScreen: PropTypes.bool.isRequired, 30 isFullScreen: PropTypes.bool.isRequired,
31 nextAppReleaseVersion: PropTypes.string,
32 installAppUpdate: PropTypes.func.isRequired, 31 installAppUpdate: PropTypes.func.isRequired,
33 appUpdateIsDownloaded: PropTypes.bool.isRequired, 32 appUpdateIsDownloaded: PropTypes.bool.isRequired,
34 }; 33 };
@@ -37,10 +36,6 @@ class AuthLayout extends Component {
37 shouldShowAppUpdateInfoBar: true, 36 shouldShowAppUpdateInfoBar: true,
38 }; 37 };
39 38
40 static defaultProps = {
41 nextAppReleaseVersion: null,
42 };
43
44 static contextTypes = { 39 static contextTypes = {
45 intl: intlShape, 40 intl: intlShape,
46 }; 41 };
@@ -54,7 +49,6 @@ class AuthLayout extends Component {
54 retryHealthCheck, 49 retryHealthCheck,
55 isHealthCheckLoading, 50 isHealthCheckLoading,
56 isFullScreen, 51 isFullScreen,
57 nextAppReleaseVersion,
58 installAppUpdate, 52 installAppUpdate,
59 appUpdateIsDownloaded, 53 appUpdateIsDownloaded,
60 } = this.props; 54 } = this.props;
@@ -77,7 +71,6 @@ class AuthLayout extends Component {
77 )} 71 )}
78 {appUpdateIsDownloaded && this.state.shouldShowAppUpdateInfoBar && ( 72 {appUpdateIsDownloaded && this.state.shouldShowAppUpdateInfoBar && (
79 <AppUpdateInfoBar 73 <AppUpdateInfoBar
80 nextAppReleaseVersion={nextAppReleaseVersion}
81 onInstallUpdate={installAppUpdate} 74 onInstallUpdate={installAppUpdate}
82 onHide={() => { 75 onHide={() => {
83 this.setState({ shouldShowAppUpdateInfoBar: false }); 76 this.setState({ shouldShowAppUpdateInfoBar: false });
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js
index 00d7a759b..e6e5d40fe 100644
--- a/src/components/layout/AppLayout.js
+++ b/src/components/layout/AppLayout.js
@@ -76,7 +76,6 @@ class AppLayout extends Component {
76 news: MobxPropTypes.arrayOrObservableArray.isRequired, 76 news: MobxPropTypes.arrayOrObservableArray.isRequired,
77 showServicesUpdatedInfoBar: PropTypes.bool.isRequired, 77 showServicesUpdatedInfoBar: PropTypes.bool.isRequired,
78 appUpdateIsDownloaded: PropTypes.bool.isRequired, 78 appUpdateIsDownloaded: PropTypes.bool.isRequired,
79 nextAppReleaseVersion: PropTypes.string,
80 authRequestFailed: PropTypes.bool.isRequired, 79 authRequestFailed: PropTypes.bool.isRequired,
81 removeNewsItem: PropTypes.func.isRequired, 80 removeNewsItem: PropTypes.func.isRequired,
82 reloadServicesAfterUpdate: PropTypes.func.isRequired, 81 reloadServicesAfterUpdate: PropTypes.func.isRequired,
@@ -93,7 +92,6 @@ class AppLayout extends Component {
93 92
94 static defaultProps = { 93 static defaultProps = {
95 children: [], 94 children: [],
96 nextAppReleaseVersion: null,
97 }; 95 };
98 96
99 static contextTypes = { 97 static contextTypes = {
@@ -111,7 +109,6 @@ class AppLayout extends Component {
111 news, 109 news,
112 showServicesUpdatedInfoBar, 110 showServicesUpdatedInfoBar,
113 appUpdateIsDownloaded, 111 appUpdateIsDownloaded,
114 nextAppReleaseVersion,
115 authRequestFailed, 112 authRequestFailed,
116 removeNewsItem, 113 removeNewsItem,
117 reloadServicesAfterUpdate, 114 reloadServicesAfterUpdate,
@@ -195,7 +192,6 @@ class AppLayout extends Component {
195 )} 192 )}
196 {appUpdateIsDownloaded && this.state.shouldShowAppUpdateInfoBar && ( 193 {appUpdateIsDownloaded && this.state.shouldShowAppUpdateInfoBar && (
197 <AppUpdateInfoBar 194 <AppUpdateInfoBar
198 nextAppReleaseVersion={nextAppReleaseVersion}
199 onInstallUpdate={installAppUpdate} 195 onInstallUpdate={installAppUpdate}
200 onHide={() => { 196 onHide={() => {
201 this.setState({ shouldShowAppUpdateInfoBar: false }); 197 this.setState({ shouldShowAppUpdateInfoBar: false });
diff --git a/src/config.ts b/src/config.ts
index 835d130da..7bb2525a5 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -27,7 +27,8 @@ export const STATS_API = 'https://stats.franzinfra.com';
27 27
28export const LOCAL_TODOS_FRONTEND_URL = 'http://localhost:4000'; 28export const LOCAL_TODOS_FRONTEND_URL = 'http://localhost:4000';
29export const PRODUCTION_TODOS_FRONTEND_URL = 'https://app.franztodos.com'; 29export const PRODUCTION_TODOS_FRONTEND_URL = 'https://app.franztodos.com';
30export const DEVELOPMENT_TODOS_FRONTEND_URL = 'https://development--franz-todos.netlify.com'; 30export const DEVELOPMENT_TODOS_FRONTEND_URL =
31 'https://development--franz-todos.netlify.com';
31 32
32export const CDN_URL = 'https://cdn.franzinfra.com'; 33export const CDN_URL = 'https://cdn.franzinfra.com';
33 34
@@ -69,8 +70,10 @@ export const SEARCH_ENGINE_NAMES = {
69}; 70};
70 71
71export const SEARCH_ENGINE_URLS = { 72export const SEARCH_ENGINE_URLS = {
72 [SEARCH_ENGINE_GOOGLE]: ({ searchTerm }) => `https://www.google.com/search?q=${searchTerm}`, 73 [SEARCH_ENGINE_GOOGLE]: ({ searchTerm }) =>
73 [SEARCH_ENGINE_DDG]: ({ searchTerm }) => `https://duckduckgo.com/?q=${searchTerm}`, 74 `https://www.google.com/search?q=${searchTerm}`,
75 [SEARCH_ENGINE_DDG]: ({ searchTerm }) =>
76 `https://duckduckgo.com/?q=${searchTerm}`,
74}; 77};
75 78
76export const CUSTOM_TODO_SERVICE = 'isUsingCustomTodoService'; 79export const CUSTOM_TODO_SERVICE = 'isUsingCustomTodoService';
@@ -83,7 +86,8 @@ const TODO_HABITICA_URL = 'https://habitica.com/login';
83const TODO_NOZBE_URL = 'https://app.nozbe.com/#login'; 86const TODO_NOZBE_URL = 'https://app.nozbe.com/#login';
84const TODO_RTM_URL = 'https://www.rememberthemilk.com/login/'; 87const TODO_RTM_URL = 'https://www.rememberthemilk.com/login/';
85const TODO_ANYDO_URL = 'https://desktop.any.do/'; 88const TODO_ANYDO_URL = 'https://desktop.any.do/';
86const TODO_GOOGLETASKS_URL = 'https://tasks.google.com/embed/?origin=https%3A%2F%2Fcalendar.google.com&fullWidth=1'; 89const TODO_GOOGLETASKS_URL =
90 'https://tasks.google.com/embed/?origin=https%3A%2F%2Fcalendar.google.com&fullWidth=1';
87 91
88export const TODO_SERVICE_RECIPE_IDS = { 92export const TODO_SERVICE_RECIPE_IDS = {
89 [TODO_TODOIST_URL]: 'todoist', 93 [TODO_TODOIST_URL]: 'todoist',
@@ -108,7 +112,8 @@ export const TODO_APPS = {
108}; 112};
109 113
110export const DEFAULT_TODO_SERVICE = TODO_FRANZ_TODOS_URL; 114export const DEFAULT_TODO_SERVICE = TODO_FRANZ_TODOS_URL;
111export const DEFAULT_TODO_RECIPE_ID = TODO_SERVICE_RECIPE_IDS[DEFAULT_TODO_SERVICE]; 115export const DEFAULT_TODO_RECIPE_ID =
116 TODO_SERVICE_RECIPE_IDS[DEFAULT_TODO_SERVICE];
112export const DEFAULT_TODO_SERVICE_NAME = TODO_APPS[DEFAULT_TODO_SERVICE]; 117export const DEFAULT_TODO_SERVICE_NAME = TODO_APPS[DEFAULT_TODO_SERVICE];
113 118
114export const SIDEBAR_WIDTH = { 119export const SIDEBAR_WIDTH = {
@@ -135,7 +140,6 @@ export const iconSizeBias = 20;
135 140
136export const DEFAULT_FEATURES_CONFIG = { 141export const DEFAULT_FEATURES_CONFIG = {
137 isServiceProxyEnabled: true, 142 isServiceProxyEnabled: true,
138 isAnnouncementsEnabled: true,
139 isWorkspaceEnabled: true, 143 isWorkspaceEnabled: true,
140 isTodosEnabled: true, 144 isTodosEnabled: true,
141 isSettingsWSEnabled: false, 145 isSettingsWSEnabled: false,
@@ -158,20 +162,12 @@ export const GITHUB_ORG_NAME = 'getferdi';
158export const GITHUB_FERDI_REPO_NAME = 'ferdi'; 162export const GITHUB_FERDI_REPO_NAME = 'ferdi';
159export const GITHUB_NIGHTLIES_REPO_NAME = 'nightlies'; 163export const GITHUB_NIGHTLIES_REPO_NAME = 'nightlies';
160 164
161export const FILE_SYSTEM_SETTINGS_TYPES = [ 165export const FILE_SYSTEM_SETTINGS_TYPES = ['app', 'proxy'];
162 'app',
163 'proxy',
164];
165 166
166export const LOCAL_SERVER = 'You are using Ferdi without a server'; 167export const LOCAL_SERVER = 'You are using Ferdi without a server';
167export const SERVER_NOT_LOADED = 'Ferdi::SERVER_NOT_LOADED'; 168export const SERVER_NOT_LOADED = 'Ferdi::SERVER_NOT_LOADED';
168 169
169export const ALLOWED_PROTOCOLS = [ 170export const ALLOWED_PROTOCOLS = ['https:', 'http:', 'ftp:', 'ferdi:'];
170 'https:',
171 'http:',
172 'ftp:',
173 'ferdi:',
174];
175 171
176export const DEFAULT_SETTING_KEEP_ALL_WORKSPACES_LOADED = false; 172export const DEFAULT_SETTING_KEEP_ALL_WORKSPACES_LOADED = false;
177 173
diff --git a/src/containers/auth/AuthLayoutContainer.js b/src/containers/auth/AuthLayoutContainer.js
index f99a8a73f..a0d50ab54 100644
--- a/src/containers/auth/AuthLayoutContainer.js
+++ b/src/containers/auth/AuthLayoutContainer.js
@@ -14,7 +14,10 @@ import AppLoader from '../../components/ui/AppLoader';
14import { oneOrManyChildElements } from '../../prop-types'; 14import { oneOrManyChildElements } from '../../prop-types';
15import FeaturesStore from '../../stores/FeaturesStore'; 15import FeaturesStore from '../../stores/FeaturesStore';
16 16
17export default @inject('stores', 'actions') @observer class AuthLayoutContainer extends Component { 17export default
18@inject('stores', 'actions')
19@observer
20class AuthLayoutContainer extends Component {
18 static propTypes = { 21 static propTypes = {
19 children: oneOrManyChildElements.isRequired, 22 children: oneOrManyChildElements.isRequired,
20 location: PropTypes.shape({ 23 location: PropTypes.shape({
@@ -23,15 +26,12 @@ export default @inject('stores', 'actions') @observer class AuthLayoutContainer
23 }; 26 };
24 27
25 render() { 28 render() {
26 const { 29 const { stores, actions, children, location } = this.props;
27 stores, actions, children, location, 30 const { app, features, globalError, user } = stores;
28 } = this.props;
29 const {
30 app, features, globalError, user,
31 } = stores;
32 31
33 const isLoadingBaseFeatures = features.defaultFeaturesRequest.isExecuting 32 const isLoadingBaseFeatures =
34 && !features.defaultFeaturesRequest.wasExecuted; 33 features.defaultFeaturesRequest.isExecuting &&
34 !features.defaultFeaturesRequest.wasExecuted;
35 35
36 if (isLoadingBaseFeatures) { 36 if (isLoadingBaseFeatures) {
37 return ( 37 return (
@@ -61,8 +61,9 @@ export default @inject('stores', 'actions') @observer class AuthLayoutContainer
61 isHealthCheckLoading={app.healthCheckRequest.isExecuting} 61 isHealthCheckLoading={app.healthCheckRequest.isExecuting}
62 isFullScreen={app.isFullScreen} 62 isFullScreen={app.isFullScreen}
63 installAppUpdate={actions.app.installUpdate} 63 installAppUpdate={actions.app.installUpdate}
64 nextAppReleaseVersion={app.nextAppReleaseVersion} 64 appUpdateIsDownloaded={
65 appUpdateIsDownloaded={app.updateStatus === app.updateStatusTypes.DOWNLOADED} 65 app.updateStatus === app.updateStatusTypes.DOWNLOADED
66 }
66 > 67 >
67 {children} 68 {children}
68 </AuthLayout> 69 </AuthLayout>
diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js
index 32b1f1cf7..83a25168b 100644
--- a/src/containers/layout/AppLayoutContainer.js
+++ b/src/containers/layout/AppLayoutContainer.js
@@ -25,7 +25,10 @@ import WorkspaceDrawer from '../../features/workspaces/components/WorkspaceDrawe
25import { workspaceStore } from '../../features/workspaces'; 25import { workspaceStore } from '../../features/workspaces';
26import WorkspacesStore from '../../features/workspaces/store'; 26import WorkspacesStore from '../../features/workspaces/store';
27 27
28export default @inject('stores', 'actions') @observer class AppLayoutContainer extends Component { 28export default
29@inject('stores', 'actions')
30@observer
31class AppLayoutContainer extends Component {
29 static defaultProps = { 32 static defaultProps = {
30 children: null, 33 children: null,
31 }; 34 };
@@ -65,25 +68,25 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e
65 68
66 const { retryRequiredRequests } = this.props.actions.requests; 69 const { retryRequiredRequests } = this.props.actions.requests;
67 70
68 const { 71 const { installUpdate, toggleMuteApp } = this.props.actions.app;
69 installUpdate,
70 toggleMuteApp,
71 } = this.props.actions.app;
72 72
73 const { 73 const { openSettings, closeSettings } = this.props.actions.ui;
74 openSettings,
75 closeSettings,
76 } = this.props.actions.ui;
77 74
78 const { children } = this.props; 75 const { children } = this.props;
79 76
80 const isLoadingFeatures = features.featuresRequest.isExecuting 77 const isLoadingFeatures =
81 && !features.featuresRequest.wasExecuted; 78 features.featuresRequest.isExecuting &&
79 !features.featuresRequest.wasExecuted;
82 80
83 const isLoadingServices = services.allServicesRequest.isExecuting 81 const isLoadingServices =
84 && services.allServicesRequest.isExecutingFirstTime; 82 services.allServicesRequest.isExecuting &&
83 services.allServicesRequest.isExecutingFirstTime;
85 84
86 if (isLoadingFeatures || isLoadingServices || workspaces.isLoadingWorkspaces) { 85 if (
86 isLoadingFeatures ||
87 isLoadingServices ||
88 workspaces.isLoadingWorkspaces
89 ) {
87 return ( 90 return (
88 <ThemeProvider theme={ui.theme}> 91 <ThemeProvider theme={ui.theme}>
89 <AppLoader /> 92 <AppLoader />
@@ -93,9 +96,11 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e
93 96
94 const workspacesDrawer = ( 97 const workspacesDrawer = (
95 <WorkspaceDrawer 98 <WorkspaceDrawer
96 getServicesForWorkspace={(workspace) => ( 99 getServicesForWorkspace={workspace =>
97 workspace ? workspaceStore.getWorkspaceServices(workspace).map((s) => s.name) : services.all.map((s) => s.name) 100 (workspace
98 )} 101 ? workspaceStore.getWorkspaceServices(workspace).map(s => s.name)
102 : services.all.map(s => s.name))
103 }
99 /> 104 />
100 ); 105 );
101 106
@@ -118,7 +123,9 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e
118 toggleMuteApp={toggleMuteApp} 123 toggleMuteApp={toggleMuteApp}
119 toggleWorkspaceDrawer={workspaceActions.toggleWorkspaceDrawer} 124 toggleWorkspaceDrawer={workspaceActions.toggleWorkspaceDrawer}
120 isWorkspaceDrawerOpen={workspaceStore.isWorkspaceDrawerOpen} 125 isWorkspaceDrawerOpen={workspaceStore.isWorkspaceDrawerOpen}
121 showMessageBadgeWhenMutedSetting={settings.all.app.showMessageBadgeWhenMuted} 126 showMessageBadgeWhenMutedSetting={
127 settings.all.app.showMessageBadgeWhenMuted
128 }
122 showMessageBadgesEvenWhenMuted={ui.showMessageBadgesEvenWhenMuted} 129 showMessageBadgesEvenWhenMuted={ui.showMessageBadgesEvenWhenMuted}
123 isTodosServiceActive={services.isTodosServiceActive || false} 130 isTodosServiceActive={services.isTodosServiceActive || false}
124 /> 131 />
@@ -145,8 +152,9 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e
145 isFullScreen={app.isFullScreen} 152 isFullScreen={app.isFullScreen}
146 isOnline={app.isOnline} 153 isOnline={app.isOnline}
147 showServicesUpdatedInfoBar={ui.showServicesUpdatedInfoBar} 154 showServicesUpdatedInfoBar={ui.showServicesUpdatedInfoBar}
148 appUpdateIsDownloaded={app.updateStatus === app.updateStatusTypes.DOWNLOADED} 155 appUpdateIsDownloaded={
149 nextAppReleaseVersion={app.nextAppReleaseVersion} 156 app.updateStatus === app.updateStatusTypes.DOWNLOADED
157 }
150 authRequestFailed={app.authRequestFailed} 158 authRequestFailed={app.authRequestFailed}
151 sidebar={sidebar} 159 sidebar={sidebar}
152 workspacesDrawer={workspacesDrawer} 160 workspacesDrawer={workspacesDrawer}
diff --git a/src/features/announcements/actions.js b/src/features/announcements/actions.js
deleted file mode 100644
index bab496314..000000000
--- a/src/features/announcements/actions.js
+++ /dev/null
@@ -1,10 +0,0 @@
1import PropTypes from 'prop-types';
2import { createActionsFromDefinitions } from '../../actions/lib/actions';
3
4export const announcementActions = createActionsFromDefinitions({
5 show: {
6 targetVersion: PropTypes.string,
7 },
8}, PropTypes.checkPropTypes);
9
10export default announcementActions;
diff --git a/src/features/announcements/api.js b/src/features/announcements/api.js
deleted file mode 100644
index 962f3e694..000000000
--- a/src/features/announcements/api.js
+++ /dev/null
@@ -1,35 +0,0 @@
1import Request from '../../stores/lib/Request';
2import apiBase from '../../api/apiBase';
3import { GITHUB_FERDI_REPO_NAME, GITHUB_NIGHTLIES_REPO_NAME, GITHUB_ORG_NAME } from '../../config';
4import { ferdiVersion } from '../../environment';
5
6const debug = require('debug')('Ferdi:feature:announcements:api');
7
8export const announcementsApi = {
9 async getCurrentVersion() {
10 debug('getting current version of electron app');
11 return Promise.resolve(ferdiVersion);
12 },
13
14 async getChangelog(version) {
15 const ferdiRepoName = version.includes('nightly') ? GITHUB_NIGHTLIES_REPO_NAME : GITHUB_FERDI_REPO_NAME;
16 const url = `https://api.github.com/repos/${GITHUB_ORG_NAME}/${ferdiRepoName}/releases/tags/v${version}`;
17 debug(`fetching release changelog from Github url: ${url}`);
18 const request = await window.fetch(url, { method: 'GET' });
19 if (!request.ok) return null;
20 const data = await request.json();
21 return data.body;
22 },
23
24 async getAnnouncement(version) {
25 const url = `${apiBase()}/announcements/${version}`;
26 debug(`fetching release announcement from api url: ${url}`);
27 const response = await window.fetch(url, { method: 'GET' });
28 if (!response.ok) return null;
29 return response.json();
30 },
31};
32
33export const getCurrentVersionRequest = new Request(announcementsApi, 'getCurrentVersion');
34export const getChangelogRequest = new Request(announcementsApi, 'getChangelog');
35export const getAnnouncementRequest = new Request(announcementsApi, 'getAnnouncement');
diff --git a/src/features/announcements/components/AnnouncementScreen.js b/src/features/announcements/components/AnnouncementScreen.js
deleted file mode 100644
index 315843db3..000000000
--- a/src/features/announcements/components/AnnouncementScreen.js
+++ /dev/null
@@ -1,300 +0,0 @@
1import React, { Component } from 'react';
2import marked from 'marked';
3import PropTypes from 'prop-types';
4import { inject, observer } from 'mobx-react';
5import { defineMessages, intlShape } from 'react-intl';
6import injectSheet from 'react-jss';
7import { Button } from '@meetfranz/forms';
8
9import { announcementsStore } from '../index';
10import UIStore from '../../../stores/UIStore';
11import AppStore from '../../../stores/AppStore';
12
13const renderer = new marked.Renderer();
14
15renderer.link = (href, title, text) => `<a target="_blank" href="${href}" title="${title}">${text}</a>`;
16
17const markedOptions = { sanitize: true, renderer };
18
19const messages = defineMessages({
20 headline: {
21 id: 'feature.announcements.changelog.headline',
22 defaultMessage: '!!!Changes in Ferdi {version}',
23 },
24});
25
26const smallScreen = '1000px';
27
28const styles = (theme) => ({
29 container: {
30 background: theme.colorBackground,
31 position: 'relative',
32 top: 0,
33 zIndex: 140,
34 width: '100%',
35 height: '100%',
36 overflowY: 'auto',
37 },
38 headline: {
39 color: theme.colorHeadline,
40 margin: [25, 0, 40],
41 // 'max-width': 500,
42 'text-align': 'center',
43 'line-height': '1.3em',
44 },
45 announcement: {
46 height: 'auto',
47
48 [`@media(min-width: ${smallScreen})`]: {
49 display: 'flex',
50 flexDirection: 'column',
51 justifyContent: 'center',
52 height: '100vh',
53 },
54 },
55 main: {
56 display: 'flex',
57 flexDirection: 'column',
58 flexGrow: 1,
59 justifyContent: 'center',
60
61 '& h1': {
62 margin: [40, 0, 15],
63 fontSize: 70,
64 color: theme.styleTypes.primary.accent,
65 textAlign: 'center',
66
67 [`@media(min-width: ${smallScreen})`]: {
68 marginTop: 0,
69 },
70 },
71 '& h2': {
72 fontSize: 30,
73 fontWeight: 300,
74 color: theme.colorText,
75 textAlign: 'center',
76 marginBottom: 60,
77 },
78 },
79 mainBody: {
80 display: 'flex',
81 flexDirection: 'column',
82 alignItems: 'center',
83 width: 'calc(100% - 80px)',
84 height: 'auto',
85 margin: '0 auto',
86 [`@media(min-width: ${smallScreen})`]: {
87 flexDirection: 'row',
88 justifyContent: 'center',
89 },
90 },
91 mainImage: {
92 minWidth: 250,
93 maxWidth: 400,
94 margin: '0 auto',
95 marginBottom: 40,
96 '& img': {
97 width: '100%',
98 },
99 [`@media(min-width: ${smallScreen})`]: {
100 margin: 0,
101 },
102 },
103 mainText: {
104 height: 'auto',
105 maxWidth: 600,
106 textAlign: 'center',
107 '& p': {
108 lineHeight: '1.5em',
109 },
110 [`@media(min-width: ${smallScreen})`]: {
111 textAlign: 'left',
112 },
113 },
114 mainCtaButton: {
115 textAlign: 'center',
116 marginTop: 40,
117 [`@media(min-width: ${smallScreen})`]: {
118 textAlign: 'left',
119 },
120 },
121 spotlight: {
122 height: 'auto',
123 background: theme.announcements.spotlight.background,
124 padding: [40, 0],
125 marginTop: 80,
126 [`@media(min-width: ${smallScreen})`]: {
127 marginTop: 0,
128 justifyContent: 'center',
129 alignItems: 'flex-start',
130 display: 'flex',
131 flexDirection: 'row',
132 },
133 },
134 spotlightTopicContainer: {
135 textAlign: 'center',
136 marginBottom: 20,
137
138 [`@media(min-width: ${smallScreen})`]: {
139 marginBottom: 0,
140 minWidth: 250,
141 maxWidth: 330,
142 width: '100%',
143 textAlign: 'right',
144 marginRight: 60,
145 },
146 },
147 spotlightContentContainer: {
148 textAlign: 'center',
149 [`@media(min-width: ${smallScreen})`]: {
150 height: 'auto',
151 maxWidth: 600,
152 paddingRight: 40,
153 textAlign: 'left',
154 },
155 '& p': {
156 lineHeight: '1.5em',
157 },
158 },
159 spotlightTopic: {
160 fontSize: 20,
161 marginBottom: 5,
162 letterSpacing: 0,
163 fontWeight: 100,
164 },
165 spotlightSubject: {
166 fontSize: 20,
167 },
168 changelog: {
169 padding: [0, 60],
170 maxWidth: 700,
171 margin: [100, 'auto'],
172 height: 'auto',
173
174 '& h3': {
175 fontSize: '24px',
176 margin: '1.5em 0 1em 0',
177 },
178 '& li': {
179 marginBottom: '1em',
180 lineHeight: '1.4em',
181 },
182 '& div': {
183 height: 'auto',
184 },
185 },
186});
187
188@inject('stores', 'actions') @injectSheet(styles) @observer
189class AnnouncementScreen extends Component {
190 static propTypes = {
191 classes: PropTypes.object.isRequired,
192 stores: PropTypes.shape({
193 ui: PropTypes.instanceOf(UIStore).isRequired,
194 }).isRequired,
195 actions: PropTypes.shape({
196 app: PropTypes.instanceOf(AppStore).isRequired,
197 }).isRequired,
198 };
199
200 static contextTypes = {
201 intl: intlShape,
202 };
203
204 render() {
205 const { classes, stores, actions } = this.props;
206 const { intl } = this.context;
207 const { changelog, announcement } = announcementsStore;
208 const themeImage = stores.ui.isDarkThemeActive ? 'dark' : 'light';
209 return (
210 <div className={classes.container}>
211 {announcement && (
212 <div className={classes.announcement}>
213 <div className={classes.main}>
214 <h1>{announcement.main.headline}</h1>
215 <h2>{announcement.main.subHeadline}</h2>
216 <div className={classes.mainBody}>
217 <div className={classes.mainImage}>
218 <img
219 src={announcement.main.image[themeImage]}
220 alt=""
221 />
222 </div>
223 <div className={classes.mainText}>
224 <div
225 dangerouslySetInnerHTML={{
226 __html: marked(announcement.main.text, markedOptions),
227 }}
228 />
229 {(announcement.main.cta.label || announcement.main.cta.href) && (
230 <div className={classes.mainCtaButton}>
231 <Button
232 label={announcement.main.cta.label}
233 onClick={() => {
234 const {
235 href,
236 } = announcement.main.cta;
237 if (announcement.main.cta.href.startsWith('http')) {
238 actions.app.openExternalUrl({ url: href });
239 } else {
240 window.location.href = `#${href}`;
241 }
242 }}
243 />
244 </div>
245 )}
246 </div>
247 </div>
248 </div>
249 {announcement.spotlight && (
250 <div className={classes.spotlight}>
251 <div className={classes.spotlightTopicContainer}>
252 <h2 className={classes.spotlightTopic}>{announcement.spotlight.title}</h2>
253 <h3 className={classes.spotlightSubject}>{announcement.spotlight.subject}</h3>
254 </div>
255 <div className={classes.spotlightContentContainer}>
256 <div
257 dangerouslySetInnerHTML={{
258 __html: marked(announcement.spotlight.text, markedOptions),
259 }}
260 />
261 <div className={classes.mainCtaButton}>
262 <Button
263 label={announcement.spotlight.cta.label}
264 onClick={() => {
265 const {
266 href,
267 } = announcement.spotlight.cta;
268 if (announcement.spotlight.cta.href.startsWith('http')) {
269 actions.app.openExternalUrl({ url: href });
270 } else {
271 window.location.href = `#${href}`;
272 }
273 }}
274 />
275 </div>
276 </div>
277 </div>
278 )}
279 </div>
280 )}
281 {changelog && (
282 <div className={classes.changelog}>
283 <h1 className={classes.headline}>
284 {intl.formatMessage(messages.headline, {
285 version: announcementsStore.targetVersion,
286 })}
287 </h1>
288 <div
289 dangerouslySetInnerHTML={{
290 __html: marked(changelog, markedOptions),
291 }}
292 />
293 </div>
294 )}
295 </div>
296 );
297 }
298}
299
300export default AnnouncementScreen;
diff --git a/src/features/announcements/constants.js b/src/features/announcements/constants.js
deleted file mode 100644
index 284226fdf..000000000
--- a/src/features/announcements/constants.js
+++ /dev/null
@@ -1,5 +0,0 @@
1export const ANNOUNCEMENTS_ROUTES = {
2 TARGET: '/announcements/:id',
3};
4
5export const GA_CATEGORY_ANNOUNCEMENTS = 'Announcements';
diff --git a/src/features/announcements/index.js b/src/features/announcements/index.js
deleted file mode 100644
index 19930c5b1..000000000
--- a/src/features/announcements/index.js
+++ /dev/null
@@ -1,29 +0,0 @@
1import { reaction } from 'mobx';
2import { AnnouncementsStore } from './store';
3
4const debug = require('debug')('Ferdi:feature:announcements');
5
6export const GA_CATEGORY_ANNOUNCEMENTS = 'Announcements';
7
8export const announcementsStore = new AnnouncementsStore();
9
10export default function initAnnouncements(stores, actions) {
11 const { features } = stores;
12
13 // Toggle announcement feature
14 reaction(
15 () => (
16 features.features.isAnnouncementsEnabled
17 ),
18 (isEnabled) => {
19 if (isEnabled) {
20 debug('Initializing `announcements` feature');
21 announcementsStore.start(stores, actions);
22 } else if (announcementsStore.isFeatureActive) {
23 debug('Disabling `announcements` feature');
24 announcementsStore.stop();
25 }
26 },
27 { fireImmediately: true },
28 );
29}
diff --git a/src/features/announcements/store.js b/src/features/announcements/store.js
deleted file mode 100644
index 794d20142..000000000
--- a/src/features/announcements/store.js
+++ /dev/null
@@ -1,148 +0,0 @@
1import {
2 action,
3 computed,
4 observable,
5} from 'mobx';
6import semver from 'semver';
7import localStorage from 'mobx-localstorage';
8
9import { FeatureStore } from '../utils/FeatureStore';
10import { ANNOUNCEMENTS_ROUTES } from './constants';
11import { getAnnouncementRequest, getChangelogRequest, getCurrentVersionRequest } from './api';
12import { announcementActions } from './actions';
13import { createActionBindings } from '../utils/ActionBinding';
14import { createReactions } from '../../stores/lib/Reaction';
15import { matchRoute } from '../../helpers/routing-helpers';
16import { DEFAULT_APP_SETTINGS } from '../../environment';
17
18const LOCAL_STORAGE_KEY = 'announcements';
19
20const debug = require('debug')('Ferdi:feature:announcements:store');
21
22export class AnnouncementsStore extends FeatureStore {
23 @observable targetVersion = null;
24
25 @observable isFeatureActive = false;
26
27 @computed get changelog() {
28 return getChangelogRequest.result;
29 }
30
31 @computed get announcement() {
32 if (!this.stores || !getAnnouncementRequest.result) return null;
33 const { locale } = this.stores.app;
34 const announcement = getAnnouncementRequest.result;
35 // User locale
36 if (announcement[locale]) return announcement[locale];
37 // Default locale
38 if (announcement[DEFAULT_APP_SETTINGS.fallbackLocale]) return announcement[DEFAULT_APP_SETTINGS.fallbackLocale];
39 // No locales specified
40 return announcement;
41 }
42
43 @computed get areNewsAvailable() {
44 const isChangelogAvailable = getChangelogRequest.wasExecuted && !!this.changelog;
45 const isAnnouncementAvailable = getAnnouncementRequest.wasExecuted && !!this.announcement;
46 return isChangelogAvailable || isAnnouncementAvailable;
47 }
48
49 @computed get settings() {
50 return localStorage.getItem(LOCAL_STORAGE_KEY) || {};
51 }
52
53 @computed get lastSeenAnnouncementVersion() {
54 return this.settings.lastSeenAnnouncementVersion || null;
55 }
56
57 @computed get currentVersion() {
58 return getCurrentVersionRequest.result;
59 }
60
61 @computed get isNewUser() {
62 return this.stores.settings.stats.appStarts <= 1;
63 }
64
65 @computed get isAnnouncementShown() {
66 const { router } = this.stores;
67 return router.location.pathname.includes('/announcements');
68 }
69
70 async start(stores, actions) {
71 debug('AnnouncementsStore::start');
72 this.stores = stores;
73 this.actions = actions;
74 getCurrentVersionRequest.execute();
75
76 this._registerActions(createActionBindings([
77 [announcementActions.show, this._showAnnouncement],
78 ]));
79
80 this._reactions = createReactions([
81 this._showAnnouncementOnRouteMatch,
82 this._showAnnouncementToUsersWhoUpdatedApp,
83 this._fetchAnnouncements,
84 ]);
85 this._registerReactions(this._reactions);
86 this.isFeatureActive = true;
87 }
88
89 stop() {
90 super.stop();
91 debug('AnnouncementsStore::stop');
92 this.isFeatureActive = false;
93 }
94
95 // ======= HELPERS ======= //
96
97 _updateSettings = (changes) => {
98 localStorage.setItem(LOCAL_STORAGE_KEY, {
99 ...this.settings,
100 ...changes,
101 });
102 };
103
104 // ======= ACTIONS ======= //
105
106 @action _showAnnouncement = ({ targetVersion } = {}) => {
107 const { router } = this.stores;
108 this.targetVersion = targetVersion || this.currentVersion;
109 this._updateSettings({
110 lastSeenAnnouncementVersion: this.currentVersion,
111 });
112 const targetRoute = `/announcements/${this.targetVersion}`;
113 if (router.location.pathname !== targetRoute) {
114 this.stores.router.push(targetRoute);
115 }
116 };
117
118 // ======= REACTIONS ========
119
120 _showAnnouncementToUsersWhoUpdatedApp = () => {
121 const { announcement, isNewUser } = this;
122 // Check if there is an announcement and don't show announcements to new users
123 if (!announcement || isNewUser) return;
124
125 // Check if the user has already used current version (= has seen the announcement)
126 const { currentVersion, lastSeenAnnouncementVersion } = this;
127 if (semver.gt(currentVersion, lastSeenAnnouncementVersion || '0.0.0')) {
128 debug(`${currentVersion} > ${lastSeenAnnouncementVersion}: announcement is shown`);
129 this._showAnnouncement();
130 }
131 };
132
133 _fetchAnnouncements = () => {
134 const targetVersion = this.targetVersion || this.currentVersion;
135 if (!targetVersion) return;
136 getChangelogRequest.reset().execute(targetVersion);
137 getAnnouncementRequest.reset().execute(targetVersion);
138 };
139
140 _showAnnouncementOnRouteMatch = () => {
141 const { router } = this.stores;
142 const match = matchRoute(ANNOUNCEMENTS_ROUTES.TARGET, router.location.pathname);
143 if (match) {
144 const targetVersion = match.id;
145 this._showAnnouncement({ targetVersion });
146 }
147 }
148}
diff --git a/src/features/todos/store.js b/src/features/todos/store.js
index f283c1e59..ec06c279d 100644
--- a/src/features/todos/store.js
+++ b/src/features/todos/store.js
@@ -1,9 +1,5 @@
1import { ThemeType } from '@meetfranz/theme'; 1import { ThemeType } from '@meetfranz/theme';
2import { 2import { computed, action, observable } from 'mobx';
3 computed,
4 action,
5 observable,
6} from 'mobx';
7import localStorage from 'mobx-localstorage'; 3import localStorage from 'mobx-localstorage';
8 4
9import { todoActions } from './actions'; 5import { todoActions } from './actions';
@@ -44,12 +40,13 @@ export default class TodoStore extends FeatureStore {
44 } 40 }
45 41
46 @computed get isTodosPanelForceHidden() { 42 @computed get isTodosPanelForceHidden() {
47 const { isAnnouncementShown } = this.stores.announcements; 43 return !this.isFeatureEnabledByUser;
48 return !this.isFeatureEnabledByUser || isAnnouncementShown;
49 } 44 }
50 45
51 @computed get isTodosPanelVisible() { 46 @computed get isTodosPanelVisible() {
52 if (this.settings.isTodosPanelVisible === undefined) return DEFAULT_TODOS_VISIBLE; 47 if (this.settings.isTodosPanelVisible === undefined) {
48 return DEFAULT_TODOS_VISIBLE;
49 }
53 return this.settings.isTodosPanelVisible; 50 return this.settings.isTodosPanelVisible;
54 } 51 }
55 52
@@ -66,7 +63,10 @@ export default class TodoStore extends FeatureStore {
66 } 63 }
67 64
68 @computed get isUsingPredefinedTodoServer() { 65 @computed get isUsingPredefinedTodoServer() {
69 return this.stores && this.stores.settings.app.predefinedTodoServer !== CUSTOM_TODO_SERVICE; 66 return (
67 this.stores &&
68 this.stores.settings.app.predefinedTodoServer !== CUSTOM_TODO_SERVICE
69 );
70 } 70 }
71 71
72 @computed get todoUrl() { 72 @computed get todoUrl() {
@@ -79,12 +79,17 @@ export default class TodoStore extends FeatureStore {
79 } 79 }
80 80
81 @computed get isTodoUrlValid() { 81 @computed get isTodoUrlValid() {
82 return !this.isUsingPredefinedTodoServer || isValidExternalURL(this.todoUrl); 82 return (
83 !this.isUsingPredefinedTodoServer || isValidExternalURL(this.todoUrl)
84 );
83 } 85 }
84 86
85 @computed get todoRecipeId() { 87 @computed get todoRecipeId() {
86 if (this.isFeatureEnabledByUser && this.isUsingPredefinedTodoServer 88 if (
87 && this.todoUrl in TODO_SERVICE_RECIPE_IDS) { 89 this.isFeatureEnabledByUser &&
90 this.isUsingPredefinedTodoServer &&
91 this.todoUrl in TODO_SERVICE_RECIPE_IDS
92 ) {
88 return TODO_SERVICE_RECIPE_IDS[this.todoUrl]; 93 return TODO_SERVICE_RECIPE_IDS[this.todoUrl];
89 } 94 }
90 return null; 95 return null;
@@ -99,16 +104,21 @@ export default class TodoStore extends FeatureStore {
99 104
100 // ACTIONS 105 // ACTIONS
101 106
102 this._registerActions(createActionBindings([ 107 this._registerActions(
103 [todoActions.resize, this._resize], 108 createActionBindings([
104 [todoActions.toggleTodosPanel, this._toggleTodosPanel], 109 [todoActions.resize, this._resize],
105 [todoActions.setTodosWebview, this._setTodosWebview], 110 [todoActions.toggleTodosPanel, this._toggleTodosPanel],
106 [todoActions.handleHostMessage, this._handleHostMessage], 111 [todoActions.setTodosWebview, this._setTodosWebview],
107 [todoActions.handleClientMessage, this._handleClientMessage], 112 [todoActions.handleHostMessage, this._handleHostMessage],
108 [todoActions.toggleTodosFeatureVisibility, this._toggleTodosFeatureVisibility], 113 [todoActions.handleClientMessage, this._handleClientMessage],
109 [todoActions.openDevTools, this._openDevTools], 114 [
110 [todoActions.reload, this._reload], 115 todoActions.toggleTodosFeatureVisibility,
111 ])); 116 this._toggleTodosFeatureVisibility,
117 ],
118 [todoActions.openDevTools, this._openDevTools],
119 [todoActions.reload, this._reload],
120 ]),
121 );
112 122
113 // REACTIONS 123 // REACTIONS
114 124
@@ -133,7 +143,7 @@ export default class TodoStore extends FeatureStore {
133 143
134 // ========== PRIVATE METHODS ========= // 144 // ========== PRIVATE METHODS ========= //
135 145
136 _updateSettings = (changes) => { 146 _updateSettings = changes => {
137 localStorage.setItem('todos', { 147 localStorage.setItem('todos', {
138 ...this.settings, 148 ...this.settings,
139 ...changes, 149 ...changes,
@@ -162,7 +172,7 @@ export default class TodoStore extends FeatureStore {
162 } 172 }
163 }; 173 };
164 174
165 @action _handleHostMessage = (message) => { 175 @action _handleHostMessage = message => {
166 debug('_handleHostMessage', message); 176 debug('_handleHostMessage', message);
167 if (message.action === 'todos:create') { 177 if (message.action === 'todos:create') {
168 this.webview.send(IPC.TODOS_HOST_CHANNEL, message); 178 this.webview.send(IPC.TODOS_HOST_CHANNEL, message);
@@ -172,11 +182,18 @@ export default class TodoStore extends FeatureStore {
172 @action _handleClientMessage = ({ channel, message = {} }) => { 182 @action _handleClientMessage = ({ channel, message = {} }) => {
173 debug('_handleClientMessage', channel, message); 183 debug('_handleClientMessage', channel, message);
174 switch (message.action) { 184 switch (message.action) {
175 case 'todos:initialized': this._onTodosClientInitialized(); break; 185 case 'todos:initialized':
176 case 'todos:goToService': this._goToService(message.data); break; 186 this._onTodosClientInitialized();
187 break;
188 case 'todos:goToService':
189 this._goToService(message.data);
190 break;
177 default: 191 default:
178 debug('Other message received', channel, message); 192 debug('Other message received', channel, message);
179 console.log('this.stores.services.isTodosServiceAdded', this.stores.services.isTodosServiceAdded); 193 console.log(
194 'this.stores.services.isTodosServiceAdded',
195 this.stores.services.isTodosServiceAdded,
196 );
180 if (this.stores.services.isTodosServiceAdded) { 197 if (this.stores.services.isTodosServiceAdded) {
181 this.actions.service.handleIPCMessage({ 198 this.actions.service.handleIPCMessage({
182 serviceId: this.stores.services.isTodosServiceAdded.id, 199 serviceId: this.stores.services.isTodosServiceAdded.id,
@@ -189,7 +206,7 @@ export default class TodoStore extends FeatureStore {
189 206
190 _handleNewWindowEvent = ({ url }) => { 207 _handleNewWindowEvent = ({ url }) => {
191 this.actions.app.openExternalUrl({ url }); 208 this.actions.app.openExternalUrl({ url });
192 } 209 };
193 210
194 @action _toggleTodosFeatureVisibility = () => { 211 @action _toggleTodosFeatureVisibility = () => {
195 debug('_toggleTodosFeatureVisibility'); 212 debug('_toggleTodosFeatureVisibility');
@@ -204,14 +221,14 @@ export default class TodoStore extends FeatureStore {
204 221
205 const webview = document.querySelector('#todos-panel webview'); 222 const webview = document.querySelector('#todos-panel webview');
206 if (webview) webview.openDevTools(); 223 if (webview) webview.openDevTools();
207 } 224 };
208 225
209 _reload = () => { 226 _reload = () => {
210 debug('_reload'); 227 debug('_reload');
211 228
212 const webview = document.querySelector('#todos-panel webview'); 229 const webview = document.querySelector('#todos-panel webview');
213 if (webview) webview.reload(); 230 if (webview) webview.reload();
214 } 231 };
215 232
216 // Todos client message handlers 233 // Todos client message handlers
217 234
@@ -291,5 +308,5 @@ export default class TodoStore extends FeatureStore {
291 }); 308 });
292 } 309 }
293 } 310 }
294 } 311 };
295} 312}
diff --git a/src/features/workspaces/components/WorkspacesDashboard.js b/src/features/workspaces/components/WorkspacesDashboard.js
index 8319d3bc6..5f34204f1 100644
--- a/src/features/workspaces/components/WorkspacesDashboard.js
+++ b/src/features/workspaces/components/WorkspacesDashboard.js
@@ -20,7 +20,7 @@ const messages = defineMessages({
20 }, 20 },
21 noServicesAdded: { 21 noServicesAdded: {
22 id: 'settings.workspaces.noWorkspacesAdded', 22 id: 'settings.workspaces.noWorkspacesAdded',
23 defaultMessage: '!!!You haven\'t created any workspaces yet.', 23 defaultMessage: "!!!You haven't created any workspaces yet.",
24 }, 24 },
25 workspacesRequestFailed: { 25 workspacesRequestFailed: {
26 id: 'settings.workspaces.workspacesRequestFailed', 26 id: 'settings.workspaces.workspacesRequestFailed',
@@ -61,9 +61,6 @@ const styles = () => ({
61 appear: { 61 appear: {
62 height: 'auto', 62 height: 'auto',
63 }, 63 },
64 announcementHeadline: {
65 marginBottom: 0,
66 },
67 teaserImage: { 64 teaserImage: {
68 width: 250, 65 width: 250,
69 margin: [-8, 0, 0, 20], 66 margin: [-8, 0, 0, 20],
@@ -71,7 +68,9 @@ const styles = () => ({
71 }, 68 },
72}); 69});
73 70
74@inject('stores') @injectSheet(styles) @observer 71@inject('stores')
72@injectSheet(styles)
73@observer
75class WorkspacesDashboard extends Component { 74class WorkspacesDashboard extends Component {
76 static propTypes = { 75 static propTypes = {
77 classes: PropTypes.object.isRequired, 76 classes: PropTypes.object.isRequired,
@@ -108,7 +107,6 @@ class WorkspacesDashboard extends Component {
108 <h1>{intl.formatMessage(messages.headline)}</h1> 107 <h1>{intl.formatMessage(messages.headline)}</h1>
109 </div> 108 </div>
110 <div className="settings__body"> 109 <div className="settings__body">
111
112 {/* ===== Workspace updated info ===== */} 110 {/* ===== Workspace updated info ===== */}
113 {updateWorkspaceRequest.wasExecuted && updateWorkspaceRequest.result && ( 111 {updateWorkspaceRequest.wasExecuted && updateWorkspaceRequest.result && (
114 <Appear className={classes.appear}> 112 <Appear className={classes.appear}>
@@ -175,11 +173,11 @@ class WorkspacesDashboard extends Component {
175 <table className={classes.table}> 173 <table className={classes.table}>
176 {/* ===== Workspaces list ===== */} 174 {/* ===== Workspaces list ===== */}
177 <tbody> 175 <tbody>
178 {workspaces.map((workspace) => ( 176 {workspaces.map(workspace => (
179 <WorkspaceItem 177 <WorkspaceItem
180 key={workspace.id} 178 key={workspace.id}
181 workspace={workspace} 179 workspace={workspace}
182 onItemClick={(w) => onWorkspaceClick(w)} 180 onItemClick={w => onWorkspaceClick(w)}
183 /> 181 />
184 ))} 182 ))}
185 </tbody> 183 </tbody>
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json
index 7aff45503..537aa7236 100644
--- a/src/i18n/locales/defaultMessages.json
+++ b/src/i18n/locales/defaultMessages.json
@@ -5,39 +5,39 @@
5 "defaultMessage": "!!!A new update for Ferdi is available.", 5 "defaultMessage": "!!!A new update for Ferdi is available.",
6 "end": { 6 "end": {
7 "column": 3, 7 "column": 3,
8 "line": 12 8 "line": 13
9 }, 9 },
10 "file": "src/components/AppUpdateInfoBar.js", 10 "file": "src/components/AppUpdateInfoBar.js",
11 "id": "infobar.updateAvailable", 11 "id": "infobar.updateAvailable",
12 "start": { 12 "start": {
13 "column": 19, 13 "column": 19,
14 "line": 9 14 "line": 10
15 } 15 }
16 }, 16 },
17 { 17 {
18 "defaultMessage": "!!!Changelog", 18 "defaultMessage": "!!!Changelog",
19 "end": { 19 "end": {
20 "column": 3, 20 "column": 3,
21 "line": 16 21 "line": 17
22 }, 22 },
23 "file": "src/components/AppUpdateInfoBar.js", 23 "file": "src/components/AppUpdateInfoBar.js",
24 "id": "infobar.buttonChangelog", 24 "id": "infobar.buttonChangelog",
25 "start": { 25 "start": {
26 "column": 13, 26 "column": 13,
27 "line": 13 27 "line": 14
28 } 28 }
29 }, 29 },
30 { 30 {
31 "defaultMessage": "!!!Restart & install update", 31 "defaultMessage": "!!!Restart & install update",
32 "end": { 32 "end": {
33 "column": 3, 33 "column": 3,
34 "line": 20 34 "line": 21
35 }, 35 },
36 "file": "src/components/AppUpdateInfoBar.js", 36 "file": "src/components/AppUpdateInfoBar.js",
37 "id": "infobar.buttonInstallUpdate", 37 "id": "infobar.buttonInstallUpdate",
38 "start": { 38 "start": {
39 "column": 23, 39 "column": 23,
40 "line": 17 40 "line": 18
41 } 41 }
42 } 42 }
43 ], 43 ],
@@ -4962,24 +4962,6 @@
4962 { 4962 {
4963 "descriptors": [ 4963 "descriptors": [
4964 { 4964 {
4965 "defaultMessage": "!!!Changes in Ferdi {version}",
4966 "end": {
4967 "column": 3,
4968 "line": 23
4969 },
4970 "file": "src/features/announcements/components/AnnouncementScreen.js",
4971 "id": "feature.announcements.changelog.headline",
4972 "start": {
4973 "column": 12,
4974 "line": 20
4975 }
4976 }
4977 ],
4978 "path": "src/features/announcements/components/AnnouncementScreen.json"
4979 },
4980 {
4981 "descriptors": [
4982 {
4983 "defaultMessage": "!!!Publish debugging information", 4965 "defaultMessage": "!!!Publish debugging information",
4984 "end": { 4966 "end": {
4985 "column": 3, 4967 "column": 3,
@@ -5898,949 +5880,936 @@
5898 "defaultMessage": "!!!Edit", 5880 "defaultMessage": "!!!Edit",
5899 "end": { 5881 "end": {
5900 "column": 3, 5882 "column": 3,
5901 "line": 22 5883 "line": 35
5902 }, 5884 },
5903 "file": "src/lib/Menu.js", 5885 "file": "src/lib/Menu.js",
5904 "id": "menu.edit", 5886 "id": "menu.edit",
5905 "start": { 5887 "start": {
5906 "column": 8, 5888 "column": 8,
5907 "line": 19 5889 "line": 32
5908 } 5890 }
5909 }, 5891 },
5910 { 5892 {
5911 "defaultMessage": "!!!Undo", 5893 "defaultMessage": "!!!Undo",
5912 "end": { 5894 "end": {
5913 "column": 3, 5895 "column": 3,
5914 "line": 26 5896 "line": 39
5915 }, 5897 },
5916 "file": "src/lib/Menu.js", 5898 "file": "src/lib/Menu.js",
5917 "id": "menu.edit.undo", 5899 "id": "menu.edit.undo",
5918 "start": { 5900 "start": {
5919 "column": 8, 5901 "column": 8,
5920 "line": 23 5902 "line": 36
5921 } 5903 }
5922 }, 5904 },
5923 { 5905 {
5924 "defaultMessage": "!!!Redo", 5906 "defaultMessage": "!!!Redo",
5925 "end": { 5907 "end": {
5926 "column": 3, 5908 "column": 3,
5927 "line": 30 5909 "line": 43
5928 }, 5910 },
5929 "file": "src/lib/Menu.js", 5911 "file": "src/lib/Menu.js",
5930 "id": "menu.edit.redo", 5912 "id": "menu.edit.redo",
5931 "start": { 5913 "start": {
5932 "column": 8, 5914 "column": 8,
5933 "line": 27 5915 "line": 40
5934 } 5916 }
5935 }, 5917 },
5936 { 5918 {
5937 "defaultMessage": "!!!Cut", 5919 "defaultMessage": "!!!Cut",
5938 "end": { 5920 "end": {
5939 "column": 3, 5921 "column": 3,
5940 "line": 34 5922 "line": 47
5941 }, 5923 },
5942 "file": "src/lib/Menu.js", 5924 "file": "src/lib/Menu.js",
5943 "id": "menu.edit.cut", 5925 "id": "menu.edit.cut",
5944 "start": { 5926 "start": {
5945 "column": 7, 5927 "column": 7,
5946 "line": 31 5928 "line": 44
5947 } 5929 }
5948 }, 5930 },
5949 { 5931 {
5950 "defaultMessage": "!!!Copy", 5932 "defaultMessage": "!!!Copy",
5951 "end": { 5933 "end": {
5952 "column": 3, 5934 "column": 3,
5953 "line": 38 5935 "line": 51
5954 }, 5936 },
5955 "file": "src/lib/Menu.js", 5937 "file": "src/lib/Menu.js",
5956 "id": "menu.edit.copy", 5938 "id": "menu.edit.copy",
5957 "start": { 5939 "start": {
5958 "column": 8, 5940 "column": 8,
5959 "line": 35 5941 "line": 48
5960 } 5942 }
5961 }, 5943 },
5962 { 5944 {
5963 "defaultMessage": "!!!Paste", 5945 "defaultMessage": "!!!Paste",
5964 "end": { 5946 "end": {
5965 "column": 3, 5947 "column": 3,
5966 "line": 42 5948 "line": 55
5967 }, 5949 },
5968 "file": "src/lib/Menu.js", 5950 "file": "src/lib/Menu.js",
5969 "id": "menu.edit.paste", 5951 "id": "menu.edit.paste",
5970 "start": { 5952 "start": {
5971 "column": 9, 5953 "column": 9,
5972 "line": 39 5954 "line": 52
5973 } 5955 }
5974 }, 5956 },
5975 { 5957 {
5976 "defaultMessage": "!!!Paste And Match Style", 5958 "defaultMessage": "!!!Paste And Match Style",
5977 "end": { 5959 "end": {
5978 "column": 3, 5960 "column": 3,
5979 "line": 46 5961 "line": 59
5980 }, 5962 },
5981 "file": "src/lib/Menu.js", 5963 "file": "src/lib/Menu.js",
5982 "id": "menu.edit.pasteAndMatchStyle", 5964 "id": "menu.edit.pasteAndMatchStyle",
5983 "start": { 5965 "start": {
5984 "column": 22, 5966 "column": 22,
5985 "line": 43 5967 "line": 56
5986 } 5968 }
5987 }, 5969 },
5988 { 5970 {
5989 "defaultMessage": "!!!Delete", 5971 "defaultMessage": "!!!Delete",
5990 "end": { 5972 "end": {
5991 "column": 3, 5973 "column": 3,
5992 "line": 50 5974 "line": 63
5993 }, 5975 },
5994 "file": "src/lib/Menu.js", 5976 "file": "src/lib/Menu.js",
5995 "id": "menu.edit.delete", 5977 "id": "menu.edit.delete",
5996 "start": { 5978 "start": {
5997 "column": 10, 5979 "column": 10,
5998 "line": 47 5980 "line": 60
5999 } 5981 }
6000 }, 5982 },
6001 { 5983 {
6002 "defaultMessage": "!!!Select All", 5984 "defaultMessage": "!!!Select All",
6003 "end": { 5985 "end": {
6004 "column": 3, 5986 "column": 3,
6005 "line": 54 5987 "line": 67
6006 }, 5988 },
6007 "file": "src/lib/Menu.js", 5989 "file": "src/lib/Menu.js",
6008 "id": "menu.edit.selectAll", 5990 "id": "menu.edit.selectAll",
6009 "start": { 5991 "start": {
6010 "column": 13, 5992 "column": 13,
6011 "line": 51 5993 "line": 64
6012 } 5994 }
6013 }, 5995 },
6014 { 5996 {
6015 "defaultMessage": "!!!Find in Page", 5997 "defaultMessage": "!!!Find in Page",
6016 "end": { 5998 "end": {
6017 "column": 3, 5999 "column": 3,
6018 "line": 58 6000 "line": 71
6019 }, 6001 },
6020 "file": "src/lib/Menu.js", 6002 "file": "src/lib/Menu.js",
6021 "id": "menu.edit.findInPage", 6003 "id": "menu.edit.findInPage",
6022 "start": { 6004 "start": {
6023 "column": 14, 6005 "column": 14,
6024 "line": 55 6006 "line": 68
6025 } 6007 }
6026 }, 6008 },
6027 { 6009 {
6028 "defaultMessage": "!!!Speech", 6010 "defaultMessage": "!!!Speech",
6029 "end": { 6011 "end": {
6030 "column": 3, 6012 "column": 3,
6031 "line": 62 6013 "line": 75
6032 }, 6014 },
6033 "file": "src/lib/Menu.js", 6015 "file": "src/lib/Menu.js",
6034 "id": "menu.edit.speech", 6016 "id": "menu.edit.speech",
6035 "start": { 6017 "start": {
6036 "column": 10, 6018 "column": 10,
6037 "line": 59 6019 "line": 72
6038 } 6020 }
6039 }, 6021 },
6040 { 6022 {
6041 "defaultMessage": "!!!Start Speaking", 6023 "defaultMessage": "!!!Start Speaking",
6042 "end": { 6024 "end": {
6043 "column": 3, 6025 "column": 3,
6044 "line": 66 6026 "line": 79
6045 }, 6027 },
6046 "file": "src/lib/Menu.js", 6028 "file": "src/lib/Menu.js",
6047 "id": "menu.edit.startSpeaking", 6029 "id": "menu.edit.startSpeaking",
6048 "start": { 6030 "start": {
6049 "column": 17, 6031 "column": 17,
6050 "line": 63 6032 "line": 76
6051 } 6033 }
6052 }, 6034 },
6053 { 6035 {
6054 "defaultMessage": "!!!Stop Speaking", 6036 "defaultMessage": "!!!Stop Speaking",
6055 "end": { 6037 "end": {
6056 "column": 3, 6038 "column": 3,
6057 "line": 70 6039 "line": 83
6058 }, 6040 },
6059 "file": "src/lib/Menu.js", 6041 "file": "src/lib/Menu.js",
6060 "id": "menu.edit.stopSpeaking", 6042 "id": "menu.edit.stopSpeaking",
6061 "start": { 6043 "start": {
6062 "column": 16, 6044 "column": 16,
6063 "line": 67 6045 "line": 80
6064 } 6046 }
6065 }, 6047 },
6066 { 6048 {
6067 "defaultMessage": "!!!Start Dictation", 6049 "defaultMessage": "!!!Start Dictation",
6068 "end": { 6050 "end": {
6069 "column": 3, 6051 "column": 3,
6070 "line": 74 6052 "line": 87
6071 }, 6053 },
6072 "file": "src/lib/Menu.js", 6054 "file": "src/lib/Menu.js",
6073 "id": "menu.edit.startDictation", 6055 "id": "menu.edit.startDictation",
6074 "start": { 6056 "start": {
6075 "column": 18, 6057 "column": 18,
6076 "line": 71 6058 "line": 84
6077 } 6059 }
6078 }, 6060 },
6079 { 6061 {
6080 "defaultMessage": "!!!Emoji & Symbols", 6062 "defaultMessage": "!!!Emoji & Symbols",
6081 "end": { 6063 "end": {
6082 "column": 3, 6064 "column": 3,
6083 "line": 78 6065 "line": 91
6084 }, 6066 },
6085 "file": "src/lib/Menu.js", 6067 "file": "src/lib/Menu.js",
6086 "id": "menu.edit.emojiSymbols", 6068 "id": "menu.edit.emojiSymbols",
6087 "start": { 6069 "start": {
6088 "column": 16, 6070 "column": 16,
6089 "line": 75 6071 "line": 88
6090 } 6072 }
6091 }, 6073 },
6092 { 6074 {
6093 "defaultMessage": "!!!Open Quick Switch", 6075 "defaultMessage": "!!!Open Quick Switch",
6094 "end": { 6076 "end": {
6095 "column": 3, 6077 "column": 3,
6096 "line": 82 6078 "line": 95
6097 }, 6079 },
6098 "file": "src/lib/Menu.js", 6080 "file": "src/lib/Menu.js",
6099 "id": "menu.view.openQuickSwitch", 6081 "id": "menu.view.openQuickSwitch",
6100 "start": { 6082 "start": {
6101 "column": 19, 6083 "column": 19,
6102 "line": 79 6084 "line": 92
6103 } 6085 }
6104 }, 6086 },
6105 { 6087 {
6106 "defaultMessage": "!!!Back", 6088 "defaultMessage": "!!!Back",
6107 "end": { 6089 "end": {
6108 "column": 3, 6090 "column": 3,
6109 "line": 86 6091 "line": 99
6110 }, 6092 },
6111 "file": "src/lib/Menu.js", 6093 "file": "src/lib/Menu.js",
6112 "id": "menu.view.back", 6094 "id": "menu.view.back",
6113 "start": { 6095 "start": {
6114 "column": 8, 6096 "column": 8,
6115 "line": 83 6097 "line": 96
6116 } 6098 }
6117 }, 6099 },
6118 { 6100 {
6119 "defaultMessage": "!!!Forward", 6101 "defaultMessage": "!!!Forward",
6120 "end": { 6102 "end": {
6121 "column": 3, 6103 "column": 3,
6122 "line": 90 6104 "line": 103
6123 }, 6105 },
6124 "file": "src/lib/Menu.js", 6106 "file": "src/lib/Menu.js",
6125 "id": "menu.view.forward", 6107 "id": "menu.view.forward",
6126 "start": { 6108 "start": {
6127 "column": 11, 6109 "column": 11,
6128 "line": 87 6110 "line": 100
6129 } 6111 }
6130 }, 6112 },
6131 { 6113 {
6132 "defaultMessage": "!!!Actual Size", 6114 "defaultMessage": "!!!Actual Size",
6133 "end": { 6115 "end": {
6134 "column": 3, 6116 "column": 3,
6135 "line": 94 6117 "line": 107
6136 }, 6118 },
6137 "file": "src/lib/Menu.js", 6119 "file": "src/lib/Menu.js",
6138 "id": "menu.view.resetZoom", 6120 "id": "menu.view.resetZoom",
6139 "start": { 6121 "start": {
6140 "column": 13, 6122 "column": 13,
6141 "line": 91 6123 "line": 104
6142 } 6124 }
6143 }, 6125 },
6144 { 6126 {
6145 "defaultMessage": "!!!Zoom In", 6127 "defaultMessage": "!!!Zoom In",
6146 "end": { 6128 "end": {
6147 "column": 3, 6129 "column": 3,
6148 "line": 98 6130 "line": 111
6149 }, 6131 },
6150 "file": "src/lib/Menu.js", 6132 "file": "src/lib/Menu.js",
6151 "id": "menu.view.zoomIn", 6133 "id": "menu.view.zoomIn",
6152 "start": { 6134 "start": {
6153 "column": 10, 6135 "column": 10,
6154 "line": 95 6136 "line": 108
6155 } 6137 }
6156 }, 6138 },
6157 { 6139 {
6158 "defaultMessage": "!!!Zoom Out", 6140 "defaultMessage": "!!!Zoom Out",
6159 "end": { 6141 "end": {
6160 "column": 3, 6142 "column": 3,
6161 "line": 102 6143 "line": 115
6162 }, 6144 },
6163 "file": "src/lib/Menu.js", 6145 "file": "src/lib/Menu.js",
6164 "id": "menu.view.zoomOut", 6146 "id": "menu.view.zoomOut",
6165 "start": { 6147 "start": {
6166 "column": 11, 6148 "column": 11,
6167 "line": 99 6149 "line": 112
6168 } 6150 }
6169 }, 6151 },
6170 { 6152 {
6171 "defaultMessage": "!!!Toggle Full Screen", 6153 "defaultMessage": "!!!Toggle Full Screen",
6172 "end": { 6154 "end": {
6173 "column": 3, 6155 "column": 3,
6174 "line": 106 6156 "line": 119
6175 }, 6157 },
6176 "file": "src/lib/Menu.js", 6158 "file": "src/lib/Menu.js",
6177 "id": "menu.view.toggleFullScreen", 6159 "id": "menu.view.toggleFullScreen",
6178 "start": { 6160 "start": {
6179 "column": 20, 6161 "column": 20,
6180 "line": 103 6162 "line": 116
6181 } 6163 }
6182 }, 6164 },
6183 { 6165 {
6184 "defaultMessage": "!!!Toggle Dark Mode", 6166 "defaultMessage": "!!!Toggle Dark Mode",
6185 "end": { 6167 "end": {
6186 "column": 3, 6168 "column": 3,
6187 "line": 110 6169 "line": 123
6188 }, 6170 },
6189 "file": "src/lib/Menu.js", 6171 "file": "src/lib/Menu.js",
6190 "id": "menu.view.toggleDarkMode", 6172 "id": "menu.view.toggleDarkMode",
6191 "start": { 6173 "start": {
6192 "column": 18, 6174 "column": 18,
6193 "line": 107 6175 "line": 120
6194 } 6176 }
6195 }, 6177 },
6196 { 6178 {
6197 "defaultMessage": "!!!Toggle Developer Tools", 6179 "defaultMessage": "!!!Toggle Developer Tools",
6198 "end": { 6180 "end": {
6199 "column": 3, 6181 "column": 3,
6200 "line": 114 6182 "line": 127
6201 }, 6183 },
6202 "file": "src/lib/Menu.js", 6184 "file": "src/lib/Menu.js",
6203 "id": "menu.view.toggleDevTools", 6185 "id": "menu.view.toggleDevTools",
6204 "start": { 6186 "start": {
6205 "column": 18, 6187 "column": 18,
6206 "line": 111 6188 "line": 124
6207 } 6189 }
6208 }, 6190 },
6209 { 6191 {
6210 "defaultMessage": "!!!Toggle Todos Developer Tools", 6192 "defaultMessage": "!!!Toggle Todos Developer Tools",
6211 "end": { 6193 "end": {
6212 "column": 3, 6194 "column": 3,
6213 "line": 118 6195 "line": 131
6214 }, 6196 },
6215 "file": "src/lib/Menu.js", 6197 "file": "src/lib/Menu.js",
6216 "id": "menu.view.toggleTodosDevTools", 6198 "id": "menu.view.toggleTodosDevTools",
6217 "start": { 6199 "start": {
6218 "column": 23, 6200 "column": 23,
6219 "line": 115 6201 "line": 128
6220 } 6202 }
6221 }, 6203 },
6222 { 6204 {
6223 "defaultMessage": "!!!Toggle Service Developer Tools", 6205 "defaultMessage": "!!!Toggle Service Developer Tools",
6224 "end": { 6206 "end": {
6225 "column": 3, 6207 "column": 3,
6226 "line": 122 6208 "line": 135
6227 }, 6209 },
6228 "file": "src/lib/Menu.js", 6210 "file": "src/lib/Menu.js",
6229 "id": "menu.view.toggleServiceDevTools", 6211 "id": "menu.view.toggleServiceDevTools",
6230 "start": { 6212 "start": {
6231 "column": 25, 6213 "column": 25,
6232 "line": 119 6214 "line": 132
6233 } 6215 }
6234 }, 6216 },
6235 { 6217 {
6236 "defaultMessage": "!!!Reload Service", 6218 "defaultMessage": "!!!Reload Service",
6237 "end": { 6219 "end": {
6238 "column": 3, 6220 "column": 3,
6239 "line": 126 6221 "line": 139
6240 }, 6222 },
6241 "file": "src/lib/Menu.js", 6223 "file": "src/lib/Menu.js",
6242 "id": "menu.view.reloadService", 6224 "id": "menu.view.reloadService",
6243 "start": { 6225 "start": {
6244 "column": 17, 6226 "column": 17,
6245 "line": 123 6227 "line": 136
6246 } 6228 }
6247 }, 6229 },
6248 { 6230 {
6249 "defaultMessage": "!!!Reload Ferdi", 6231 "defaultMessage": "!!!Reload Ferdi",
6250 "end": { 6232 "end": {
6251 "column": 3, 6233 "column": 3,
6252 "line": 130 6234 "line": 143
6253 }, 6235 },
6254 "file": "src/lib/Menu.js", 6236 "file": "src/lib/Menu.js",
6255 "id": "menu.view.reloadFerdi", 6237 "id": "menu.view.reloadFerdi",
6256 "start": { 6238 "start": {
6257 "column": 15, 6239 "column": 15,
6258 "line": 127 6240 "line": 140
6259 } 6241 }
6260 }, 6242 },
6261 { 6243 {
6262 "defaultMessage": "!!!Lock Ferdi", 6244 "defaultMessage": "!!!Lock Ferdi",
6263 "end": { 6245 "end": {
6264 "column": 3, 6246 "column": 3,
6265 "line": 134 6247 "line": 147
6266 }, 6248 },
6267 "file": "src/lib/Menu.js", 6249 "file": "src/lib/Menu.js",
6268 "id": "menu.view.lockFerdi", 6250 "id": "menu.view.lockFerdi",
6269 "start": { 6251 "start": {
6270 "column": 13, 6252 "column": 13,
6271 "line": 131 6253 "line": 144
6272 } 6254 }
6273 }, 6255 },
6274 { 6256 {
6275 "defaultMessage": "!!!Reload ToDos", 6257 "defaultMessage": "!!!Reload ToDos",
6276 "end": { 6258 "end": {
6277 "column": 3, 6259 "column": 3,
6278 "line": 138 6260 "line": 151
6279 }, 6261 },
6280 "file": "src/lib/Menu.js", 6262 "file": "src/lib/Menu.js",
6281 "id": "menu.view.reloadTodos", 6263 "id": "menu.view.reloadTodos",
6282 "start": { 6264 "start": {
6283 "column": 15, 6265 "column": 15,
6284 "line": 135 6266 "line": 148
6285 } 6267 }
6286 }, 6268 },
6287 { 6269 {
6288 "defaultMessage": "!!!Minimize", 6270 "defaultMessage": "!!!Minimize",
6289 "end": { 6271 "end": {
6290 "column": 3, 6272 "column": 3,
6291 "line": 142 6273 "line": 155
6292 }, 6274 },
6293 "file": "src/lib/Menu.js", 6275 "file": "src/lib/Menu.js",
6294 "id": "menu.window.minimize", 6276 "id": "menu.window.minimize",
6295 "start": { 6277 "start": {
6296 "column": 12, 6278 "column": 12,
6297 "line": 139 6279 "line": 152
6298 } 6280 }
6299 }, 6281 },
6300 { 6282 {
6301 "defaultMessage": "!!!Close", 6283 "defaultMessage": "!!!Close",
6302 "end": { 6284 "end": {
6303 "column": 3, 6285 "column": 3,
6304 "line": 146 6286 "line": 159
6305 }, 6287 },
6306 "file": "src/lib/Menu.js", 6288 "file": "src/lib/Menu.js",
6307 "id": "menu.window.close", 6289 "id": "menu.window.close",
6308 "start": { 6290 "start": {
6309 "column": 9, 6291 "column": 9,
6310 "line": 143 6292 "line": 156
6311 } 6293 }
6312 }, 6294 },
6313 { 6295 {
6314 "defaultMessage": "!!!Learn More", 6296 "defaultMessage": "!!!Learn More",
6315 "end": { 6297 "end": {
6316 "column": 3, 6298 "column": 3,
6317 "line": 150 6299 "line": 163
6318 }, 6300 },
6319 "file": "src/lib/Menu.js", 6301 "file": "src/lib/Menu.js",
6320 "id": "menu.help.learnMore", 6302 "id": "menu.help.learnMore",
6321 "start": { 6303 "start": {
6322 "column": 13, 6304 "column": 13,
6323 "line": 147 6305 "line": 160
6324 } 6306 }
6325 }, 6307 },
6326 { 6308 {
6327 "defaultMessage": "!!!Changelog", 6309 "defaultMessage": "!!!Changelog",
6328 "end": { 6310 "end": {
6329 "column": 3, 6311 "column": 3,
6330 "line": 154 6312 "line": 167
6331 }, 6313 },
6332 "file": "src/lib/Menu.js", 6314 "file": "src/lib/Menu.js",
6333 "id": "menu.help.changelog", 6315 "id": "menu.help.changelog",
6334 "start": { 6316 "start": {
6335 "column": 13, 6317 "column": 13,
6336 "line": 151 6318 "line": 164
6337 } 6319 }
6338 }, 6320 },
6339 { 6321 {
6340 "defaultMessage": "!!!Import/Export Configuration Data", 6322 "defaultMessage": "!!!Import/Export Configuration Data",
6341 "end": { 6323 "end": {
6342 "column": 3, 6324 "column": 3,
6343 "line": 158 6325 "line": 171
6344 }, 6326 },
6345 "file": "src/lib/Menu.js", 6327 "file": "src/lib/Menu.js",
6346 "id": "menu.help.importExportData", 6328 "id": "menu.help.importExportData",
6347 "start": { 6329 "start": {
6348 "column": 20, 6330 "column": 20,
6349 "line": 155 6331 "line": 168
6350 } 6332 }
6351 }, 6333 },
6352 { 6334 {
6353 "defaultMessage": "!!!Support", 6335 "defaultMessage": "!!!Support",
6354 "end": { 6336 "end": {
6355 "column": 3, 6337 "column": 3,
6356 "line": 162 6338 "line": 175
6357 }, 6339 },
6358 "file": "src/lib/Menu.js", 6340 "file": "src/lib/Menu.js",
6359 "id": "menu.help.support", 6341 "id": "menu.help.support",
6360 "start": { 6342 "start": {
6361 "column": 11, 6343 "column": 11,
6362 "line": 159 6344 "line": 172
6363 } 6345 }
6364 }, 6346 },
6365 { 6347 {
6366 "defaultMessage": "!!!Copy Debug Information", 6348 "defaultMessage": "!!!Copy Debug Information",
6367 "end": { 6349 "end": {
6368 "column": 3, 6350 "column": 3,
6369 "line": 166 6351 "line": 179
6370 }, 6352 },
6371 "file": "src/lib/Menu.js", 6353 "file": "src/lib/Menu.js",
6372 "id": "menu.help.debugInfo", 6354 "id": "menu.help.debugInfo",
6373 "start": { 6355 "start": {
6374 "column": 13, 6356 "column": 13,
6375 "line": 163 6357 "line": 176
6376 } 6358 }
6377 }, 6359 },
6378 { 6360 {
6379 "defaultMessage": "!!!Publish Debug Information", 6361 "defaultMessage": "!!!Publish Debug Information",
6380 "end": { 6362 "end": {
6381 "column": 3, 6363 "column": 3,
6382 "line": 170 6364 "line": 183
6383 }, 6365 },
6384 "file": "src/lib/Menu.js", 6366 "file": "src/lib/Menu.js",
6385 "id": "menu.help.publishDebugInfo", 6367 "id": "menu.help.publishDebugInfo",
6386 "start": { 6368 "start": {
6387 "column": 20, 6369 "column": 20,
6388 "line": 167 6370 "line": 180
6389 } 6371 }
6390 }, 6372 },
6391 { 6373 {
6392 "defaultMessage": "!!!Ferdi Debug Information", 6374 "defaultMessage": "!!!Ferdi Debug Information",
6393 "end": { 6375 "end": {
6394 "column": 3, 6376 "column": 3,
6395 "line": 174 6377 "line": 187
6396 }, 6378 },
6397 "file": "src/lib/Menu.js", 6379 "file": "src/lib/Menu.js",
6398 "id": "menu.help.debugInfoCopiedHeadline", 6380 "id": "menu.help.debugInfoCopiedHeadline",
6399 "start": { 6381 "start": {
6400 "column": 27, 6382 "column": 27,
6401 "line": 171 6383 "line": 184
6402 } 6384 }
6403 }, 6385 },
6404 { 6386 {
6405 "defaultMessage": "!!!Your Debug Information has been copied to your clipboard.", 6387 "defaultMessage": "!!!Your Debug Information has been copied to your clipboard.",
6406 "end": { 6388 "end": {
6407 "column": 3, 6389 "column": 3,
6408 "line": 178 6390 "line": 192
6409 }, 6391 },
6410 "file": "src/lib/Menu.js", 6392 "file": "src/lib/Menu.js",
6411 "id": "menu.help.debugInfoCopiedBody", 6393 "id": "menu.help.debugInfoCopiedBody",
6412 "start": { 6394 "start": {
6413 "column": 23, 6395 "column": 23,
6414 "line": 175 6396 "line": 188
6415 } 6397 }
6416 }, 6398 },
6417 { 6399 {
6418 "defaultMessage": "!!!Unlock with Touch ID", 6400 "defaultMessage": "!!!Unlock with Touch ID",
6419 "end": { 6401 "end": {
6420 "column": 3, 6402 "column": 3,
6421 "line": 182 6403 "line": 196
6422 }, 6404 },
6423 "file": "src/lib/Menu.js", 6405 "file": "src/lib/Menu.js",
6424 "id": "locked.touchId", 6406 "id": "locked.touchId",
6425 "start": { 6407 "start": {
6426 "column": 11, 6408 "column": 11,
6427 "line": 179 6409 "line": 193
6428 } 6410 }
6429 }, 6411 },
6430 { 6412 {
6431 "defaultMessage": "!!!unlock via Touch ID", 6413 "defaultMessage": "!!!unlock via Touch ID",
6432 "end": { 6414 "end": {
6433 "column": 3, 6415 "column": 3,
6434 "line": 186 6416 "line": 200
6435 }, 6417 },
6436 "file": "src/lib/Menu.js", 6418 "file": "src/lib/Menu.js",
6437 "id": "locked.touchIdPrompt", 6419 "id": "locked.touchIdPrompt",
6438 "start": { 6420 "start": {
6439 "column": 17, 6421 "column": 17,
6440 "line": 183 6422 "line": 197
6441 } 6423 }
6442 }, 6424 },
6443 { 6425 {
6444 "defaultMessage": "!!!Terms of Service", 6426 "defaultMessage": "!!!Terms of Service",
6445 "end": { 6427 "end": {
6446 "column": 3, 6428 "column": 3,
6447 "line": 190 6429 "line": 204
6448 }, 6430 },
6449 "file": "src/lib/Menu.js", 6431 "file": "src/lib/Menu.js",
6450 "id": "menu.help.tos", 6432 "id": "menu.help.tos",
6451 "start": { 6433 "start": {
6452 "column": 7, 6434 "column": 7,
6453 "line": 187 6435 "line": 201
6454 } 6436 }
6455 }, 6437 },
6456 { 6438 {
6457 "defaultMessage": "!!!Privacy Statement", 6439 "defaultMessage": "!!!Privacy Statement",
6458 "end": { 6440 "end": {
6459 "column": 3, 6441 "column": 3,
6460 "line": 194 6442 "line": 208
6461 }, 6443 },
6462 "file": "src/lib/Menu.js", 6444 "file": "src/lib/Menu.js",
6463 "id": "menu.help.privacy", 6445 "id": "menu.help.privacy",
6464 "start": { 6446 "start": {
6465 "column": 11, 6447 "column": 11,
6466 "line": 191 6448 "line": 205
6467 } 6449 }
6468 }, 6450 },
6469 { 6451 {
6470 "defaultMessage": "!!!File", 6452 "defaultMessage": "!!!File",
6471 "end": { 6453 "end": {
6472 "column": 3, 6454 "column": 3,
6473 "line": 198 6455 "line": 212
6474 }, 6456 },
6475 "file": "src/lib/Menu.js", 6457 "file": "src/lib/Menu.js",
6476 "id": "menu.file", 6458 "id": "menu.file",
6477 "start": { 6459 "start": {
6478 "column": 8, 6460 "column": 8,
6479 "line": 195 6461 "line": 209
6480 } 6462 }
6481 }, 6463 },
6482 { 6464 {
6483 "defaultMessage": "!!!View", 6465 "defaultMessage": "!!!View",
6484 "end": { 6466 "end": {
6485 "column": 3, 6467 "column": 3,
6486 "line": 202 6468 "line": 216
6487 }, 6469 },
6488 "file": "src/lib/Menu.js", 6470 "file": "src/lib/Menu.js",
6489 "id": "menu.view", 6471 "id": "menu.view",
6490 "start": { 6472 "start": {
6491 "column": 8, 6473 "column": 8,
6492 "line": 199 6474 "line": 213
6493 } 6475 }
6494 }, 6476 },
6495 { 6477 {
6496 "defaultMessage": "!!!Services", 6478 "defaultMessage": "!!!Services",
6497 "end": { 6479 "end": {
6498 "column": 3, 6480 "column": 3,
6499 "line": 206 6481 "line": 220
6500 }, 6482 },
6501 "file": "src/lib/Menu.js", 6483 "file": "src/lib/Menu.js",
6502 "id": "menu.services", 6484 "id": "menu.services",
6503 "start": { 6485 "start": {
6504 "column": 12, 6486 "column": 12,
6505 "line": 203 6487 "line": 217
6506 } 6488 }
6507 }, 6489 },
6508 { 6490 {
6509 "defaultMessage": "!!!Window", 6491 "defaultMessage": "!!!Window",
6510 "end": { 6492 "end": {
6511 "column": 3, 6493 "column": 3,
6512 "line": 210 6494 "line": 224
6513 }, 6495 },
6514 "file": "src/lib/Menu.js", 6496 "file": "src/lib/Menu.js",
6515 "id": "menu.window", 6497 "id": "menu.window",
6516 "start": { 6498 "start": {
6517 "column": 10, 6499 "column": 10,
6518 "line": 207 6500 "line": 221
6519 } 6501 }
6520 }, 6502 },
6521 { 6503 {
6522 "defaultMessage": "!!!Help", 6504 "defaultMessage": "!!!Help",
6523 "end": { 6505 "end": {
6524 "column": 3, 6506 "column": 3,
6525 "line": 214 6507 "line": 228
6526 }, 6508 },
6527 "file": "src/lib/Menu.js", 6509 "file": "src/lib/Menu.js",
6528 "id": "menu.help", 6510 "id": "menu.help",
6529 "start": { 6511 "start": {
6530 "column": 8, 6512 "column": 8,
6531 "line": 211 6513 "line": 225
6532 } 6514 }
6533 }, 6515 },
6534 { 6516 {
6535 "defaultMessage": "!!!About Ferdi", 6517 "defaultMessage": "!!!About Ferdi",
6536 "end": { 6518 "end": {
6537 "column": 3, 6519 "column": 3,
6538 "line": 218 6520 "line": 232
6539 }, 6521 },
6540 "file": "src/lib/Menu.js", 6522 "file": "src/lib/Menu.js",
6541 "id": "menu.app.about", 6523 "id": "menu.app.about",
6542 "start": { 6524 "start": {
6543 "column": 9, 6525 "column": 9,
6544 "line": 215 6526 "line": 229
6545 }
6546 },
6547 {
6548 "defaultMessage": "!!!What's new?",
6549 "end": {
6550 "column": 3,
6551 "line": 222
6552 },
6553 "file": "src/lib/Menu.js",
6554 "id": "menu.app.announcement",
6555 "start": {
6556 "column": 16,
6557 "line": 219
6558 } 6527 }
6559 }, 6528 },
6560 { 6529 {
6561 "defaultMessage": "!!!Settings", 6530 "defaultMessage": "!!!Settings",
6562 "end": { 6531 "end": {
6563 "column": 3, 6532 "column": 3,
6564 "line": 226 6533 "line": 236
6565 }, 6534 },
6566 "file": "src/lib/Menu.js", 6535 "file": "src/lib/Menu.js",
6567 "id": "menu.app.settings", 6536 "id": "menu.app.settings",
6568 "start": { 6537 "start": {
6569 "column": 12, 6538 "column": 12,
6570 "line": 223 6539 "line": 233
6571 } 6540 }
6572 }, 6541 },
6573 { 6542 {
6574 "defaultMessage": "!!!Check for updates", 6543 "defaultMessage": "!!!Check for updates",
6575 "end": { 6544 "end": {
6576 "column": 3, 6545 "column": 3,
6577 "line": 230 6546 "line": 240
6578 }, 6547 },
6579 "file": "src/lib/Menu.js", 6548 "file": "src/lib/Menu.js",
6580 "id": "menu.app.checkForUpdates", 6549 "id": "menu.app.checkForUpdates",
6581 "start": { 6550 "start": {
6582 "column": 19, 6551 "column": 19,
6583 "line": 227 6552 "line": 237
6584 } 6553 }
6585 }, 6554 },
6586 { 6555 {
6587 "defaultMessage": "!!!Hide", 6556 "defaultMessage": "!!!Hide",
6588 "end": { 6557 "end": {
6589 "column": 3, 6558 "column": 3,
6590 "line": 234 6559 "line": 244
6591 }, 6560 },
6592 "file": "src/lib/Menu.js", 6561 "file": "src/lib/Menu.js",
6593 "id": "menu.app.hide", 6562 "id": "menu.app.hide",
6594 "start": { 6563 "start": {
6595 "column": 8, 6564 "column": 8,
6596 "line": 231 6565 "line": 241
6597 } 6566 }
6598 }, 6567 },
6599 { 6568 {
6600 "defaultMessage": "!!!Hide Others", 6569 "defaultMessage": "!!!Hide Others",
6601 "end": { 6570 "end": {
6602 "column": 3, 6571 "column": 3,
6603 "line": 238 6572 "line": 248
6604 }, 6573 },
6605 "file": "src/lib/Menu.js", 6574 "file": "src/lib/Menu.js",
6606 "id": "menu.app.hideOthers", 6575 "id": "menu.app.hideOthers",
6607 "start": { 6576 "start": {
6608 "column": 14, 6577 "column": 14,
6609 "line": 235 6578 "line": 245
6610 } 6579 }
6611 }, 6580 },
6612 { 6581 {
6613 "defaultMessage": "!!!Unhide", 6582 "defaultMessage": "!!!Unhide",
6614 "end": { 6583 "end": {
6615 "column": 3, 6584 "column": 3,
6616 "line": 242 6585 "line": 252
6617 }, 6586 },
6618 "file": "src/lib/Menu.js", 6587 "file": "src/lib/Menu.js",
6619 "id": "menu.app.unhide", 6588 "id": "menu.app.unhide",
6620 "start": { 6589 "start": {
6621 "column": 10, 6590 "column": 10,
6622 "line": 239 6591 "line": 249
6623 } 6592 }
6624 }, 6593 },
6625 { 6594 {
6626 "defaultMessage": "!!!Auto-hide menu bar", 6595 "defaultMessage": "!!!Auto-hide menu bar",
6627 "end": { 6596 "end": {
6628 "column": 3, 6597 "column": 3,
6629 "line": 246 6598 "line": 256
6630 }, 6599 },
6631 "file": "src/lib/Menu.js", 6600 "file": "src/lib/Menu.js",
6632 "id": "menu.app.autohideMenuBar", 6601 "id": "menu.app.autohideMenuBar",
6633 "start": { 6602 "start": {
6634 "column": 19, 6603 "column": 19,
6635 "line": 243 6604 "line": 253
6636 } 6605 }
6637 }, 6606 },
6638 { 6607 {
6639 "defaultMessage": "!!!Quit", 6608 "defaultMessage": "!!!Quit",
6640 "end": { 6609 "end": {
6641 "column": 3, 6610 "column": 3,
6642 "line": 250 6611 "line": 260
6643 }, 6612 },
6644 "file": "src/lib/Menu.js", 6613 "file": "src/lib/Menu.js",
6645 "id": "menu.app.quit", 6614 "id": "menu.app.quit",
6646 "start": { 6615 "start": {
6647 "column": 8, 6616 "column": 8,
6648 "line": 247 6617 "line": 257
6649 } 6618 }
6650 }, 6619 },
6651 { 6620 {
6652 "defaultMessage": "!!!Add New Service...", 6621 "defaultMessage": "!!!Add New Service...",
6653 "end": { 6622 "end": {
6654 "column": 3, 6623 "column": 3,
6655 "line": 254 6624 "line": 264
6656 }, 6625 },
6657 "file": "src/lib/Menu.js", 6626 "file": "src/lib/Menu.js",
6658 "id": "menu.services.addNewService", 6627 "id": "menu.services.addNewService",
6659 "start": { 6628 "start": {
6660 "column": 17, 6629 "column": 17,
6661 "line": 251 6630 "line": 261
6662 } 6631 }
6663 }, 6632 },
6664 { 6633 {
6665 "defaultMessage": "!!!Add New Workspace...", 6634 "defaultMessage": "!!!Add New Workspace...",
6666 "end": { 6635 "end": {
6667 "column": 3, 6636 "column": 3,
6668 "line": 258 6637 "line": 268
6669 }, 6638 },
6670 "file": "src/lib/Menu.js", 6639 "file": "src/lib/Menu.js",
6671 "id": "menu.workspaces.addNewWorkspace", 6640 "id": "menu.workspaces.addNewWorkspace",
6672 "start": { 6641 "start": {
6673 "column": 19, 6642 "column": 19,
6674 "line": 255 6643 "line": 265
6675 } 6644 }
6676 }, 6645 },
6677 { 6646 {
6678 "defaultMessage": "!!!Open workspace drawer", 6647 "defaultMessage": "!!!Open workspace drawer",
6679 "end": { 6648 "end": {
6680 "column": 3, 6649 "column": 3,
6681 "line": 262 6650 "line": 272
6682 }, 6651 },
6683 "file": "src/lib/Menu.js", 6652 "file": "src/lib/Menu.js",
6684 "id": "menu.workspaces.openWorkspaceDrawer", 6653 "id": "menu.workspaces.openWorkspaceDrawer",
6685 "start": { 6654 "start": {
6686 "column": 23, 6655 "column": 23,
6687 "line": 259 6656 "line": 269
6688 } 6657 }
6689 }, 6658 },
6690 { 6659 {
6691 "defaultMessage": "!!!Close workspace drawer", 6660 "defaultMessage": "!!!Close workspace drawer",
6692 "end": { 6661 "end": {
6693 "column": 3, 6662 "column": 3,
6694 "line": 266 6663 "line": 276
6695 }, 6664 },
6696 "file": "src/lib/Menu.js", 6665 "file": "src/lib/Menu.js",
6697 "id": "menu.workspaces.closeWorkspaceDrawer", 6666 "id": "menu.workspaces.closeWorkspaceDrawer",
6698 "start": { 6667 "start": {
6699 "column": 24, 6668 "column": 24,
6700 "line": 263 6669 "line": 273
6701 } 6670 }
6702 }, 6671 },
6703 { 6672 {
6704 "defaultMessage": "!!!Activate next service...", 6673 "defaultMessage": "!!!Activate next service...",
6705 "end": { 6674 "end": {
6706 "column": 3, 6675 "column": 3,
6707 "line": 270 6676 "line": 280
6708 }, 6677 },
6709 "file": "src/lib/Menu.js", 6678 "file": "src/lib/Menu.js",
6710 "id": "menu.services.setNextServiceActive", 6679 "id": "menu.services.setNextServiceActive",
6711 "start": { 6680 "start": {
6712 "column": 23, 6681 "column": 23,
6713 "line": 267 6682 "line": 277
6714 } 6683 }
6715 }, 6684 },
6716 { 6685 {
6717 "defaultMessage": "!!!Activate previous service...", 6686 "defaultMessage": "!!!Activate previous service...",
6718 "end": { 6687 "end": {
6719 "column": 3, 6688 "column": 3,
6720 "line": 274 6689 "line": 284
6721 }, 6690 },
6722 "file": "src/lib/Menu.js", 6691 "file": "src/lib/Menu.js",
6723 "id": "menu.services.activatePreviousService", 6692 "id": "menu.services.activatePreviousService",
6724 "start": { 6693 "start": {
6725 "column": 27, 6694 "column": 27,
6726 "line": 271 6695 "line": 281
6727 } 6696 }
6728 }, 6697 },
6729 { 6698 {
6730 "defaultMessage": "!!!Disable notifications & audio", 6699 "defaultMessage": "!!!Disable notifications & audio",
6731 "end": { 6700 "end": {
6732 "column": 3, 6701 "column": 3,
6733 "line": 278 6702 "line": 288
6734 }, 6703 },
6735 "file": "src/lib/Menu.js", 6704 "file": "src/lib/Menu.js",
6736 "id": "sidebar.muteApp", 6705 "id": "sidebar.muteApp",
6737 "start": { 6706 "start": {
6738 "column": 11, 6707 "column": 11,
6739 "line": 275 6708 "line": 285
6740 } 6709 }
6741 }, 6710 },
6742 { 6711 {
6743 "defaultMessage": "!!!Enable notifications & audio", 6712 "defaultMessage": "!!!Enable notifications & audio",
6744 "end": { 6713 "end": {
6745 "column": 3, 6714 "column": 3,
6746 "line": 282 6715 "line": 292
6747 }, 6716 },
6748 "file": "src/lib/Menu.js", 6717 "file": "src/lib/Menu.js",
6749 "id": "sidebar.unmuteApp", 6718 "id": "sidebar.unmuteApp",
6750 "start": { 6719 "start": {
6751 "column": 13, 6720 "column": 13,
6752 "line": 279 6721 "line": 289
6753 } 6722 }
6754 }, 6723 },
6755 { 6724 {
6756 "defaultMessage": "!!!Workspaces", 6725 "defaultMessage": "!!!Workspaces",
6757 "end": { 6726 "end": {
6758 "column": 3, 6727 "column": 3,
6759 "line": 286 6728 "line": 296
6760 }, 6729 },
6761 "file": "src/lib/Menu.js", 6730 "file": "src/lib/Menu.js",
6762 "id": "menu.workspaces", 6731 "id": "menu.workspaces",
6763 "start": { 6732 "start": {
6764 "column": 14, 6733 "column": 14,
6765 "line": 283 6734 "line": 293
6766 } 6735 }
6767 }, 6736 },
6768 { 6737 {
6769 "defaultMessage": "!!!Default", 6738 "defaultMessage": "!!!Default",
6770 "end": { 6739 "end": {
6771 "column": 3, 6740 "column": 3,
6772 "line": 290 6741 "line": 300
6773 }, 6742 },
6774 "file": "src/lib/Menu.js", 6743 "file": "src/lib/Menu.js",
6775 "id": "menu.workspaces.defaultWorkspace", 6744 "id": "menu.workspaces.defaultWorkspace",
6776 "start": { 6745 "start": {
6777 "column": 20, 6746 "column": 20,
6778 "line": 287 6747 "line": 297
6779 } 6748 }
6780 }, 6749 },
6781 { 6750 {
6782 "defaultMessage": "!!!Todos", 6751 "defaultMessage": "!!!Todos",
6783 "end": { 6752 "end": {
6784 "column": 3, 6753 "column": 3,
6785 "line": 294 6754 "line": 304
6786 }, 6755 },
6787 "file": "src/lib/Menu.js", 6756 "file": "src/lib/Menu.js",
6788 "id": "menu.todos", 6757 "id": "menu.todos",
6789 "start": { 6758 "start": {
6790 "column": 9, 6759 "column": 9,
6791 "line": 291 6760 "line": 301
6792 } 6761 }
6793 }, 6762 },
6794 { 6763 {
6795 "defaultMessage": "!!!Open Todos drawer", 6764 "defaultMessage": "!!!Open Todos drawer",
6796 "end": { 6765 "end": {
6797 "column": 3, 6766 "column": 3,
6798 "line": 298 6767 "line": 308
6799 }, 6768 },
6800 "file": "src/lib/Menu.js", 6769 "file": "src/lib/Menu.js",
6801 "id": "menu.Todoss.openTodosDrawer", 6770 "id": "menu.Todoss.openTodosDrawer",
6802 "start": { 6771 "start": {
6803 "column": 19, 6772 "column": 19,
6804 "line": 295 6773 "line": 305
6805 } 6774 }
6806 }, 6775 },
6807 { 6776 {
6808 "defaultMessage": "!!!Close Todos drawer", 6777 "defaultMessage": "!!!Close Todos drawer",
6809 "end": { 6778 "end": {
6810 "column": 3, 6779 "column": 3,
6811 "line": 302 6780 "line": 312
6812 }, 6781 },
6813 "file": "src/lib/Menu.js", 6782 "file": "src/lib/Menu.js",
6814 "id": "menu.Todoss.closeTodosDrawer", 6783 "id": "menu.Todoss.closeTodosDrawer",
6815 "start": { 6784 "start": {
6816 "column": 20, 6785 "column": 20,
6817 "line": 299 6786 "line": 309
6818 } 6787 }
6819 }, 6788 },
6820 { 6789 {
6821 "defaultMessage": "!!!Enable Todos", 6790 "defaultMessage": "!!!Enable Todos",
6822 "end": { 6791 "end": {
6823 "column": 3, 6792 "column": 3,
6824 "line": 306 6793 "line": 316
6825 }, 6794 },
6826 "file": "src/lib/Menu.js", 6795 "file": "src/lib/Menu.js",
6827 "id": "menu.todos.enableTodos", 6796 "id": "menu.todos.enableTodos",
6828 "start": { 6797 "start": {
6829 "column": 15, 6798 "column": 15,
6830 "line": 303 6799 "line": 313
6831 } 6800 }
6832 }, 6801 },
6833 { 6802 {
6834 "defaultMessage": "!!!Home", 6803 "defaultMessage": "!!!Home",
6835 "end": { 6804 "end": {
6836 "column": 3, 6805 "column": 3,
6837 "line": 310 6806 "line": 320
6838 }, 6807 },
6839 "file": "src/lib/Menu.js", 6808 "file": "src/lib/Menu.js",
6840 "id": "menu.services.goHome", 6809 "id": "menu.services.goHome",
6841 "start": { 6810 "start": {
6842 "column": 17, 6811 "column": 17,
6843 "line": 307 6812 "line": 317
6844 } 6813 }
6845 } 6814 }
6846 ], 6815 ],
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index f7e3d662a..5c967bc82 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -10,7 +10,6 @@
10 "connectionLostBanner.cta": "Reload Service", 10 "connectionLostBanner.cta": "Reload Service",
11 "connectionLostBanner.informationLink": "What happened?", 11 "connectionLostBanner.informationLink": "What happened?",
12 "connectionLostBanner.message": "Oh no! Ferdi lost the connection to {name}.", 12 "connectionLostBanner.message": "Oh no! Ferdi lost the connection to {name}.",
13 "feature.announcements.changelog.headline": "Changes in Ferdi {version}",
14 "feature.debugger.title": "Publish debugging information", 13 "feature.debugger.title": "Publish debugging information",
15 "feature.nightlyBuilds.activate": "Activate", 14 "feature.nightlyBuilds.activate": "Activate",
16 "feature.nightlyBuilds.cancel": "Cancel", 15 "feature.nightlyBuilds.cancel": "Cancel",
@@ -76,7 +75,6 @@
76 "menu.Todoss.closeTodosDrawer": "Close Todos drawer", 75 "menu.Todoss.closeTodosDrawer": "Close Todos drawer",
77 "menu.Todoss.openTodosDrawer": "Open Todos drawer", 76 "menu.Todoss.openTodosDrawer": "Open Todos drawer",
78 "menu.app.about": "About Ferdi", 77 "menu.app.about": "About Ferdi",
79 "menu.app.announcement": "What's new?",
80 "menu.app.autohideMenuBar": "Auto-hide menu bar", 78 "menu.app.autohideMenuBar": "Auto-hide menu bar",
81 "menu.app.checkForUpdates": "Check for updates", 79 "menu.app.checkForUpdates": "Check for updates",
82 "menu.app.hide": "Hide", 80 "menu.app.hide": "Hide",
@@ -461,4 +459,4 @@
461 "workspaceDrawer.workspaceFeatureInfo": "<p>Ferdi Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.</p><p>You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.</p>", 459 "workspaceDrawer.workspaceFeatureInfo": "<p>Ferdi Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.</p><p>You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.</p>",
462 "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings", 460 "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings",
463 "workspaces.switchingIndicator.switchingTo": "Switching to" 461 "workspaces.switchingIndicator.switchingTo": "Switching to"
464} 462} \ No newline at end of file
diff --git a/src/i18n/messages/src/components/AppUpdateInfoBar.json b/src/i18n/messages/src/components/AppUpdateInfoBar.json
index da801b548..b99eaff67 100644
--- a/src/i18n/messages/src/components/AppUpdateInfoBar.json
+++ b/src/i18n/messages/src/components/AppUpdateInfoBar.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!A new update for Ferdi is available.", 4 "defaultMessage": "!!!A new update for Ferdi is available.",
5 "file": "src/components/AppUpdateInfoBar.js", 5 "file": "src/components/AppUpdateInfoBar.js",
6 "start": { 6 "start": {
7 "line": 9, 7 "line": 10,
8 "column": 19 8 "column": 19
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 12, 11 "line": 13,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!Changelog", 17 "defaultMessage": "!!!Changelog",
18 "file": "src/components/AppUpdateInfoBar.js", 18 "file": "src/components/AppUpdateInfoBar.js",
19 "start": { 19 "start": {
20 "line": 13, 20 "line": 14,
21 "column": 13 21 "column": 13
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 16, 24 "line": 17,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Restart & install update", 30 "defaultMessage": "!!!Restart & install update",
31 "file": "src/components/AppUpdateInfoBar.js", 31 "file": "src/components/AppUpdateInfoBar.js",
32 "start": { 32 "start": {
33 "line": 17, 33 "line": 18,
34 "column": 23 34 "column": 23
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 20, 37 "line": 21,
38 "column": 3 38 "column": 3
39 } 39 }
40 } 40 }
diff --git a/src/i18n/messages/src/features/announcements/components/AnnouncementScreen.json b/src/i18n/messages/src/features/announcements/components/AnnouncementScreen.json
deleted file mode 100644
index 6b94ff792..000000000
--- a/src/i18n/messages/src/features/announcements/components/AnnouncementScreen.json
+++ /dev/null
@@ -1,15 +0,0 @@
1[
2 {
3 "id": "feature.announcements.changelog.headline",
4 "defaultMessage": "!!!Changes in Ferdi {version}",
5 "file": "src/features/announcements/components/AnnouncementScreen.js",
6 "start": {
7 "line": 20,
8 "column": 12
9 },
10 "end": {
11 "line": 23,
12 "column": 3
13 }
14 }
15] \ No newline at end of file
diff --git a/src/i18n/messages/src/lib/Menu.json b/src/i18n/messages/src/lib/Menu.json
index 373c3a64b..3799c9130 100644
--- a/src/i18n/messages/src/lib/Menu.json
+++ b/src/i18n/messages/src/lib/Menu.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Edit", 4 "defaultMessage": "!!!Edit",
5 "file": "src/lib/Menu.js", 5 "file": "src/lib/Menu.js",
6 "start": { 6 "start": {
7 "line": 19, 7 "line": 32,
8 "column": 8 8 "column": 8
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 22, 11 "line": 35,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!Undo", 17 "defaultMessage": "!!!Undo",
18 "file": "src/lib/Menu.js", 18 "file": "src/lib/Menu.js",
19 "start": { 19 "start": {
20 "line": 23, 20 "line": 36,
21 "column": 8 21 "column": 8
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 26, 24 "line": 39,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Redo", 30 "defaultMessage": "!!!Redo",
31 "file": "src/lib/Menu.js", 31 "file": "src/lib/Menu.js",
32 "start": { 32 "start": {
33 "line": 27, 33 "line": 40,
34 "column": 8 34 "column": 8
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 30, 37 "line": 43,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,11 @@
43 "defaultMessage": "!!!Cut", 43 "defaultMessage": "!!!Cut",
44 "file": "src/lib/Menu.js", 44 "file": "src/lib/Menu.js",
45 "start": { 45 "start": {
46 "line": 31, 46 "line": 44,
47 "column": 7 47 "column": 7
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 34, 50 "line": 47,
51 "column": 3 51 "column": 3
52 } 52 }
53 }, 53 },
@@ -56,11 +56,11 @@
56 "defaultMessage": "!!!Copy", 56 "defaultMessage": "!!!Copy",
57 "file": "src/lib/Menu.js", 57 "file": "src/lib/Menu.js",
58 "start": { 58 "start": {
59 "line": 35, 59 "line": 48,
60 "column": 8 60 "column": 8
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 38, 63 "line": 51,
64 "column": 3 64 "column": 3
65 } 65 }
66 }, 66 },
@@ -69,11 +69,11 @@
69 "defaultMessage": "!!!Paste", 69 "defaultMessage": "!!!Paste",
70 "file": "src/lib/Menu.js", 70 "file": "src/lib/Menu.js",
71 "start": { 71 "start": {
72 "line": 39, 72 "line": 52,
73 "column": 9 73 "column": 9
74 }, 74 },
75 "end": { 75 "end": {
76 "line": 42, 76 "line": 55,
77 "column": 3 77 "column": 3
78 } 78 }
79 }, 79 },
@@ -82,11 +82,11 @@
82 "defaultMessage": "!!!Paste And Match Style", 82 "defaultMessage": "!!!Paste And Match Style",
83 "file": "src/lib/Menu.js", 83 "file": "src/lib/Menu.js",
84 "start": { 84 "start": {
85 "line": 43, 85 "line": 56,
86 "column": 22 86 "column": 22
87 }, 87 },
88 "end": { 88 "end": {
89 "line": 46, 89 "line": 59,
90 "column": 3 90 "column": 3
91 } 91 }
92 }, 92 },
@@ -95,11 +95,11 @@
95 "defaultMessage": "!!!Delete", 95 "defaultMessage": "!!!Delete",
96 "file": "src/lib/Menu.js", 96 "file": "src/lib/Menu.js",
97 "start": { 97 "start": {
98 "line": 47, 98 "line": 60,
99 "column": 10 99 "column": 10
100 }, 100 },
101 "end": { 101 "end": {
102 "line": 50, 102 "line": 63,
103 "column": 3 103 "column": 3
104 } 104 }
105 }, 105 },
@@ -108,11 +108,11 @@
108 "defaultMessage": "!!!Select All", 108 "defaultMessage": "!!!Select All",
109 "file": "src/lib/Menu.js", 109 "file": "src/lib/Menu.js",
110 "start": { 110 "start": {
111 "line": 51, 111 "line": 64,
112 "column": 13 112 "column": 13
113 }, 113 },
114 "end": { 114 "end": {
115 "line": 54, 115 "line": 67,
116 "column": 3 116 "column": 3
117 } 117 }
118 }, 118 },
@@ -121,11 +121,11 @@
121 "defaultMessage": "!!!Find in Page", 121 "defaultMessage": "!!!Find in Page",
122 "file": "src/lib/Menu.js", 122 "file": "src/lib/Menu.js",
123 "start": { 123 "start": {
124 "line": 55, 124 "line": 68,
125 "column": 14 125 "column": 14
126 }, 126 },
127 "end": { 127 "end": {
128 "line": 58, 128 "line": 71,
129 "column": 3 129 "column": 3
130 } 130 }
131 }, 131 },
@@ -134,11 +134,11 @@
134 "defaultMessage": "!!!Speech", 134 "defaultMessage": "!!!Speech",
135 "file": "src/lib/Menu.js", 135 "file": "src/lib/Menu.js",
136 "start": { 136 "start": {
137 "line": 59, 137 "line": 72,
138 "column": 10 138 "column": 10
139 }, 139 },
140 "end": { 140 "end": {
141 "line": 62, 141 "line": 75,
142 "column": 3 142 "column": 3
143 } 143 }
144 }, 144 },
@@ -147,11 +147,11 @@
147 "defaultMessage": "!!!Start Speaking", 147 "defaultMessage": "!!!Start Speaking",
148 "file": "src/lib/Menu.js", 148 "file": "src/lib/Menu.js",
149 "start": { 149 "start": {
150 "line": 63, 150 "line": 76,
151 "column": 17 151 "column": 17
152 }, 152 },
153 "end": { 153 "end": {
154 "line": 66, 154 "line": 79,
155 "column": 3 155 "column": 3
156 } 156 }
157 }, 157 },
@@ -160,11 +160,11 @@
160 "defaultMessage": "!!!Stop Speaking", 160 "defaultMessage": "!!!Stop Speaking",
161 "file": "src/lib/Menu.js", 161 "file": "src/lib/Menu.js",
162 "start": { 162 "start": {
163 "line": 67, 163 "line": 80,
164 "column": 16 164 "column": 16
165 }, 165 },
166 "end": { 166 "end": {
167 "line": 70, 167 "line": 83,
168 "column": 3 168 "column": 3
169 } 169 }
170 }, 170 },
@@ -173,11 +173,11 @@
173 "defaultMessage": "!!!Start Dictation", 173 "defaultMessage": "!!!Start Dictation",
174 "file": "src/lib/Menu.js", 174 "file": "src/lib/Menu.js",
175 "start": { 175 "start": {
176 "line": 71, 176 "line": 84,
177 "column": 18 177 "column": 18
178 }, 178 },
179 "end": { 179 "end": {
180 "line": 74, 180 "line": 87,
181 "column": 3 181 "column": 3
182 } 182 }
183 }, 183 },
@@ -186,11 +186,11 @@
186 "defaultMessage": "!!!Emoji & Symbols", 186 "defaultMessage": "!!!Emoji & Symbols",
187 "file": "src/lib/Menu.js", 187 "file": "src/lib/Menu.js",
188 "start": { 188 "start": {
189 "line": 75, 189 "line": 88,
190 "column": 16 190 "column": 16
191 }, 191 },
192 "end": { 192 "end": {
193 "line": 78, 193 "line": 91,
194 "column": 3 194 "column": 3
195 } 195 }
196 }, 196 },
@@ -199,11 +199,11 @@
199 "defaultMessage": "!!!Open Quick Switch", 199 "defaultMessage": "!!!Open Quick Switch",
200 "file": "src/lib/Menu.js", 200 "file": "src/lib/Menu.js",
201 "start": { 201 "start": {
202 "line": 79, 202 "line": 92,
203 "column": 19 203 "column": 19
204 }, 204 },
205 "end": { 205 "end": {
206 "line": 82, 206 "line": 95,
207 "column": 3 207 "column": 3
208 } 208 }
209 }, 209 },
@@ -212,11 +212,11 @@
212 "defaultMessage": "!!!Back", 212 "defaultMessage": "!!!Back",
213 "file": "src/lib/Menu.js", 213 "file": "src/lib/Menu.js",
214 "start": { 214 "start": {
215 "line": 83, 215 "line": 96,
216 "column": 8 216 "column": 8
217 }, 217 },
218 "end": { 218 "end": {
219 "line": 86, 219 "line": 99,
220 "column": 3 220 "column": 3
221 } 221 }
222 }, 222 },
@@ -225,11 +225,11 @@
225 "defaultMessage": "!!!Forward", 225 "defaultMessage": "!!!Forward",
226 "file": "src/lib/Menu.js", 226 "file": "src/lib/Menu.js",
227 "start": { 227 "start": {
228 "line": 87, 228 "line": 100,
229 "column": 11 229 "column": 11
230 }, 230 },
231 "end": { 231 "end": {
232 "line": 90, 232 "line": 103,
233 "column": 3 233 "column": 3
234 } 234 }
235 }, 235 },
@@ -238,11 +238,11 @@
238 "defaultMessage": "!!!Actual Size", 238 "defaultMessage": "!!!Actual Size",
239 "file": "src/lib/Menu.js", 239 "file": "src/lib/Menu.js",
240 "start": { 240 "start": {
241 "line": 91, 241 "line": 104,
242 "column": 13 242 "column": 13
243 }, 243 },
244 "end": { 244 "end": {
245 "line": 94, 245 "line": 107,
246 "column": 3 246 "column": 3
247 } 247 }
248 }, 248 },
@@ -251,11 +251,11 @@
251 "defaultMessage": "!!!Zoom In", 251 "defaultMessage": "!!!Zoom In",
252 "file": "src/lib/Menu.js", 252 "file": "src/lib/Menu.js",
253 "start": { 253 "start": {
254 "line": 95, 254 "line": 108,
255 "column": 10 255 "column": 10
256 }, 256 },
257 "end": { 257 "end": {
258 "line": 98, 258 "line": 111,
259 "column": 3 259 "column": 3
260 } 260 }
261 }, 261 },
@@ -264,11 +264,11 @@
264 "defaultMessage": "!!!Zoom Out", 264 "defaultMessage": "!!!Zoom Out",
265 "file": "src/lib/Menu.js", 265 "file": "src/lib/Menu.js",
266 "start": { 266 "start": {
267 "line": 99, 267 "line": 112,
268 "column": 11 268 "column": 11
269 }, 269 },
270 "end": { 270 "end": {
271 "line": 102, 271 "line": 115,
272 "column": 3 272 "column": 3
273 } 273 }
274 }, 274 },
@@ -277,11 +277,11 @@
277 "defaultMessage": "!!!Toggle Full Screen", 277 "defaultMessage": "!!!Toggle Full Screen",
278 "file": "src/lib/Menu.js", 278 "file": "src/lib/Menu.js",
279 "start": { 279 "start": {
280 "line": 103, 280 "line": 116,
281 "column": 20 281 "column": 20
282 }, 282 },
283 "end": { 283 "end": {
284 "line": 106, 284 "line": 119,
285 "column": 3 285 "column": 3
286 } 286 }
287 }, 287 },
@@ -290,11 +290,11 @@
290 "defaultMessage": "!!!Toggle Dark Mode", 290 "defaultMessage": "!!!Toggle Dark Mode",
291 "file": "src/lib/Menu.js", 291 "file": "src/lib/Menu.js",
292 "start": { 292 "start": {
293 "line": 107, 293 "line": 120,
294 "column": 18 294 "column": 18
295 }, 295 },
296 "end": { 296 "end": {
297 "line": 110, 297 "line": 123,
298 "column": 3 298 "column": 3
299 } 299 }
300 }, 300 },
@@ -303,11 +303,11 @@
303 "defaultMessage": "!!!Toggle Developer Tools", 303 "defaultMessage": "!!!Toggle Developer Tools",
304 "file": "src/lib/Menu.js", 304 "file": "src/lib/Menu.js",
305 "start": { 305 "start": {
306 "line": 111, 306 "line": 124,
307 "column": 18 307 "column": 18
308 }, 308 },
309 "end": { 309 "end": {
310 "line": 114, 310 "line": 127,
311 "column": 3 311 "column": 3
312 } 312 }
313 }, 313 },
@@ -316,11 +316,11 @@
316 "defaultMessage": "!!!Toggle Todos Developer Tools", 316 "defaultMessage": "!!!Toggle Todos Developer Tools",
317 "file": "src/lib/Menu.js", 317 "file": "src/lib/Menu.js",
318 "start": { 318 "start": {
319 "line": 115, 319 "line": 128,
320 "column": 23 320 "column": 23
321 }, 321 },
322 "end": { 322 "end": {
323 "line": 118, 323 "line": 131,
324 "column": 3 324 "column": 3
325 } 325 }
326 }, 326 },
@@ -329,11 +329,11 @@
329 "defaultMessage": "!!!Toggle Service Developer Tools", 329 "defaultMessage": "!!!Toggle Service Developer Tools",
330 "file": "src/lib/Menu.js", 330 "file": "src/lib/Menu.js",
331 "start": { 331 "start": {
332 "line": 119, 332 "line": 132,
333 "column": 25 333 "column": 25
334 }, 334 },
335 "end": { 335 "end": {
336 "line": 122, 336 "line": 135,
337 "column": 3 337 "column": 3
338 } 338 }
339 }, 339 },
@@ -342,11 +342,11 @@
342 "defaultMessage": "!!!Reload Service", 342 "defaultMessage": "!!!Reload Service",
343 "file": "src/lib/Menu.js", 343 "file": "src/lib/Menu.js",
344 "start": { 344 "start": {
345 "line": 123, 345 "line": 136,
346 "column": 17 346 "column": 17
347 }, 347 },
348 "end": { 348 "end": {
349 "line": 126, 349 "line": 139,
350 "column": 3 350 "column": 3
351 } 351 }
352 }, 352 },
@@ -355,11 +355,11 @@
355 "defaultMessage": "!!!Reload Ferdi", 355 "defaultMessage": "!!!Reload Ferdi",
356 "file": "src/lib/Menu.js", 356 "file": "src/lib/Menu.js",
357 "start": { 357 "start": {
358 "line": 127, 358 "line": 140,
359 "column": 15 359 "column": 15
360 }, 360 },
361 "end": { 361 "end": {
362 "line": 130, 362 "line": 143,
363 "column": 3 363 "column": 3
364 } 364 }
365 }, 365 },
@@ -368,11 +368,11 @@
368 "defaultMessage": "!!!Lock Ferdi", 368 "defaultMessage": "!!!Lock Ferdi",
369 "file": "src/lib/Menu.js", 369 "file": "src/lib/Menu.js",
370 "start": { 370 "start": {
371 "line": 131, 371 "line": 144,
372 "column": 13 372 "column": 13
373 }, 373 },
374 "end": { 374 "end": {
375 "line": 134, 375 "line": 147,
376 "column": 3 376 "column": 3
377 } 377 }
378 }, 378 },
@@ -381,11 +381,11 @@
381 "defaultMessage": "!!!Reload ToDos", 381 "defaultMessage": "!!!Reload ToDos",
382 "file": "src/lib/Menu.js", 382 "file": "src/lib/Menu.js",
383 "start": { 383 "start": {
384 "line": 135, 384 "line": 148,
385 "column": 15 385 "column": 15
386 }, 386 },
387 "end": { 387 "end": {
388 "line": 138, 388 "line": 151,
389 "column": 3 389 "column": 3
390 } 390 }
391 }, 391 },
@@ -394,11 +394,11 @@
394 "defaultMessage": "!!!Minimize", 394 "defaultMessage": "!!!Minimize",
395 "file": "src/lib/Menu.js", 395 "file": "src/lib/Menu.js",
396 "start": { 396 "start": {
397 "line": 139, 397 "line": 152,
398 "column": 12 398 "column": 12
399 }, 399 },
400 "end": { 400 "end": {
401 "line": 142, 401 "line": 155,
402 "column": 3 402 "column": 3
403 } 403 }
404 }, 404 },
@@ -407,11 +407,11 @@
407 "defaultMessage": "!!!Close", 407 "defaultMessage": "!!!Close",
408 "file": "src/lib/Menu.js", 408 "file": "src/lib/Menu.js",
409 "start": { 409 "start": {
410 "line": 143, 410 "line": 156,
411 "column": 9 411 "column": 9
412 }, 412 },
413 "end": { 413 "end": {
414 "line": 146, 414 "line": 159,
415 "column": 3 415 "column": 3
416 } 416 }
417 }, 417 },
@@ -420,11 +420,11 @@
420 "defaultMessage": "!!!Learn More", 420 "defaultMessage": "!!!Learn More",
421 "file": "src/lib/Menu.js", 421 "file": "src/lib/Menu.js",
422 "start": { 422 "start": {
423 "line": 147, 423 "line": 160,
424 "column": 13 424 "column": 13
425 }, 425 },
426 "end": { 426 "end": {
427 "line": 150, 427 "line": 163,
428 "column": 3 428 "column": 3
429 } 429 }
430 }, 430 },
@@ -433,11 +433,11 @@
433 "defaultMessage": "!!!Changelog", 433 "defaultMessage": "!!!Changelog",
434 "file": "src/lib/Menu.js", 434 "file": "src/lib/Menu.js",
435 "start": { 435 "start": {
436 "line": 151, 436 "line": 164,
437 "column": 13 437 "column": 13
438 }, 438 },
439 "end": { 439 "end": {
440 "line": 154, 440 "line": 167,
441 "column": 3 441 "column": 3
442 } 442 }
443 }, 443 },
@@ -446,11 +446,11 @@
446 "defaultMessage": "!!!Import/Export Configuration Data", 446 "defaultMessage": "!!!Import/Export Configuration Data",
447 "file": "src/lib/Menu.js", 447 "file": "src/lib/Menu.js",
448 "start": { 448 "start": {
449 "line": 155, 449 "line": 168,
450 "column": 20 450 "column": 20
451 }, 451 },
452 "end": { 452 "end": {
453 "line": 158, 453 "line": 171,
454 "column": 3 454 "column": 3
455 } 455 }
456 }, 456 },
@@ -459,11 +459,11 @@
459 "defaultMessage": "!!!Support", 459 "defaultMessage": "!!!Support",
460 "file": "src/lib/Menu.js", 460 "file": "src/lib/Menu.js",
461 "start": { 461 "start": {
462 "line": 159, 462 "line": 172,
463 "column": 11 463 "column": 11
464 }, 464 },
465 "end": { 465 "end": {
466 "line": 162, 466 "line": 175,
467 "column": 3 467 "column": 3
468 } 468 }
469 }, 469 },
@@ -472,11 +472,11 @@
472 "defaultMessage": "!!!Copy Debug Information", 472 "defaultMessage": "!!!Copy Debug Information",
473 "file": "src/lib/Menu.js", 473 "file": "src/lib/Menu.js",
474 "start": { 474 "start": {
475 "line": 163, 475 "line": 176,
476 "column": 13 476 "column": 13
477 }, 477 },
478 "end": { 478 "end": {
479 "line": 166, 479 "line": 179,
480 "column": 3 480 "column": 3
481 } 481 }
482 }, 482 },
@@ -485,11 +485,11 @@
485 "defaultMessage": "!!!Publish Debug Information", 485 "defaultMessage": "!!!Publish Debug Information",
486 "file": "src/lib/Menu.js", 486 "file": "src/lib/Menu.js",
487 "start": { 487 "start": {
488 "line": 167, 488 "line": 180,
489 "column": 20 489 "column": 20
490 }, 490 },
491 "end": { 491 "end": {
492 "line": 170, 492 "line": 183,
493 "column": 3 493 "column": 3
494 } 494 }
495 }, 495 },
@@ -498,11 +498,11 @@
498 "defaultMessage": "!!!Ferdi Debug Information", 498 "defaultMessage": "!!!Ferdi Debug Information",
499 "file": "src/lib/Menu.js", 499 "file": "src/lib/Menu.js",
500 "start": { 500 "start": {
501 "line": 171, 501 "line": 184,
502 "column": 27 502 "column": 27
503 }, 503 },
504 "end": { 504 "end": {
505 "line": 174, 505 "line": 187,
506 "column": 3 506 "column": 3
507 } 507 }
508 }, 508 },
@@ -511,11 +511,11 @@
511 "defaultMessage": "!!!Your Debug Information has been copied to your clipboard.", 511 "defaultMessage": "!!!Your Debug Information has been copied to your clipboard.",
512 "file": "src/lib/Menu.js", 512 "file": "src/lib/Menu.js",
513 "start": { 513 "start": {
514 "line": 175, 514 "line": 188,
515 "column": 23 515 "column": 23
516 }, 516 },
517 "end": { 517 "end": {
518 "line": 178, 518 "line": 192,
519 "column": 3 519 "column": 3
520 } 520 }
521 }, 521 },
@@ -524,11 +524,11 @@
524 "defaultMessage": "!!!Unlock with Touch ID", 524 "defaultMessage": "!!!Unlock with Touch ID",
525 "file": "src/lib/Menu.js", 525 "file": "src/lib/Menu.js",
526 "start": { 526 "start": {
527 "line": 179, 527 "line": 193,
528 "column": 11 528 "column": 11
529 }, 529 },
530 "end": { 530 "end": {
531 "line": 182, 531 "line": 196,
532 "column": 3 532 "column": 3
533 } 533 }
534 }, 534 },
@@ -537,11 +537,11 @@
537 "defaultMessage": "!!!unlock via Touch ID", 537 "defaultMessage": "!!!unlock via Touch ID",
538 "file": "src/lib/Menu.js", 538 "file": "src/lib/Menu.js",
539 "start": { 539 "start": {
540 "line": 183, 540 "line": 197,
541 "column": 17 541 "column": 17
542 }, 542 },
543 "end": { 543 "end": {
544 "line": 186, 544 "line": 200,
545 "column": 3 545 "column": 3
546 } 546 }
547 }, 547 },
@@ -550,11 +550,11 @@
550 "defaultMessage": "!!!Terms of Service", 550 "defaultMessage": "!!!Terms of Service",
551 "file": "src/lib/Menu.js", 551 "file": "src/lib/Menu.js",
552 "start": { 552 "start": {
553 "line": 187, 553 "line": 201,
554 "column": 7 554 "column": 7
555 }, 555 },
556 "end": { 556 "end": {
557 "line": 190, 557 "line": 204,
558 "column": 3 558 "column": 3
559 } 559 }
560 }, 560 },
@@ -563,11 +563,11 @@
563 "defaultMessage": "!!!Privacy Statement", 563 "defaultMessage": "!!!Privacy Statement",
564 "file": "src/lib/Menu.js", 564 "file": "src/lib/Menu.js",
565 "start": { 565 "start": {
566 "line": 191, 566 "line": 205,
567 "column": 11 567 "column": 11
568 }, 568 },
569 "end": { 569 "end": {
570 "line": 194, 570 "line": 208,
571 "column": 3 571 "column": 3
572 } 572 }
573 }, 573 },
@@ -576,11 +576,11 @@
576 "defaultMessage": "!!!File", 576 "defaultMessage": "!!!File",
577 "file": "src/lib/Menu.js", 577 "file": "src/lib/Menu.js",
578 "start": { 578 "start": {
579 "line": 195, 579 "line": 209,
580 "column": 8 580 "column": 8
581 }, 581 },
582 "end": { 582 "end": {
583 "line": 198, 583 "line": 212,
584 "column": 3 584 "column": 3
585 } 585 }
586 }, 586 },
@@ -589,11 +589,11 @@
589 "defaultMessage": "!!!View", 589 "defaultMessage": "!!!View",
590 "file": "src/lib/Menu.js", 590 "file": "src/lib/Menu.js",
591 "start": { 591 "start": {
592 "line": 199, 592 "line": 213,
593 "column": 8 593 "column": 8
594 }, 594 },
595 "end": { 595 "end": {
596 "line": 202, 596 "line": 216,
597 "column": 3 597 "column": 3
598 } 598 }
599 }, 599 },
@@ -602,11 +602,11 @@
602 "defaultMessage": "!!!Services", 602 "defaultMessage": "!!!Services",
603 "file": "src/lib/Menu.js", 603 "file": "src/lib/Menu.js",
604 "start": { 604 "start": {
605 "line": 203, 605 "line": 217,
606 "column": 12 606 "column": 12
607 }, 607 },
608 "end": { 608 "end": {
609 "line": 206, 609 "line": 220,
610 "column": 3 610 "column": 3
611 } 611 }
612 }, 612 },
@@ -615,11 +615,11 @@
615 "defaultMessage": "!!!Window", 615 "defaultMessage": "!!!Window",
616 "file": "src/lib/Menu.js", 616 "file": "src/lib/Menu.js",
617 "start": { 617 "start": {
618 "line": 207, 618 "line": 221,
619 "column": 10 619 "column": 10
620 }, 620 },
621 "end": { 621 "end": {
622 "line": 210, 622 "line": 224,
623 "column": 3 623 "column": 3
624 } 624 }
625 }, 625 },
@@ -628,11 +628,11 @@
628 "defaultMessage": "!!!Help", 628 "defaultMessage": "!!!Help",
629 "file": "src/lib/Menu.js", 629 "file": "src/lib/Menu.js",
630 "start": { 630 "start": {
631 "line": 211, 631 "line": 225,
632 "column": 8 632 "column": 8
633 }, 633 },
634 "end": { 634 "end": {
635 "line": 214, 635 "line": 228,
636 "column": 3 636 "column": 3
637 } 637 }
638 }, 638 },
@@ -641,24 +641,11 @@
641 "defaultMessage": "!!!About Ferdi", 641 "defaultMessage": "!!!About Ferdi",
642 "file": "src/lib/Menu.js", 642 "file": "src/lib/Menu.js",
643 "start": { 643 "start": {
644 "line": 215, 644 "line": 229,
645 "column": 9 645 "column": 9
646 }, 646 },
647 "end": { 647 "end": {
648 "line": 218, 648 "line": 232,
649 "column": 3
650 }
651 },
652 {
653 "id": "menu.app.announcement",
654 "defaultMessage": "!!!What's new?",
655 "file": "src/lib/Menu.js",
656 "start": {
657 "line": 219,
658 "column": 16
659 },
660 "end": {
661 "line": 222,
662 "column": 3 649 "column": 3
663 } 650 }
664 }, 651 },
@@ -667,11 +654,11 @@
667 "defaultMessage": "!!!Settings", 654 "defaultMessage": "!!!Settings",
668 "file": "src/lib/Menu.js", 655 "file": "src/lib/Menu.js",
669 "start": { 656 "start": {
670 "line": 223, 657 "line": 233,
671 "column": 12 658 "column": 12
672 }, 659 },
673 "end": { 660 "end": {
674 "line": 226, 661 "line": 236,
675 "column": 3 662 "column": 3
676 } 663 }
677 }, 664 },
@@ -680,11 +667,11 @@
680 "defaultMessage": "!!!Check for updates", 667 "defaultMessage": "!!!Check for updates",
681 "file": "src/lib/Menu.js", 668 "file": "src/lib/Menu.js",
682 "start": { 669 "start": {
683 "line": 227, 670 "line": 237,
684 "column": 19 671 "column": 19
685 }, 672 },
686 "end": { 673 "end": {
687 "line": 230, 674 "line": 240,
688 "column": 3 675 "column": 3
689 } 676 }
690 }, 677 },
@@ -693,11 +680,11 @@
693 "defaultMessage": "!!!Hide", 680 "defaultMessage": "!!!Hide",
694 "file": "src/lib/Menu.js", 681 "file": "src/lib/Menu.js",
695 "start": { 682 "start": {
696 "line": 231, 683 "line": 241,
697 "column": 8 684 "column": 8
698 }, 685 },
699 "end": { 686 "end": {
700 "line": 234, 687 "line": 244,
701 "column": 3 688 "column": 3
702 } 689 }
703 }, 690 },
@@ -706,11 +693,11 @@
706 "defaultMessage": "!!!Hide Others", 693 "defaultMessage": "!!!Hide Others",
707 "file": "src/lib/Menu.js", 694 "file": "src/lib/Menu.js",
708 "start": { 695 "start": {
709 "line": 235, 696 "line": 245,
710 "column": 14 697 "column": 14
711 }, 698 },
712 "end": { 699 "end": {
713 "line": 238, 700 "line": 248,
714 "column": 3 701 "column": 3
715 } 702 }
716 }, 703 },
@@ -719,11 +706,11 @@
719 "defaultMessage": "!!!Unhide", 706 "defaultMessage": "!!!Unhide",
720 "file": "src/lib/Menu.js", 707 "file": "src/lib/Menu.js",
721 "start": { 708 "start": {
722 "line": 239, 709 "line": 249,
723 "column": 10 710 "column": 10
724 }, 711 },
725 "end": { 712 "end": {
726 "line": 242, 713 "line": 252,
727 "column": 3 714 "column": 3
728 } 715 }
729 }, 716 },
@@ -732,11 +719,11 @@
732 "defaultMessage": "!!!Auto-hide menu bar", 719 "defaultMessage": "!!!Auto-hide menu bar",
733 "file": "src/lib/Menu.js", 720 "file": "src/lib/Menu.js",
734 "start": { 721 "start": {
735 "line": 243, 722 "line": 253,
736 "column": 19 723 "column": 19
737 }, 724 },
738 "end": { 725 "end": {
739 "line": 246, 726 "line": 256,
740 "column": 3 727 "column": 3
741 } 728 }
742 }, 729 },
@@ -745,11 +732,11 @@
745 "defaultMessage": "!!!Quit", 732 "defaultMessage": "!!!Quit",
746 "file": "src/lib/Menu.js", 733 "file": "src/lib/Menu.js",
747 "start": { 734 "start": {
748 "line": 247, 735 "line": 257,
749 "column": 8 736 "column": 8
750 }, 737 },
751 "end": { 738 "end": {
752 "line": 250, 739 "line": 260,
753 "column": 3 740 "column": 3
754 } 741 }
755 }, 742 },
@@ -758,11 +745,11 @@
758 "defaultMessage": "!!!Add New Service...", 745 "defaultMessage": "!!!Add New Service...",
759 "file": "src/lib/Menu.js", 746 "file": "src/lib/Menu.js",
760 "start": { 747 "start": {
761 "line": 251, 748 "line": 261,
762 "column": 17 749 "column": 17
763 }, 750 },
764 "end": { 751 "end": {
765 "line": 254, 752 "line": 264,
766 "column": 3 753 "column": 3
767 } 754 }
768 }, 755 },
@@ -771,11 +758,11 @@
771 "defaultMessage": "!!!Add New Workspace...", 758 "defaultMessage": "!!!Add New Workspace...",
772 "file": "src/lib/Menu.js", 759 "file": "src/lib/Menu.js",
773 "start": { 760 "start": {
774 "line": 255, 761 "line": 265,
775 "column": 19 762 "column": 19
776 }, 763 },
777 "end": { 764 "end": {
778 "line": 258, 765 "line": 268,
779 "column": 3 766 "column": 3
780 } 767 }
781 }, 768 },
@@ -784,11 +771,11 @@
784 "defaultMessage": "!!!Open workspace drawer", 771 "defaultMessage": "!!!Open workspace drawer",
785 "file": "src/lib/Menu.js", 772 "file": "src/lib/Menu.js",
786 "start": { 773 "start": {
787 "line": 259, 774 "line": 269,
788 "column": 23 775 "column": 23
789 }, 776 },
790 "end": { 777 "end": {
791 "line": 262, 778 "line": 272,
792 "column": 3 779 "column": 3
793 } 780 }
794 }, 781 },
@@ -797,11 +784,11 @@
797 "defaultMessage": "!!!Close workspace drawer", 784 "defaultMessage": "!!!Close workspace drawer",
798 "file": "src/lib/Menu.js", 785 "file": "src/lib/Menu.js",
799 "start": { 786 "start": {
800 "line": 263, 787 "line": 273,
801 "column": 24 788 "column": 24
802 }, 789 },
803 "end": { 790 "end": {
804 "line": 266, 791 "line": 276,
805 "column": 3 792 "column": 3
806 } 793 }
807 }, 794 },
@@ -810,11 +797,11 @@
810 "defaultMessage": "!!!Activate next service...", 797 "defaultMessage": "!!!Activate next service...",
811 "file": "src/lib/Menu.js", 798 "file": "src/lib/Menu.js",
812 "start": { 799 "start": {
813 "line": 267, 800 "line": 277,
814 "column": 23 801 "column": 23
815 }, 802 },
816 "end": { 803 "end": {
817 "line": 270, 804 "line": 280,
818 "column": 3 805 "column": 3
819 } 806 }
820 }, 807 },
@@ -823,11 +810,11 @@
823 "defaultMessage": "!!!Activate previous service...", 810 "defaultMessage": "!!!Activate previous service...",
824 "file": "src/lib/Menu.js", 811 "file": "src/lib/Menu.js",
825 "start": { 812 "start": {
826 "line": 271, 813 "line": 281,
827 "column": 27 814 "column": 27
828 }, 815 },
829 "end": { 816 "end": {
830 "line": 274, 817 "line": 284,
831 "column": 3 818 "column": 3
832 } 819 }
833 }, 820 },
@@ -836,11 +823,11 @@
836 "defaultMessage": "!!!Disable notifications & audio", 823 "defaultMessage": "!!!Disable notifications & audio",
837 "file": "src/lib/Menu.js", 824 "file": "src/lib/Menu.js",
838 "start": { 825 "start": {
839 "line": 275, 826 "line": 285,
840 "column": 11 827 "column": 11
841 }, 828 },
842 "end": { 829 "end": {
843 "line": 278, 830 "line": 288,
844 "column": 3 831 "column": 3
845 } 832 }
846 }, 833 },
@@ -849,11 +836,11 @@
849 "defaultMessage": "!!!Enable notifications & audio", 836 "defaultMessage": "!!!Enable notifications & audio",
850 "file": "src/lib/Menu.js", 837 "file": "src/lib/Menu.js",
851 "start": { 838 "start": {
852 "line": 279, 839 "line": 289,
853 "column": 13 840 "column": 13
854 }, 841 },
855 "end": { 842 "end": {
856 "line": 282, 843 "line": 292,
857 "column": 3 844 "column": 3
858 } 845 }
859 }, 846 },
@@ -862,11 +849,11 @@
862 "defaultMessage": "!!!Workspaces", 849 "defaultMessage": "!!!Workspaces",
863 "file": "src/lib/Menu.js", 850 "file": "src/lib/Menu.js",
864 "start": { 851 "start": {
865 "line": 283, 852 "line": 293,
866 "column": 14 853 "column": 14
867 }, 854 },
868 "end": { 855 "end": {
869 "line": 286, 856 "line": 296,
870 "column": 3 857 "column": 3
871 } 858 }
872 }, 859 },
@@ -875,11 +862,11 @@
875 "defaultMessage": "!!!Default", 862 "defaultMessage": "!!!Default",
876 "file": "src/lib/Menu.js", 863 "file": "src/lib/Menu.js",
877 "start": { 864 "start": {
878 "line": 287, 865 "line": 297,
879 "column": 20 866 "column": 20
880 }, 867 },
881 "end": { 868 "end": {
882 "line": 290, 869 "line": 300,
883 "column": 3 870 "column": 3
884 } 871 }
885 }, 872 },
@@ -888,11 +875,11 @@
888 "defaultMessage": "!!!Todos", 875 "defaultMessage": "!!!Todos",
889 "file": "src/lib/Menu.js", 876 "file": "src/lib/Menu.js",
890 "start": { 877 "start": {
891 "line": 291, 878 "line": 301,
892 "column": 9 879 "column": 9
893 }, 880 },
894 "end": { 881 "end": {
895 "line": 294, 882 "line": 304,
896 "column": 3 883 "column": 3
897 } 884 }
898 }, 885 },
@@ -901,11 +888,11 @@
901 "defaultMessage": "!!!Open Todos drawer", 888 "defaultMessage": "!!!Open Todos drawer",
902 "file": "src/lib/Menu.js", 889 "file": "src/lib/Menu.js",
903 "start": { 890 "start": {
904 "line": 295, 891 "line": 305,
905 "column": 19 892 "column": 19
906 }, 893 },
907 "end": { 894 "end": {
908 "line": 298, 895 "line": 308,
909 "column": 3 896 "column": 3
910 } 897 }
911 }, 898 },
@@ -914,11 +901,11 @@
914 "defaultMessage": "!!!Close Todos drawer", 901 "defaultMessage": "!!!Close Todos drawer",
915 "file": "src/lib/Menu.js", 902 "file": "src/lib/Menu.js",
916 "start": { 903 "start": {
917 "line": 299, 904 "line": 309,
918 "column": 20 905 "column": 20
919 }, 906 },
920 "end": { 907 "end": {
921 "line": 302, 908 "line": 312,
922 "column": 3 909 "column": 3
923 } 910 }
924 }, 911 },
@@ -927,11 +914,11 @@
927 "defaultMessage": "!!!Enable Todos", 914 "defaultMessage": "!!!Enable Todos",
928 "file": "src/lib/Menu.js", 915 "file": "src/lib/Menu.js",
929 "start": { 916 "start": {
930 "line": 303, 917 "line": 313,
931 "column": 15 918 "column": 15
932 }, 919 },
933 "end": { 920 "end": {
934 "line": 306, 921 "line": 316,
935 "column": 3 922 "column": 3
936 } 923 }
937 }, 924 },
@@ -940,11 +927,11 @@
940 "defaultMessage": "!!!Home", 927 "defaultMessage": "!!!Home",
941 "file": "src/lib/Menu.js", 928 "file": "src/lib/Menu.js",
942 "start": { 929 "start": {
943 "line": 307, 930 "line": 317,
944 "column": 17 931 "column": 17
945 }, 932 },
946 "end": { 933 "end": {
947 "line": 310, 934 "line": 320,
948 "column": 3 935 "column": 3
949 } 936 }
950 } 937 }
diff --git a/src/internal-server/app/Controllers/Http/StaticController.js b/src/internal-server/app/Controllers/Http/StaticController.js
index 28c5389a9..33e0e52d9 100644
--- a/src/internal-server/app/Controllers/Http/StaticController.js
+++ b/src/internal-server/app/Controllers/Http/StaticController.js
@@ -7,25 +7,14 @@ import { DEFAULT_FEATURES_CONFIG } from '../../../../config';
7// TODO: This endpoint and associated code needs to be remoeved as cleanup 7// TODO: This endpoint and associated code needs to be remoeved as cleanup
8class StaticController { 8class StaticController {
9 // Enable all features 9 // Enable all features
10 features({ 10 features({ response }) {
11 response,
12 }) {
13 return response.send(DEFAULT_FEATURES_CONFIG); 11 return response.send(DEFAULT_FEATURES_CONFIG);
14 } 12 }
15 13
16 // Return an empty array 14 // Return an empty array
17 emptyArray({ 15 emptyArray({ response }) {
18 response,
19 }) {
20 return response.send([]); 16 return response.send([]);
21 } 17 }
22
23 // Show announcements
24 announcement({
25 response,
26 }) {
27 return response.send({});
28 }
29} 18}
30 19
31export default StaticController; 20export default StaticController;
diff --git a/src/internal-server/start/routes.js b/src/internal-server/start/routes.js
index b32b094ee..e75380ccd 100644
--- a/src/internal-server/start/routes.js
+++ b/src/internal-server/start/routes.js
@@ -25,12 +25,12 @@ const OnlyAllowFerdi = async ({ request, response }, next) => {
25}; 25};
26 26
27// Health: Returning if all systems function correctly 27// Health: Returning if all systems function correctly
28Route.get('health', ({ 28Route.get('health', ({ response }) =>
29 response, 29 response.send({
30}) => response.send({ 30 api: 'success',
31 api: 'success', 31 db: 'success',
32 db: 'success', 32 }),
33})).middleware(OnlyAllowFerdi); 33).middleware(OnlyAllowFerdi);
34 34
35// API is grouped under '/v1/' route 35// API is grouped under '/v1/' route
36Route.group(() => { 36Route.group(() => {
@@ -67,8 +67,9 @@ Route.group(() => {
67 Route.get('features/:mode?', 'StaticController.features'); 67 Route.get('features/:mode?', 'StaticController.features');
68 Route.get('services', 'StaticController.emptyArray'); 68 Route.get('services', 'StaticController.emptyArray');
69 Route.get('news', 'StaticController.emptyArray'); 69 Route.get('news', 'StaticController.emptyArray');
70 Route.get('announcements/:version', 'StaticController.announcement'); 70})
71}).prefix(API_VERSION).middleware(OnlyAllowFerdi); 71 .prefix(API_VERSION)
72 .middleware(OnlyAllowFerdi);
72 73
73Route.group(() => { 74Route.group(() => {
74 Route.get('icon/:id', 'ServiceController.icon'); 75 Route.get('icon/:id', 'ServiceController.icon');
diff --git a/src/lib/Menu.js b/src/lib/Menu.js
index 5a99299c5..66447d551 100644
--- a/src/lib/Menu.js
+++ b/src/lib/Menu.js
@@ -2,12 +2,25 @@ import { clipboard } from 'electron';
2import { app, Menu, dialog, systemPreferences } from '@electron/remote'; 2import { app, Menu, dialog, systemPreferences } from '@electron/remote';
3import { autorun, observable } from 'mobx'; 3import { autorun, observable } from 'mobx';
4import { defineMessages } from 'react-intl'; 4import { defineMessages } from 'react-intl';
5import { CUSTOM_WEBSITE_RECIPE_ID, GITHUB_FERDI_URL, LIVE_API_FERDI_WEBSITE } from '../config';
6import { 5import {
7 cmdOrCtrlShortcutKey, altKey, shiftKey, settingsShortcutKey, isLinux, isMac, aboutAppDetails, lockFerdiShortcutKey, todosToggleShortcutKey, workspaceToggleShortcutKey, addNewServiceShortcutKey, muteFerdiShortcutKey, 6 CUSTOM_WEBSITE_RECIPE_ID,
7 GITHUB_FERDI_URL,
8 LIVE_API_FERDI_WEBSITE,
9} from '../config';
10import {
11 cmdOrCtrlShortcutKey,
12 altKey,
13 shiftKey,
14 settingsShortcutKey,
15 isLinux,
16 isMac,
17 aboutAppDetails,
18 lockFerdiShortcutKey,
19 todosToggleShortcutKey,
20 workspaceToggleShortcutKey,
21 addNewServiceShortcutKey,
22 muteFerdiShortcutKey,
8} from '../environment'; 23} from '../environment';
9import { announcementsStore } from '../features/announcements';
10import { announcementActions } from '../features/announcements/actions';
11import { todosStore } from '../features/todos'; 24import { todosStore } from '../features/todos';
12import { todoActions } from '../features/todos/actions'; 25import { todoActions } from '../features/todos/actions';
13import { workspaceActions } from '../features/workspaces/actions'; 26import { workspaceActions } from '../features/workspaces/actions';
@@ -174,7 +187,8 @@ const menuItems = defineMessages({
174 }, 187 },
175 debugInfoCopiedBody: { 188 debugInfoCopiedBody: {
176 id: 'menu.help.debugInfoCopiedBody', 189 id: 'menu.help.debugInfoCopiedBody',
177 defaultMessage: '!!!Your Debug Information has been copied to your clipboard.', 190 defaultMessage:
191 '!!!Your Debug Information has been copied to your clipboard.',
178 }, 192 },
179 touchId: { 193 touchId: {
180 id: 'locked.touchId', 194 id: 'locked.touchId',
@@ -216,10 +230,6 @@ const menuItems = defineMessages({
216 id: 'menu.app.about', 230 id: 'menu.app.about',
217 defaultMessage: '!!!About Ferdi', 231 defaultMessage: '!!!About Ferdi',
218 }, 232 },
219 announcement: {
220 id: 'menu.app.announcement',
221 defaultMessage: '!!!What\'s new?',
222 },
223 settings: { 233 settings: {
224 id: 'menu.app.settings', 234 id: 'menu.app.settings',
225 defaultMessage: '!!!Settings', 235 defaultMessage: '!!!Settings',
@@ -513,44 +523,49 @@ const _titleBarTemplateFactory = (intl, locked) => [
513 submenu: [ 523 submenu: [
514 { 524 {
515 label: intl.formatMessage(menuItems.learnMore), 525 label: intl.formatMessage(menuItems.learnMore),
516 click() { openExternalUrl(LIVE_API_FERDI_WEBSITE, true); }, 526 click() {
527 openExternalUrl(LIVE_API_FERDI_WEBSITE, true);
528 },
517 }, 529 },
518 { 530 {
519 label: intl.formatMessage(menuItems.changelog), 531 label: intl.formatMessage(menuItems.changelog),
520 click() { openExternalUrl(`${GITHUB_FERDI_URL}/ferdi/blob/develop/CHANGELOG.md`, true); }, 532 click() {
533 openExternalUrl(
534 `${GITHUB_FERDI_URL}/ferdi/blob/develop/CHANGELOG.md`,
535 true,
536 );
537 },
521 }, 538 },
522 { 539 {
523 label: intl.formatMessage(menuItems.importExportData), 540 label: intl.formatMessage(menuItems.importExportData),
524 click() { openExternalUrl(apiBase(false), true); }, 541 click() {
525 enabled: !locked, 542 openExternalUrl(apiBase(false), true);
526 },
527 {
528 type: 'separator',
529 },
530 {
531 label: intl.formatMessage(menuItems.announcement),
532 click: () => {
533 announcementActions.show();
534 }, 543 },
535 enabled: !locked && window.ferdi.stores.user.isLoggedIn && announcementsStore.areNewsAvailable, 544 enabled: !locked,
536 }, 545 },
537 { 546 {
538 type: 'separator', 547 type: 'separator',
539 }, 548 },
540 { 549 {
541 label: intl.formatMessage(menuItems.support), 550 label: intl.formatMessage(menuItems.support),
542 click() { openExternalUrl(`${LIVE_API_FERDI_WEBSITE}/contact`, true); }, 551 click() {
552 openExternalUrl(`${LIVE_API_FERDI_WEBSITE}/contact`, true);
553 },
543 }, 554 },
544 { 555 {
545 type: 'separator', 556 type: 'separator',
546 }, 557 },
547 { 558 {
548 label: intl.formatMessage(menuItems.tos), 559 label: intl.formatMessage(menuItems.tos),
549 click() { openExternalUrl(`${termsBase()}/terms`, true); }, 560 click() {
561 openExternalUrl(`${termsBase()}/terms`, true);
562 },
550 }, 563 },
551 { 564 {
552 label: intl.formatMessage(menuItems.privacy), 565 label: intl.formatMessage(menuItems.privacy),
553 click() { openExternalUrl(`${termsBase()}/privacy`, true); }, 566 click() {
567 openExternalUrl(`${termsBase()}/privacy`, true);
568 },
554 }, 569 },
555 ], 570 ],
556 }, 571 },
@@ -599,7 +614,8 @@ export default class FranzMenu {
599 window.ferdi.actions.settings.update({ 614 window.ferdi.actions.settings.update({
600 type: 'app', 615 type: 'app',
601 data: { 616 data: {
602 autohideMenuBar: !window.ferdi.stores.settings.app.autohideMenuBar, 617 autohideMenuBar:
618 !window.ferdi.stores.settings.app.autohideMenuBar,
603 }, 619 },
604 }); 620 });
605 }, 621 },
@@ -607,22 +623,28 @@ export default class FranzMenu {
607 } 623 }
608 624
609 if (!this.stores.settings.app.locked) { 625 if (!this.stores.settings.app.locked) {
610 tpl[1].submenu.push({ 626 tpl[1].submenu.push(
611 type: 'separator', 627 {
612 }, { 628 type: 'separator',
613 label: intl.formatMessage(menuItems.toggleDevTools),
614 accelerator: `${cmdOrCtrlShortcutKey()}+${altKey()}+I`,
615 click: (menuItem, browserWindow) => {
616 browserWindow.webContents.toggleDevTools();
617 }, 629 },
618 }, { 630 {
619 label: intl.formatMessage(menuItems.toggleServiceDevTools), 631 label: intl.formatMessage(menuItems.toggleDevTools),
620 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+${altKey()}+I`, 632 accelerator: `${cmdOrCtrlShortcutKey()}+${altKey()}+I`,
621 click: () => { 633 click: (menuItem, browserWindow) => {
622 this.actions.service.openDevToolsForActiveService(); 634 browserWindow.webContents.toggleDevTools();
635 },
623 }, 636 },
624 enabled: this.stores.user.isLoggedIn && this.stores.services.enabled.length > 0, 637 {
625 }); 638 label: intl.formatMessage(menuItems.toggleServiceDevTools),
639 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+${altKey()}+I`,
640 click: () => {
641 this.actions.service.openDevToolsForActiveService();
642 },
643 enabled:
644 this.stores.user.isLoggedIn &&
645 this.stores.services.enabled.length > 0,
646 },
647 );
626 648
627 if (this.stores.features.features.isTodosEnabled) { 649 if (this.stores.features.features.isTodosEnabled) {
628 tpl[1].submenu.push({ 650 tpl[1].submenu.push({
@@ -635,49 +657,62 @@ export default class FranzMenu {
635 }); 657 });
636 } 658 }
637 659
638 tpl[1].submenu.unshift({ 660 tpl[1].submenu.unshift(
639 label: intl.formatMessage(menuItems.reloadService), 661 {
640 id: 'reloadService', // TODO: needed? 662 label: intl.formatMessage(menuItems.reloadService),
641 accelerator: `${cmdOrCtrlShortcutKey()}+R`, 663 id: 'reloadService', // TODO: needed?
642 click: () => { 664 accelerator: `${cmdOrCtrlShortcutKey()}+R`,
643 if (this.stores.user.isLoggedIn 665 click: () => {
644 && this.stores.services.enabled.length > 0) { 666 if (
645 if (this.stores.services.active.recipe.id === CUSTOM_WEBSITE_RECIPE_ID) { 667 this.stores.user.isLoggedIn &&
646 this.stores.services.active.webview.reload(); 668 this.stores.services.enabled.length > 0
669 ) {
670 if (
671 this.stores.services.active.recipe.id ===
672 CUSTOM_WEBSITE_RECIPE_ID
673 ) {
674 this.stores.services.active.webview.reload();
675 } else {
676 this.actions.service.reloadActive();
677 }
647 } else { 678 } else {
648 this.actions.service.reloadActive(); 679 window.location.reload();
649 } 680 }
650 } else { 681 },
682 },
683 {
684 label: intl.formatMessage(menuItems.reloadFerdi),
685 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+R`,
686 click: () => {
651 window.location.reload(); 687 window.location.reload();
652 } 688 },
653 }, 689 },
654 }, { 690 {
655 label: intl.formatMessage(menuItems.reloadFerdi), 691 label: intl.formatMessage(menuItems.reloadTodos),
656 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+R`, 692 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+${altKey()}+R`,
657 click: () => { 693 click: () => {
658 window.location.reload(); 694 this.actions.todos.reload();
695 },
659 }, 696 },
660 }, { 697 {
661 label: intl.formatMessage(menuItems.reloadTodos), 698 type: 'separator',
662 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+${altKey()}+R`,
663 click: () => {
664 this.actions.todos.reload();
665 }, 699 },
666 }, { 700 {
667 type: 'separator', 701 label: intl.formatMessage(menuItems.lockFerdi),
668 }, { 702 accelerator: `${lockFerdiShortcutKey()}`,
669 label: intl.formatMessage(menuItems.lockFerdi), 703 enabled:
670 accelerator: `${lockFerdiShortcutKey()}`, 704 this.stores.user.isLoggedIn &&
671 enabled: this.stores.user.isLoggedIn && this.stores.settings.app.lockingFeatureEnabled, 705 this.stores.settings.app.lockingFeatureEnabled,
672 click() { 706 click() {
673 actions.settings.update({ 707 actions.settings.update({
674 type: 'app', 708 type: 'app',
675 data: { 709 data: {
676 locked: true, 710 locked: true,
677 }, 711 },
678 }); 712 });
713 },
679 }, 714 },
680 }); 715 );
681 716
682 if (serviceTpl.length > 0) { 717 if (serviceTpl.length > 0) {
683 tpl[2].submenu = serviceTpl; 718 tpl[2].submenu = serviceTpl;
@@ -691,26 +726,34 @@ export default class FranzMenu {
691 tpl[4].submenu = this.todosMenu(); 726 tpl[4].submenu = this.todosMenu();
692 } 727 }
693 } else { 728 } else {
694 const touchIdEnabled = isMac ? (this.stores.settings.app.useTouchIdToUnlock && systemPreferences.canPromptTouchID()) : false; 729 const touchIdEnabled = isMac
730 ? this.stores.settings.app.useTouchIdToUnlock &&
731 systemPreferences.canPromptTouchID()
732 : false;
695 733
696 tpl[0].submenu.unshift({ 734 tpl[0].submenu.unshift(
697 label: intl.formatMessage(menuItems.touchId), 735 {
698 accelerator: `${lockFerdiShortcutKey()}`, 736 label: intl.formatMessage(menuItems.touchId),
699 visible: touchIdEnabled, 737 accelerator: `${lockFerdiShortcutKey()}`,
700 click() { 738 visible: touchIdEnabled,
701 systemPreferences.promptTouchID(intl.formatMessage(menuItems.touchIdPrompt)).then(() => { 739 click() {
702 actions.settings.update({ 740 systemPreferences
703 type: 'app', 741 .promptTouchID(intl.formatMessage(menuItems.touchIdPrompt))
704 data: { 742 .then(() => {
705 locked: false, 743 actions.settings.update({
706 }, 744 type: 'app',
707 }); 745 data: {
708 }); 746 locked: false,
747 },
748 });
749 });
750 },
709 }, 751 },
710 }, { 752 {
711 type: 'separator', 753 type: 'separator',
712 visible: touchIdEnabled, 754 visible: touchIdEnabled,
713 }); 755 },
756 );
714 } 757 }
715 758
716 tpl.unshift({ 759 tpl.unshift({
@@ -838,9 +881,12 @@ export default class FranzMenu {
838 }, 881 },
839 ]; 882 ];
840 883
841 tpl[tpl.length - 1].submenu.push({ 884 tpl[tpl.length - 1].submenu.push(
842 type: 'separator', 885 {
843 }, about); 886 type: 'separator',
887 },
888 about,
889 );
844 } 890 }
845 891
846 if (!this.stores.settings.app.locked) { 892 if (!this.stores.settings.app.locked) {
@@ -856,9 +902,12 @@ export default class FranzMenu {
856 tpl[5].submenu = this.todosMenu(); 902 tpl[5].submenu = this.todosMenu();
857 } 903 }
858 904
859 tpl[tpl.length - 1].submenu.push({ 905 tpl[tpl.length - 1].submenu.push(
860 type: 'separator', 906 {
861 }, ...this.debugMenu()); 907 type: 'separator',
908 },
909 ...this.debugMenu(),
910 );
862 } 911 }
863 this.currentTemplate = tpl; 912 this.currentTemplate = tpl;
864 const menu = Menu.buildFromTemplate(tpl); 913 const menu = Menu.buildFromTemplate(tpl);
@@ -872,73 +921,95 @@ export default class FranzMenu {
872 const menu = []; 921 const menu = [];
873 const cmdAltShortcutsVisibile = !isLinux; 922 const cmdAltShortcutsVisibile = !isLinux;
874 923
875 menu.push({ 924 menu.push(
876 label: intl.formatMessage(menuItems.addNewService), 925 {
877 accelerator: `${addNewServiceShortcutKey()}`, 926 label: intl.formatMessage(menuItems.addNewService),
878 click: () => { 927 accelerator: `${addNewServiceShortcutKey()}`,
879 this.actions.ui.openSettings({ path: 'recipes' }); 928 click: () => {
929 this.actions.ui.openSettings({ path: 'recipes' });
930 },
880 }, 931 },
881 }, { 932 {
882 type: 'separator', 933 type: 'separator',
883 }, { 934 },
884 label: intl.formatMessage(menuItems.activateNextService), 935 {
885 accelerator: `${cmdOrCtrlShortcutKey()}+tab`, 936 label: intl.formatMessage(menuItems.activateNextService),
886 click: () => this.actions.service.setActiveNext(), 937 accelerator: `${cmdOrCtrlShortcutKey()}+tab`,
887 visible: !cmdAltShortcutsVisibile, 938 click: () => this.actions.service.setActiveNext(),
888 }, { 939 visible: !cmdAltShortcutsVisibile,
889 label: intl.formatMessage(menuItems.activateNextService), 940 },
890 accelerator: `${cmdOrCtrlShortcutKey()}+${altKey()}+right`, 941 {
891 click: () => this.actions.service.setActiveNext(), 942 label: intl.formatMessage(menuItems.activateNextService),
892 visible: cmdAltShortcutsVisibile, 943 accelerator: `${cmdOrCtrlShortcutKey()}+${altKey()}+right`,
893 }, { 944 click: () => this.actions.service.setActiveNext(),
894 label: intl.formatMessage(menuItems.activatePreviousService), 945 visible: cmdAltShortcutsVisibile,
895 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+tab`, 946 },
896 click: () => this.actions.service.setActivePrev(), 947 {
897 visible: !cmdAltShortcutsVisibile, 948 label: intl.formatMessage(menuItems.activatePreviousService),
898 }, { 949 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+tab`,
899 label: intl.formatMessage(menuItems.activatePreviousService), 950 click: () => this.actions.service.setActivePrev(),
900 accelerator: `${cmdOrCtrlShortcutKey()}+${altKey()}+left`, 951 visible: !cmdAltShortcutsVisibile,
901 click: () => this.actions.service.setActivePrev(), 952 },
902 visible: cmdAltShortcutsVisibile, 953 {
903 }, { 954 label: intl.formatMessage(menuItems.activatePreviousService),
904 label: intl.formatMessage( 955 accelerator: `${cmdOrCtrlShortcutKey()}+${altKey()}+left`,
905 settings.all.app.isAppMuted ? menuItems.unmuteApp : menuItems.muteApp, 956 click: () => this.actions.service.setActivePrev(),
906 ).replace('&', '&&'), 957 visible: cmdAltShortcutsVisibile,
907 accelerator: `${muteFerdiShortcutKey()}`, 958 },
908 click: () => this.actions.app.toggleMuteApp(), 959 {
909 }, { 960 label: intl
910 type: 'separator', 961 .formatMessage(
911 }); 962 settings.all.app.isAppMuted
912 963 ? menuItems.unmuteApp
913 services.allDisplayed.forEach((service, i) => (menu.push({ 964 : menuItems.muteApp,
914 label: this._getServiceName(service), 965 )
915 accelerator: i < 9 ? `${cmdOrCtrlShortcutKey()}+${i + 1}` : null, 966 .replace('&', '&&'),
916 type: 'radio', 967 accelerator: `${muteFerdiShortcutKey()}`,
917 checked: service.isActive, 968 click: () => this.actions.app.toggleMuteApp(),
918 click: () => { 969 },
919 this.actions.service.setActive({ serviceId: service.id }); 970 {
920 971 type: 'separator',
921 if (isMac && i === 0) {
922 app.mainWindow.restore();
923 }
924 }, 972 },
925 }))); 973 );
926 974
927 if (services.active && services.active.recipe.id === CUSTOM_WEBSITE_RECIPE_ID) { 975 services.allDisplayed.forEach((service, i) =>
928 menu.push({ 976 menu.push({
929 type: 'separator', 977 label: this._getServiceName(service),
930 }, { 978 accelerator: i < 9 ? `${cmdOrCtrlShortcutKey()}+${i + 1}` : null,
931 label: intl.formatMessage(menuItems.serviceGoHome), 979 type: 'radio',
932 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+H`, 980 checked: service.isActive,
933 click: () => this.actions.service.reloadActive(), 981 click: () => {
934 }); 982 this.actions.service.setActive({ serviceId: service.id });
983
984 if (isMac && i === 0) {
985 app.mainWindow.restore();
986 }
987 },
988 }),
989 );
990
991 if (
992 services.active &&
993 services.active.recipe.id === CUSTOM_WEBSITE_RECIPE_ID
994 ) {
995 menu.push(
996 {
997 type: 'separator',
998 },
999 {
1000 label: intl.formatMessage(menuItems.serviceGoHome),
1001 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+H`,
1002 click: () => this.actions.service.reloadActive(),
1003 },
1004 );
935 } 1005 }
936 1006
937 return menu; 1007 return menu;
938 } 1008 }
939 1009
940 workspacesMenu() { 1010 workspacesMenu() {
941 const { workspaces, activeWorkspace, isWorkspaceDrawerOpen } = workspaceStore; 1011 const { workspaces, activeWorkspace, isWorkspaceDrawerOpen } =
1012 workspaceStore;
942 const { intl } = window.ferdi; 1013 const { intl } = window.ferdi;
943 const menu = []; 1014 const menu = [];
944 1015
@@ -954,9 +1025,9 @@ export default class FranzMenu {
954 1025
955 // Open workspace drawer: 1026 // Open workspace drawer:
956 if (!this.stores.settings.app.alwaysShowWorkspaces) { 1027 if (!this.stores.settings.app.alwaysShowWorkspaces) {
957 const drawerLabel = ( 1028 const drawerLabel = isWorkspaceDrawerOpen
958 isWorkspaceDrawerOpen ? menuItems.closeWorkspaceDrawer : menuItems.openWorkspaceDrawer 1029 ? menuItems.closeWorkspaceDrawer
959 ); 1030 : menuItems.openWorkspaceDrawer;
960 menu.push({ 1031 menu.push({
961 label: intl.formatMessage(drawerLabel), 1032 label: intl.formatMessage(drawerLabel),
962 accelerator: `${workspaceToggleShortcutKey()}`, 1033 accelerator: `${workspaceToggleShortcutKey()}`,
@@ -983,15 +1054,18 @@ export default class FranzMenu {
983 }); 1054 });
984 1055
985 // Workspace items 1056 // Workspace items
986 workspaces.forEach((workspace, i) => menu.push({ 1057 workspaces.forEach((workspace, i) =>
987 label: workspace.name, 1058 menu.push({
988 accelerator: i < 9 ? `${cmdOrCtrlShortcutKey()}+${altKey()}+${i + 1}` : null, 1059 label: workspace.name,
989 type: 'radio', 1060 accelerator:
990 checked: activeWorkspace ? workspace.id === activeWorkspace.id : false, 1061 i < 9 ? `${cmdOrCtrlShortcutKey()}+${altKey()}+${i + 1}` : null,
991 click: () => { 1062 type: 'radio',
992 workspaceActions.activate({ workspace }); 1063 checked: activeWorkspace ? workspace.id === activeWorkspace.id : false,
993 }, 1064 click: () => {
994 })); 1065 workspaceActions.activate({ workspace });
1066 },
1067 }),
1068 );
995 1069
996 return menu; 1070 return menu;
997 } 1071 }
@@ -1001,7 +1075,9 @@ export default class FranzMenu {
1001 const { intl } = window.ferdi; 1075 const { intl } = window.ferdi;
1002 const menu = []; 1076 const menu = [];
1003 1077
1004 const drawerLabel = isTodosPanelVisible ? menuItems.closeTodosDrawer : menuItems.openTodosDrawer; 1078 const drawerLabel = isTodosPanelVisible
1079 ? menuItems.closeTodosDrawer
1080 : menuItems.openTodosDrawer;
1005 1081
1006 menu.push({ 1082 menu.push({
1007 label: intl.formatMessage(drawerLabel), 1083 label: intl.formatMessage(drawerLabel),
@@ -1013,14 +1089,17 @@ export default class FranzMenu {
1013 }); 1089 });
1014 1090
1015 if (!isFeatureEnabledByUser) { 1091 if (!isFeatureEnabledByUser) {
1016 menu.push({ 1092 menu.push(
1017 type: 'separator', 1093 {
1018 }, { 1094 type: 'separator',
1019 label: intl.formatMessage(menuItems.enableTodos),
1020 click: () => {
1021 todoActions.toggleTodosFeatureVisibility();
1022 }, 1095 },
1023 }); 1096 {
1097 label: intl.formatMessage(menuItems.enableTodos),
1098 click: () => {
1099 todoActions.toggleTodosFeatureVisibility();
1100 },
1101 },
1102 );
1024 } 1103 }
1025 1104
1026 return menu; 1105 return menu;
@@ -1029,28 +1108,31 @@ export default class FranzMenu {
1029 debugMenu() { 1108 debugMenu() {
1030 const { intl } = window.ferdi; 1109 const { intl } = window.ferdi;
1031 1110
1032 return [{ 1111 return [
1033 label: intl.formatMessage(menuItems.debugInfo), 1112 {
1034 click: () => { 1113 label: intl.formatMessage(menuItems.debugInfo),
1035 const { debugInfo } = this.stores.app; 1114 click: () => {
1115 const { debugInfo } = this.stores.app;
1036 1116
1037 clipboard.write({ 1117 clipboard.write({
1038 text: JSON.stringify(debugInfo), 1118 text: JSON.stringify(debugInfo),
1039 }); 1119 });
1040 1120
1041 this.actions.app.notify({ 1121 this.actions.app.notify({
1042 title: intl.formatMessage(menuItems.debugInfoCopiedHeadline), 1122 title: intl.formatMessage(menuItems.debugInfoCopiedHeadline),
1043 options: { 1123 options: {
1044 body: intl.formatMessage(menuItems.debugInfoCopiedBody), 1124 body: intl.formatMessage(menuItems.debugInfoCopiedBody),
1045 }, 1125 },
1046 }); 1126 });
1127 },
1047 }, 1128 },
1048 }, { 1129 {
1049 label: intl.formatMessage(menuItems.publishDebugInfo), 1130 label: intl.formatMessage(menuItems.publishDebugInfo),
1050 click: () => { 1131 click: () => {
1051 window.ferdi.features.publishDebugInfo.state.isModalVisible = true; 1132 window.ferdi.features.publishDebugInfo.state.isModalVisible = true;
1133 },
1052 }, 1134 },
1053 }]; 1135 ];
1054 } 1136 }
1055 1137
1056 _getServiceName(service) { 1138 _getServiceName(service) {
diff --git a/src/routes.js b/src/routes.js
index d67ccee13..5af0c4db9 100644
--- a/src/routes.js
+++ b/src/routes.js
@@ -1,9 +1,7 @@
1import React, { Component } from 'react'; 1import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { inject, observer } from 'mobx-react'; 3import { inject, observer } from 'mobx-react';
4import { 4import { Router, Route, IndexRedirect } from 'react-router';
5 Router, Route, IndexRedirect,
6} from 'react-router';
7 5
8import AppLayoutContainer from './containers/layout/AppLayoutContainer'; 6import AppLayoutContainer from './containers/layout/AppLayoutContainer';
9import SettingsWindow from './containers/settings/SettingsWindow'; 7import SettingsWindow from './containers/settings/SettingsWindow';
@@ -29,38 +27,39 @@ import AuthLayoutContainer from './containers/auth/AuthLayoutContainer';
29import WorkspacesScreen from './features/workspaces/containers/WorkspacesScreen'; 27import WorkspacesScreen from './features/workspaces/containers/WorkspacesScreen';
30import EditWorkspaceScreen from './features/workspaces/containers/EditWorkspaceScreen'; 28import EditWorkspaceScreen from './features/workspaces/containers/EditWorkspaceScreen';
31import { WORKSPACES_ROUTES } from './features/workspaces/constants'; 29import { WORKSPACES_ROUTES } from './features/workspaces/constants';
32import AnnouncementScreen from './features/announcements/components/AnnouncementScreen';
33import { ANNOUNCEMENTS_ROUTES } from './features/announcements/constants';
34 30
35import SettingsStore from './stores/SettingsStore'; 31import SettingsStore from './stores/SettingsStore';
36 32
37export default @inject('stores', 'actions') @observer class Routes extends Component { 33export default
34@inject('stores', 'actions')
35@observer
36class Routes extends Component {
38 render() { 37 render() {
39 const { 38 const { locked, lockingFeatureEnabled } = this.props.stores.settings.app;
40 locked,
41 lockingFeatureEnabled,
42 } = this.props.stores.settings.app;
43 39
44 const { history } = this.props; 40 const { history } = this.props;
45 41
46 if (lockingFeatureEnabled && locked) { 42 if (lockingFeatureEnabled && locked) {
47 return ( 43 return <LockedScreen />;
48 <LockedScreen />
49 );
50 } 44 }
51 45
52 return ( 46 return (
53 <Router history={history}> 47 <Router history={history}>
54 <Route path="/" component={AppLayoutContainer}> 48 <Route path="/" component={AppLayoutContainer}>
55 <Route path={ANNOUNCEMENTS_ROUTES.TARGET} component={AnnouncementScreen} />
56 <Route path="/settings" component={SettingsWindow}> 49 <Route path="/settings" component={SettingsWindow}>
57 <IndexRedirect to="/settings/recipes" /> 50 <IndexRedirect to="/settings/recipes" />
58 <Route path="/settings/recipes" component={RecipesScreen} /> 51 <Route path="/settings/recipes" component={RecipesScreen} />
59 <Route path="/settings/recipes/:filter" component={RecipesScreen} /> 52 <Route path="/settings/recipes/:filter" component={RecipesScreen} />
60 <Route path="/settings/services" component={ServicesScreen} /> 53 <Route path="/settings/services" component={ServicesScreen} />
61 <Route path="/settings/services/:action/:id" component={EditServiceScreen} /> 54 <Route
55 path="/settings/services/:action/:id"
56 component={EditServiceScreen}
57 />
62 <Route path={WORKSPACES_ROUTES.ROOT} component={WorkspacesScreen} /> 58 <Route path={WORKSPACES_ROUTES.ROOT} component={WorkspacesScreen} />
63 <Route path={WORKSPACES_ROUTES.EDIT} component={EditWorkspaceScreen} /> 59 <Route
60 path={WORKSPACES_ROUTES.EDIT}
61 component={EditWorkspaceScreen}
62 />
64 <Route path="/settings/user" component={AccountScreen} /> 63 <Route path="/settings/user" component={AccountScreen} />
65 <Route path="/settings/user/edit" component={EditUserScreen} /> 64 <Route path="/settings/user/edit" component={EditUserScreen} />
66 <Route path="/settings/team" component={TeamScreen} /> 65 <Route path="/settings/team" component={TeamScreen} />
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js
index cb8c71268..469e7519e 100644
--- a/src/stores/AppStore.js
+++ b/src/stores/AppStore.js
@@ -48,7 +48,8 @@ const autoLauncher = new AutoLaunch({
48 path: executablePath, 48 path: executablePath,
49}); 49});
50 50
51const CATALINA_NOTIFICATION_HACK_KEY = '_temp_askedForCatalinaNotificationPermissions'; 51const CATALINA_NOTIFICATION_HACK_KEY =
52 '_temp_askedForCatalinaNotificationPermissions';
52 53
53export default class AppStore extends Store { 54export default class AppStore extends Store {
54 updateStatusTypes = { 55 updateStatusTypes = {
@@ -92,8 +93,6 @@ export default class AppStore extends Store {
92 93
93 @observable isFocused = true; 94 @observable isFocused = true;
94 95
95 @observable nextAppReleaseVersion = null;
96
97 dictionaries = []; 96 dictionaries = [];
98 97
99 fetchDataInterval = null; 98 fetchDataInterval = null;
@@ -175,7 +174,6 @@ export default class AppStore extends Store {
175 ipcRenderer.on('autoUpdate', (event, data) => { 174 ipcRenderer.on('autoUpdate', (event, data) => {
176 if (data.available) { 175 if (data.available) {
177 this.updateStatus = this.updateStatusTypes.AVAILABLE; 176 this.updateStatus = this.updateStatusTypes.AVAILABLE;
178 this.nextAppReleaseVersion = data.version;
179 if (isMac) { 177 if (isMac) {
180 app.dock.bounce(); 178 app.dock.bounce();
181 } 179 }
@@ -236,8 +234,8 @@ export default class AppStore extends Store {
236 this.actions.service.resetLastPollTimer(); 234 this.actions.service.resetLastPollTimer();
237 235
238 if ( 236 if (
239 this.timeSuspensionStart.add(10, 'm').isBefore(moment()) 237 this.timeSuspensionStart.add(10, 'm').isBefore(moment()) &&
240 && this.stores.settings.app.get('reloadAfterResume') 238 this.stores.settings.app.get('reloadAfterResume')
241 ) { 239 ) {
242 debug('Reloading services, user info and features'); 240 debug('Reloading services, user info and features');
243 241
@@ -283,15 +281,15 @@ export default class AppStore extends Store {
283 ferdi: { 281 ferdi: {
284 version: ferdiVersion, 282 version: ferdiVersion,
285 electron: electronVersion, 283 electron: electronVersion,
286 installedRecipes: this.stores.recipes.all.map((recipe) => ({ 284 installedRecipes: this.stores.recipes.all.map(recipe => ({
287 id: recipe.id, 285 id: recipe.id,
288 version: recipe.version, 286 version: recipe.version,
289 })), 287 })),
290 devRecipes: this.stores.recipePreviews.dev.map((recipe) => ({ 288 devRecipes: this.stores.recipePreviews.dev.map(recipe => ({
291 id: recipe.id, 289 id: recipe.id,
292 version: recipe.version, 290 version: recipe.version,
293 })), 291 })),
294 services: this.stores.services.all.map((service) => ({ 292 services: this.stores.services.all.map(service => ({
295 id: service.id, 293 id: service.id,
296 recipe: service.recipe.id, 294 recipe: service.recipe.id,
297 isAttached: service.isAttached, 295 isAttached: service.isAttached,
@@ -302,7 +300,7 @@ export default class AppStore extends Store {
302 isDarkModeEnabled: service.isDarkModeEnabled, 300 isDarkModeEnabled: service.isDarkModeEnabled,
303 })), 301 })),
304 messages: this.stores.globalError.messages, 302 messages: this.stores.globalError.messages,
305 workspaces: this.stores.workspaces.workspaces.map((workspace) => ({ 303 workspaces: this.stores.workspaces.workspaces.map(workspace => ({
306 id: workspace.id, 304 id: workspace.id,
307 services: workspace.services, 305 services: workspace.services,
308 })), 306 })),
@@ -315,9 +313,7 @@ export default class AppStore extends Store {
315 } 313 }
316 314
317 // Actions 315 // Actions
318 @action _notify({ 316 @action _notify({ title, options, notificationId, serviceId = null }) {
319 title, options, notificationId, serviceId = null,
320 }) {
321 if (this.stores.settings.all.app.isAppMuted) return; 317 if (this.stores.settings.all.app.isAppMuted) return;
322 318
323 // TODO: is there a simple way to use blobs for notifications without storing them on disk? 319 // TODO: is there a simple way to use blobs for notifications without storing them on disk?
@@ -359,8 +355,8 @@ export default class AppStore extends Store {
359 if (indicator === 0 && unreadIndirectMessageCount !== 0) { 355 if (indicator === 0 && unreadIndirectMessageCount !== 0) {
360 indicator = '•'; 356 indicator = '•';
361 } else if ( 357 } else if (
362 unreadDirectMessageCount === 0 358 unreadDirectMessageCount === 0 &&
363 && unreadIndirectMessageCount === 0 359 unreadIndirectMessageCount === 0
364 ) { 360 ) {
365 indicator = 0; 361 indicator = 0;
366 } else { 362 } else {
@@ -441,22 +437,25 @@ export default class AppStore extends Store {
441 const clearAppCache = this.clearAppCacheRequest.execute(); 437 const clearAppCache = this.clearAppCacheRequest.execute();
442 const allServiceIds = await getServiceIdsFromPartitions(); 438 const allServiceIds = await getServiceIdsFromPartitions();
443 const allOrphanedServiceIds = allServiceIds.filter( 439 const allOrphanedServiceIds = allServiceIds.filter(
444 (id) => !this.stores.services.all.find( 440 id =>
445 (s) => id.replace('service-', '') === s.id, 441 !this.stores.services.all.find(
446 ), 442 s => id.replace('service-', '') === s.id,
443 ),
447 ); 444 );
448 445
449 try { 446 try {
450 await Promise.all( 447 await Promise.all(
451 allOrphanedServiceIds.map((id) => removeServicePartitionDirectory(id)), 448 allOrphanedServiceIds.map(id => removeServicePartitionDirectory(id)),
452 ); 449 );
453 } catch (ex) { 450 } catch (ex) {
454 console.log('Error while deleting service partition directory - ', ex); 451 console.log('Error while deleting service partition directory - ', ex);
455 } 452 }
456 await Promise.all( 453 await Promise.all(
457 this.stores.services.all.map((s) => this.actions.service.clearCache({ 454 this.stores.services.all.map(s =>
458 serviceId: s.id, 455 this.actions.service.clearCache({
459 })), 456 serviceId: s.id,
457 }),
458 ),
460 ); 459 );
461 460
462 await clearAppCache._promise; 461 await clearAppCache._promise;
@@ -488,9 +487,9 @@ export default class AppStore extends Store {
488 } 487 }
489 488
490 if ( 489 if (
491 locale 490 locale &&
492 && Object.prototype.hasOwnProperty.call(locales, locale) 491 Object.prototype.hasOwnProperty.call(locales, locale) &&
493 && locale !== this.locale 492 locale !== this.locale
494 ) { 493 ) {
495 this.locale = locale; 494 this.locale = locale;
496 } else if (!locale) { 495 } else if (!locale) {
@@ -568,8 +567,8 @@ export default class AppStore extends Store {
568 const dnd = await ipcRenderer.invoke('get-dnd'); 567 const dnd = await ipcRenderer.invoke('get-dnd');
569 debug('Do not disturb mode is', dnd); 568 debug('Do not disturb mode is', dnd);
570 if ( 569 if (
571 dnd !== this.stores.settings.all.app.isAppMuted 570 dnd !== this.stores.settings.all.app.isAppMuted &&
572 && !this.isSystemMuteOverridden 571 !this.isSystemMuteOverridden
573 ) { 572 ) {
574 this.actions.app.muteApp({ 573 this.actions.app.muteApp({
575 isMuted: dnd, 574 isMuted: dnd,
diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js
index 0dff2fda2..1d50dd714 100644
--- a/src/stores/FeaturesStore.js
+++ b/src/stores/FeaturesStore.js
@@ -1,8 +1,4 @@
1import { 1import { computed, observable, runInAction } from 'mobx';
2 computed,
3 observable,
4 runInAction,
5} from 'mobx';
6 2
7import Store from './lib/Store'; 3import Store from './lib/Store';
8import CachedRequest from './lib/CachedRequest'; 4import CachedRequest from './lib/CachedRequest';
@@ -13,7 +9,6 @@ import workspaces from '../features/workspaces';
13import quickSwitch from '../features/quickSwitch'; 9import quickSwitch from '../features/quickSwitch';
14import nightlyBuilds from '../features/nightlyBuilds'; 10import nightlyBuilds from '../features/nightlyBuilds';
15import publishDebugInfo from '../features/publishDebugInfo'; 11import publishDebugInfo from '../features/publishDebugInfo';
16import announcements from '../features/announcements';
17import settingsWS from '../features/settingsWS'; 12import settingsWS from '../features/settingsWS';
18import communityRecipes from '../features/communityRecipes'; 13import communityRecipes from '../features/communityRecipes';
19import todos from '../features/todos'; 14import todos from '../features/todos';
@@ -22,11 +17,17 @@ import appearance from '../features/appearance';
22import { DEFAULT_FEATURES_CONFIG } from '../config'; 17import { DEFAULT_FEATURES_CONFIG } from '../config';
23 18
24export default class FeaturesStore extends Store { 19export default class FeaturesStore extends Store {
25 @observable defaultFeaturesRequest = new CachedRequest(this.api.features, 'default'); 20 @observable defaultFeaturesRequest = new CachedRequest(
21 this.api.features,
22 'default',
23 );
26 24
27 @observable featuresRequest = new CachedRequest(this.api.features, 'features'); 25 @observable featuresRequest = new CachedRequest(
26 this.api.features,
27 'features',
28 );
28 29
29 @observable features = ({ ...DEFAULT_FEATURES_CONFIG }); 30 @observable features = { ...DEFAULT_FEATURES_CONFIG };
30 31
31 async setup() { 32 async setup() {
32 this.registerReactions([ 33 this.registerReactions([
@@ -39,7 +40,9 @@ export default class FeaturesStore extends Store {
39 } 40 }
40 41
41 @computed get anonymousFeatures() { 42 @computed get anonymousFeatures() {
42 return this.defaultFeaturesRequest.execute().result || DEFAULT_FEATURES_CONFIG; 43 return (
44 this.defaultFeaturesRequest.execute().result || DEFAULT_FEATURES_CONFIG
45 );
43 } 46 }
44 47
45 _updateFeatures = () => { 48 _updateFeatures = () => {
@@ -72,7 +75,6 @@ export default class FeaturesStore extends Store {
72 quickSwitch(this.stores, this.actions); 75 quickSwitch(this.stores, this.actions);
73 nightlyBuilds(this.stores, this.actions); 76 nightlyBuilds(this.stores, this.actions);
74 publishDebugInfo(this.stores, this.actions); 77 publishDebugInfo(this.stores, this.actions);
75 announcements(this.stores, this.actions);
76 settingsWS(this.stores, this.actions); 78 settingsWS(this.stores, this.actions);
77 communityRecipes(this.stores, this.actions); 79 communityRecipes(this.stores, this.actions);
78 todos(this.stores, this.actions); 80 todos(this.stores, this.actions);
diff --git a/src/stores/index.ts b/src/stores/index.ts
index e980f2c5b..4cd4e92ea 100644
--- a/src/stores/index.ts
+++ b/src/stores/index.ts
@@ -10,7 +10,6 @@ import NewsStore from './NewsStore';
10import RequestStore from './RequestStore'; 10import RequestStore from './RequestStore';
11import GlobalErrorStore from './GlobalErrorStore'; 11import GlobalErrorStore from './GlobalErrorStore';
12import { workspaceStore } from '../features/workspaces'; 12import { workspaceStore } from '../features/workspaces';
13import { announcementsStore } from '../features/announcements';
14import { communityRecipesStore } from '../features/communityRecipes'; 13import { communityRecipesStore } from '../features/communityRecipes';
15import { todosStore } from '../features/todos'; 14import { todosStore } from '../features/todos';
16 15
@@ -30,13 +29,12 @@ export default (api, actions, router) => {
30 requests: new RequestStore(stores, api, actions), 29 requests: new RequestStore(stores, api, actions),
31 globalError: new GlobalErrorStore(stores, api, actions), 30 globalError: new GlobalErrorStore(stores, api, actions),
32 workspaces: workspaceStore, 31 workspaces: workspaceStore,
33 announcements: announcementsStore,
34 communityRecipes: communityRecipesStore, 32 communityRecipes: communityRecipesStore,
35 todos: todosStore, 33 todos: todosStore,
36 }); 34 });
37 35
38 // Initialize all stores 36 // Initialize all stores
39 Object.keys(stores).forEach((name) => { 37 Object.keys(stores).forEach(name => {
40 if (stores[name] && stores[name].initialize) { 38 if (stores[name] && stores[name].initialize) {
41 stores[name].initialize(); 39 stores[name].initialize();
42 } 40 }