aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-04-11 16:44:16 +0200
committerLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-04-11 16:44:16 +0200
commiteaf4aff646eed56e65c8dd8e70143ab5634ad4b4 (patch)
treeae400dca67edfd828a30da1e11d7e5e507785860 /src
parentrefactor announcements to newest feature pattern (diff)
downloadferdium-app-eaf4aff646eed56e65c8dd8e70143ab5634ad4b4.tar.gz
ferdium-app-eaf4aff646eed56e65c8dd8e70143ab5634ad4b4.tar.zst
ferdium-app-eaf4aff646eed56e65c8dd8e70143ab5634ad4b4.zip
WIP: announcement feature and workspace fixes
Diffstat (limited to 'src')
-rw-r--r--src/features/announcements/actions.js4
-rw-r--r--src/features/announcements/api.js18
-rw-r--r--src/features/announcements/components/AnnouncementScreen.js152
-rw-r--r--src/features/announcements/store.js151
-rw-r--r--src/features/utils/FeatureStore.js29
-rw-r--r--src/features/workspaces/store.js18
-rw-r--r--src/i18n/locales/defaultMessages.json6
-rw-r--r--src/i18n/locales/en-US.json6
-rw-r--r--src/i18n/messages/src/features/announcements/Component.json4
-rw-r--r--src/i18n/messages/src/features/announcements/components/AnnouncementScreen.json8
-rw-r--r--src/i18n/messages/src/lib/Menu.json214
-rw-r--r--src/lib/Menu.js15
-rw-r--r--src/stores/index.js4
13 files changed, 405 insertions, 224 deletions
diff --git a/src/features/announcements/actions.js b/src/features/announcements/actions.js
index 68b262ded..bab496314 100644
--- a/src/features/announcements/actions.js
+++ b/src/features/announcements/actions.js
@@ -2,7 +2,9 @@ import PropTypes from 'prop-types';
2import { createActionsFromDefinitions } from '../../actions/lib/actions'; 2import { createActionsFromDefinitions } from '../../actions/lib/actions';
3 3
4export const announcementActions = createActionsFromDefinitions({ 4export const announcementActions = createActionsFromDefinitions({
5 show: {}, 5 show: {
6 targetVersion: PropTypes.string,
7 },
6}, PropTypes.checkPropTypes); 8}, PropTypes.checkPropTypes);
7 9
8export default announcementActions; 10export default announcementActions;
diff --git a/src/features/announcements/api.js b/src/features/announcements/api.js
index 09fcb8235..a581bd8de 100644
--- a/src/features/announcements/api.js
+++ b/src/features/announcements/api.js
@@ -1,5 +1,6 @@
1import { remote } from 'electron'; 1import { remote } from 'electron';
2import Request from '../../stores/lib/Request'; 2import Request from '../../stores/lib/Request';
3import { API, API_VERSION } from '../../environment';
3 4
4const debug = require('debug')('Franz:feature:announcements:api'); 5const debug = require('debug')('Franz:feature:announcements:api');
5 6
@@ -9,15 +10,24 @@ export const announcementsApi = {
9 return Promise.resolve(remote.app.getVersion()); 10 return Promise.resolve(remote.app.getVersion());
10 }, 11 },
11 12
12 async getAnnouncementForVersion(version) { 13 async getChangelog(version) {
13 debug('fetching release announcement from Github'); 14 debug('fetching release changelog from Github');
14 const url = `https://api.github.com/repos/meetfranz/franz/releases/tags/v${version}`; 15 const url = `https://api.github.com/repos/meetfranz/franz/releases/tags/v${version}`;
15 const request = await window.fetch(url, { method: 'GET' }); 16 const request = await window.fetch(url, { method: 'GET' });
16 if (!request.ok) throw request; 17 if (!request.ok) return null;
17 const data = await request.json(); 18 const data = await request.json();
18 return data.body; 19 return data.body;
19 }, 20 },
21
22 async getAnnouncement(version) {
23 debug('fetching release announcement from api');
24 const url = `${API}/${API_VERSION}/announcements/${version}`;
25 const response = await window.fetch(url, { method: 'GET' });
26 if (!response.ok) return null;
27 return response.json();
28 },
20}; 29};
21 30
22export const getCurrentVersionRequest = new Request(announcementsApi, 'getCurrentVersion'); 31export const getCurrentVersionRequest = new Request(announcementsApi, 'getCurrentVersion');
23export const getAnnouncementRequest = new Request(announcementsApi, 'getAnnouncementForVersion'); 32export const getChangelogRequest = new Request(announcementsApi, 'getChangelog');
33export const getAnnouncementRequest = new Request(announcementsApi, 'getAnnouncement');
diff --git a/src/features/announcements/components/AnnouncementScreen.js b/src/features/announcements/components/AnnouncementScreen.js
index 5b3e7aeaa..2d5efc396 100644
--- a/src/features/announcements/components/AnnouncementScreen.js
+++ b/src/features/announcements/components/AnnouncementScreen.js
@@ -4,27 +4,29 @@ import PropTypes from 'prop-types';
4import { inject, observer } from 'mobx-react'; 4import { inject, observer } from 'mobx-react';
5import { defineMessages, intlShape } from 'react-intl'; 5import { defineMessages, intlShape } from 'react-intl';
6import injectSheet from 'react-jss'; 6import injectSheet from 'react-jss';
7import { themeSidebarWidth } from '../../../../packages/theme/lib/themes/legacy'; 7import { Button } from '@meetfranz/forms';
8
8import { announcementsStore } from '../index'; 9import { announcementsStore } from '../index';
10import UIStore from '../../../stores/UIStore';
9 11
10const messages = defineMessages({ 12const messages = defineMessages({
11 headline: { 13 headline: {
12 id: 'feature.announcements.headline', 14 id: 'feature.announcements.changelog.headline',
13 defaultMessage: '!!!What\'s new in Franz {version}?', 15 defaultMessage: '!!!Changes in Franz {version}',
14 }, 16 },
15}); 17});
16 18
19const smallScreen = '1000px';
20
17const styles = theme => ({ 21const styles = theme => ({
18 container: { 22 container: {
19 background: theme.colorBackground, 23 background: theme.colorBackground,
20 position: 'absolute', 24 position: 'absolute',
21 top: 0, 25 top: 0,
22 zIndex: 140, 26 zIndex: 140,
23 width: `calc(100% - ${themeSidebarWidth})`, 27 width: '100%',
24 display: 'flex', 28 height: '100%',
25 'flex-direction': 'column', 29 overflowY: 'auto',
26 'align-items': 'center',
27 'justify-content': 'center',
28 }, 30 },
29 headline: { 31 headline: {
30 color: theme.colorHeadline, 32 color: theme.colorHeadline,
@@ -33,7 +35,76 @@ const styles = theme => ({
33 'text-align': 'center', 35 'text-align': 'center',
34 'line-height': '1.3em', 36 'line-height': '1.3em',
35 }, 37 },
36 body: { 38 announcement: {
39 height: '100vh',
40 display: 'flex',
41 flexDirection: 'column',
42 justifyContent: 'center',
43 },
44 main: {
45 flexGrow: 1,
46 '& h1': {
47 marginTop: 40,
48 fontSize: 50,
49 color: theme.styleTypes.primary.accent,
50 textAlign: 'center',
51 [`@media(min-width: ${smallScreen})`]: {
52 marginTop: 75,
53 },
54 },
55 '& h2': {
56 fontSize: 24,
57 fontWeight: 300,
58 color: theme.colorText,
59 textAlign: 'center',
60 },
61 },
62 mainBody: {
63 display: 'flex',
64 flexDirection: 'column',
65 alignItems: 'center',
66 width: 'calc(100% - 80px)',
67 height: 'auto',
68 margin: '0 auto',
69 [`@media(min-width: ${smallScreen})`]: {
70 flexDirection: 'row',
71 justifyContent: 'center',
72 },
73 },
74 mainImage: {
75 minWidth: 250,
76 maxWidth: 400,
77 margin: '0 auto',
78 marginBottom: 40,
79 '& img': {
80 width: '100%',
81 },
82 [`@media(min-width: ${smallScreen})`]: {
83 margin: 0,
84 },
85 },
86 mainText: {
87 height: 'auto',
88 maxWidth: 600,
89 textAlign: 'center',
90 '& p': {
91 lineHeight: '1.5em',
92 },
93 [`@media(min-width: ${smallScreen})`]: {
94 textAlign: 'left',
95 },
96 },
97 mainCtaButton: {
98 textAlign: 'center',
99 marginTop: 40,
100 [`@media(min-width: ${smallScreen})`]: {
101 textAlign: 'left',
102 },
103 },
104 spotlight: {
105 height: 'auto',
106 },
107 changelog: {
37 '& h3': { 108 '& h3': {
38 fontSize: '24px', 109 fontSize: '24px',
39 margin: '1.5em 0 1em 0', 110 margin: '1.5em 0 1em 0',
@@ -45,10 +116,13 @@ const styles = theme => ({
45}); 116});
46 117
47 118
48@inject('actions') @injectSheet(styles) @observer 119@inject('stores', 'actions') @injectSheet(styles) @observer
49class AnnouncementScreen extends Component { 120class AnnouncementScreen extends Component {
50 static propTypes = { 121 static propTypes = {
51 classes: PropTypes.object.isRequired, 122 classes: PropTypes.object.isRequired,
123 stores: PropTypes.shape({
124 ui: PropTypes.instanceOf(UIStore).isRequired,
125 }).isRequired,
52 }; 126 };
53 127
54 static contextTypes = { 128 static contextTypes = {
@@ -56,21 +130,55 @@ class AnnouncementScreen extends Component {
56 }; 130 };
57 131
58 render() { 132 render() {
59 const { classes } = this.props; 133 const { classes, stores } = this.props;
60 const { intl } = this.context; 134 const { intl } = this.context;
135 const { changelog, announcement } = announcementsStore;
136 const themeImage = stores.ui.isDarkThemeActive ? 'dark' : 'light';
61 return ( 137 return (
62 <div className={`${classes.container}`}> 138 <div className={`${classes.container}`}>
63 <h1 className={classes.headline}> 139 <div className={classes.announcement}>
64 {intl.formatMessage(messages.headline, { 140 <div className={classes.main}>
65 version: announcementsStore.currentVersion, 141 <h1>{announcement.main.headline}</h1>
66 })} 142 <h2>{announcement.main.subHeadline}</h2>
67 </h1> 143 <div className={classes.mainBody}>
68 <div 144 <div className={classes.mainImage}>
69 className={classes.body} 145 <img
70 dangerouslySetInnerHTML={{ 146 src={announcement.main.image[themeImage]}
71 __html: marked(announcementsStore.announcement, { sanitize: true }), 147 alt=""
72 }} 148 />
73 /> 149 </div>
150 <div className={classes.mainText}>
151 <p
152 dangerouslySetInnerHTML={{
153 __html: marked(announcement.main.text,{ sanitize: true }),
154 }}
155 />
156 <div className={classes.mainCtaButton}>
157 <Button label={announcement.main.cta.label} />
158 </div>
159 </div>
160 </div>
161 </div>
162 {announcement.spotlight && (
163 <div className={classes.spotlight}>
164 <h2>{announcement.spotlight.title}</h2>
165 </div>
166 )}
167 </div>
168 {changelog && (
169 <div className={classes.changelog}>
170 <h1 className={classes.headline}>
171 {intl.formatMessage(messages.headline, {
172 version: announcementsStore.currentVersion,
173 })}
174 </h1>
175 <div
176 dangerouslySetInnerHTML={{
177 __html: marked(changelog, { sanitize: true }),
178 }}
179 />
180 </div>
181 )}
74 </div> 182 </div>
75 ); 183 );
76 } 184 }
diff --git a/src/features/announcements/store.js b/src/features/announcements/store.js
index c59700926..d4fb0a52c 100644
--- a/src/features/announcements/store.js
+++ b/src/features/announcements/store.js
@@ -1,96 +1,93 @@
1import { action, observable, reaction } from 'mobx'; 1import {
2 action,
3 computed,
4 observable,
5 reaction,
6} from 'mobx';
2import semver from 'semver'; 7import semver from 'semver';
8import localStorage from 'mobx-localstorage';
9
3import { FeatureStore } from '../utils/FeatureStore'; 10import { FeatureStore } from '../utils/FeatureStore';
4import { getAnnouncementRequest, getCurrentVersionRequest } from './api'; 11import { getAnnouncementRequest, getChangelogRequest, getCurrentVersionRequest } from './api';
12import { announcementActions } from './actions';
13
14const LOCAL_STORAGE_KEY = 'announcements';
5 15
6const debug = require('debug')('Franz:feature:announcements:store'); 16const debug = require('debug')('Franz:feature:announcements:store');
7 17
8export class AnnouncementsStore extends FeatureStore { 18export class AnnouncementsStore extends FeatureStore {
9 19 @observable targetVersion = null;
10 @observable announcement = null;
11
12 @observable currentVersion = null;
13
14 @observable lastUsedVersion = null;
15 20
16 @observable isAnnouncementVisible = false; 21 @observable isAnnouncementVisible = false;
17 22
18 @observable isFeatureActive = false; 23 @observable isFeatureActive = false;
19 24
20 async start(stores, actions) { 25 @computed get changelog() {
21 debug('AnnouncementsStore::start'); 26 return getChangelogRequest.result;
22 this.stores = stores;
23 this.actions = actions;
24 await this.fetchLastUsedVersion();
25 await this.fetchCurrentVersion();
26 await this.fetchReleaseAnnouncement();
27 this.showAnnouncementIfNotSeenYet();
28
29 this.actions.announcements.show.listen(this._showAnnouncement.bind(this));
30 this.isFeatureActive = true;
31 } 27 }
32 28
33 stop() { 29 @computed get announcement() {
34 debug('AnnouncementsStore::stop'); 30 return getAnnouncementRequest.result;
35 this.isFeatureActive = false;
36 this.isAnnouncementVisible = false;
37 } 31 }
38 32
39 // ====== PUBLIC ====== 33 @computed get settings() {
40 34 return localStorage.getItem(LOCAL_STORAGE_KEY) || {};
41 async fetchLastUsedVersion() {
42 debug('getting last used version from local storage');
43 const lastUsedVersion = window.localStorage.getItem('lastUsedVersion');
44 this._setLastUsedVersion(lastUsedVersion == null ? '0.0.0' : lastUsedVersion);
45 } 35 }
46 36
47 async fetchCurrentVersion() { 37 @computed get lastSeenAnnouncementVersion() {
48 debug('getting current version from api'); 38 return this.settings.lastSeenAnnouncementVersion || null;
49 const version = await getCurrentVersionRequest.execute();
50 this._setCurrentVersion(version);
51 } 39 }
52 40
53 async fetchReleaseAnnouncement() { 41 @computed get currentVersion() {
54 debug('getting release announcement from api'); 42 return getCurrentVersionRequest.result;
55 try {
56 const announcement = await getAnnouncementRequest.execute(this.currentVersion);
57 this._setAnnouncement(announcement);
58 } catch (error) {
59 this._setAnnouncement(null);
60 }
61 } 43 }
62 44
63 showAnnouncementIfNotSeenYet() { 45 @computed get isNewUser() {
64 const { announcement, currentVersion, lastUsedVersion } = this; 46 return this.stores.settings.stats.appStarts <= 1;
65 if (announcement && semver.gt(currentVersion, lastUsedVersion)) {
66 debug(`${currentVersion} < ${lastUsedVersion}: announcement is shown`);
67 this._showAnnouncement();
68 } else {
69 debug(`${currentVersion} >= ${lastUsedVersion}: announcement is hidden`);
70 this._hideAnnouncement();
71 }
72 } 47 }
73 48
74 // ====== PRIVATE ====== 49 async start(stores, actions) {
50 debug('AnnouncementsStore::start');
51 this.stores = stores;
52 this.actions = actions;
53 getCurrentVersionRequest.execute();
75 54
76 @action _setCurrentVersion(version) { 55 this._registerActions([
77 debug(`setting current version to ${version}`); 56 [announcementActions.show, this._showAnnouncement],
78 this.currentVersion = version; 57 ]);
79 }
80 58
81 @action _setLastUsedVersion(version) { 59 this._registerReactions([
82 debug(`setting last used version to ${version}`); 60 this._fetchAnnouncements,
83 this.lastUsedVersion = version; 61 this._showAnnouncementToUsersWhoUpdatedApp,
62 ]);
63 this.isFeatureActive = true;
84 } 64 }
85 65
86 @action _setAnnouncement(announcement) { 66 stop() {
87 debug(`setting announcement to ${announcement}`); 67 super.stop();
88 this.announcement = announcement; 68 debug('AnnouncementsStore::stop');
69 this.isFeatureActive = false;
70 this.isAnnouncementVisible = false;
89 } 71 }
90 72
91 @action _showAnnouncement() { 73 // ======= HELPERS ======= //
74
75 _updateSettings = (changes) => {
76 localStorage.setItem(LOCAL_STORAGE_KEY, {
77 ...this.settings,
78 ...changes,
79 });
80 };
81
82 // ======= ACTIONS ======= //
83
84 @action _showAnnouncement = ({ targetVersion } = {}) => {
85 this.targetVersion = targetVersion || this.currentVersion;
92 this.isAnnouncementVisible = true; 86 this.isAnnouncementVisible = true;
93 this.actions.service.blurActive(); 87 this.actions.service.blurActive();
88 this._updateSettings({
89 lastSeenAnnouncementVersion: this.currentVersion,
90 });
94 const dispose = reaction( 91 const dispose = reaction(
95 () => this.stores.services.active, 92 () => this.stores.services.active,
96 () => { 93 () => {
@@ -98,9 +95,37 @@ export class AnnouncementsStore extends FeatureStore {
98 dispose(); 95 dispose();
99 }, 96 },
100 ); 97 );
101 } 98 };
102 99
103 @action _hideAnnouncement() { 100 @action _hideAnnouncement() {
104 this.isAnnouncementVisible = false; 101 this.isAnnouncementVisible = false;
105 } 102 }
103
104 // ======= REACTIONS ========
105
106 _showAnnouncementToUsersWhoUpdatedApp = () => {
107 const { announcement, isNewUser } = this;
108 console.log(announcement, isNewUser);
109 // Check if there is an announcement and on't show announcements to new users
110 if (!announcement || isNewUser) return;
111
112 this._showAnnouncement();
113
114 // Check if the user has already used current version (= has seen the announcement)
115 // const { currentVersion, lastSeenAnnouncementVersion } = this;
116 // if (semver.gt(currentVersion, lastSeenAnnouncementVersion)) {
117 // debug(`${currentVersion} < ${lastSeenAnnouncementVersion}: announcement is shown`);
118 // this._showAnnouncement();
119 // } else {
120 // debug(`${currentVersion} >= ${lastSeenAnnouncementVersion}: announcement is hidden`);
121 // this._hideAnnouncement();
122 // }
123 };
124
125 _fetchAnnouncements = () => {
126 const targetVersion = this.targetVersion || this.currentVersion;
127 if (!targetVersion) return;
128 getChangelogRequest.execute('5.0.1');
129 getAnnouncementRequest.execute('5.1.0');
130 }
106} 131}
diff --git a/src/features/utils/FeatureStore.js b/src/features/utils/FeatureStore.js
index 66b66a104..48962561d 100644
--- a/src/features/utils/FeatureStore.js
+++ b/src/features/utils/FeatureStore.js
@@ -5,17 +5,38 @@ export class FeatureStore {
5 5
6 _reactions = null; 6 _reactions = null;
7 7
8 _listenToActions(actions) { 8 _registerActions(actions) {
9 if (this._actions) this._actions.forEach(a => a[0].off(a[1]));
10 this._actions = []; 9 this._actions = [];
11 actions.forEach(a => this._actions.push(a)); 10 actions.forEach(a => this._actions.push(a));
11 this._startListeningToActions();
12 }
13
14 _startListeningToActions() {
15 this._stopListeningToActions();
12 this._actions.forEach(a => a[0].listen(a[1])); 16 this._actions.forEach(a => a[0].listen(a[1]));
13 } 17 }
14 18
15 _startReactions(reactions) { 19 _stopListeningToActions() {
16 if (this._reactions) this._reactions.forEach(r => r.stop()); 20 this._actions.forEach(a => a[0].off(a[1]));
21 }
22
23 _registerReactions(reactions) {
17 this._reactions = []; 24 this._reactions = [];
18 reactions.forEach(r => this._reactions.push(new Reaction(r))); 25 reactions.forEach(r => this._reactions.push(new Reaction(r)));
26 this._startReactions();
27 }
28
29 _startReactions() {
30 this._stopReactions();
19 this._reactions.forEach(r => r.start()); 31 this._reactions.forEach(r => r.start());
20 } 32 }
33
34 _stopReactions() {
35 this._reactions.forEach(r => r.stop());
36 }
37
38 stop() {
39 this._stopListeningToActions();
40 this._stopReactions();
41 }
21} 42}
diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js
index ea601700e..4841a4e08 100644
--- a/src/features/workspaces/store.js
+++ b/src/features/workspaces/store.js
@@ -51,12 +51,16 @@ export default class WorkspacesStore extends FeatureStore {
51 return getUserWorkspacesRequest.wasExecuted && this.workspaces.length > 0; 51 return getUserWorkspacesRequest.wasExecuted && this.workspaces.length > 0;
52 } 52 }
53 53
54 @computed get isUserAllowedToUseFeature() {
55 return !this.isPremiumUpgradeRequired;
56 }
57
54 start(stores, actions) { 58 start(stores, actions) {
55 debug('WorkspacesStore::start'); 59 debug('WorkspacesStore::start');
56 this.stores = stores; 60 this.stores = stores;
57 this.actions = actions; 61 this.actions = actions;
58 62
59 this._listenToActions([ 63 this._registerActions([
60 [workspaceActions.edit, this._edit], 64 [workspaceActions.edit, this._edit],
61 [workspaceActions.create, this._create], 65 [workspaceActions.create, this._create],
62 [workspaceActions.delete, this._delete], 66 [workspaceActions.delete, this._delete],
@@ -67,7 +71,7 @@ export default class WorkspacesStore extends FeatureStore {
67 [workspaceActions.openWorkspaceSettings, this._openWorkspaceSettings], 71 [workspaceActions.openWorkspaceSettings, this._openWorkspaceSettings],
68 ]); 72 ]);
69 73
70 this._startReactions([ 74 this._registerReactions([
71 this._setWorkspaceBeingEditedReaction, 75 this._setWorkspaceBeingEditedReaction,
72 this._setActiveServiceOnWorkspaceSwitchReaction, 76 this._setActiveServiceOnWorkspaceSwitchReaction,
73 this._setFeatureEnabledReaction, 77 this._setFeatureEnabledReaction,
@@ -75,6 +79,7 @@ export default class WorkspacesStore extends FeatureStore {
75 this._activateLastUsedWorkspaceReaction, 79 this._activateLastUsedWorkspaceReaction,
76 this._openDrawerWithSettingsReaction, 80 this._openDrawerWithSettingsReaction,
77 this._cleanupInvalidServiceReferences, 81 this._cleanupInvalidServiceReferences,
82 this._disableActionsForFreeUser,
78 ]); 83 ]);
79 84
80 getUserWorkspacesRequest.execute(); 85 getUserWorkspacesRequest.execute();
@@ -82,6 +87,7 @@ export default class WorkspacesStore extends FeatureStore {
82 } 87 }
83 88
84 stop() { 89 stop() {
90 super.stop();
85 debug('WorkspacesStore::stop'); 91 debug('WorkspacesStore::stop');
86 this.isFeatureActive = false; 92 this.isFeatureActive = false;
87 this.activeWorkspace = null; 93 this.activeWorkspace = null;
@@ -273,4 +279,12 @@ export default class WorkspacesStore extends FeatureStore {
273 getUserWorkspacesRequest.execute(); 279 getUserWorkspacesRequest.execute();
274 } 280 }
275 }; 281 };
282
283 _disableActionsForFreeUser = () => {
284 if (!this.isUserAllowedToUseFeature) {
285 this._stopListeningToActions();
286 } else {
287 this._startListeningToActions();
288 }
289 }
276} 290}
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json
index 1dd31324e..5ad7a8bd8 100644
--- a/src/i18n/locales/defaultMessages.json
+++ b/src/i18n/locales/defaultMessages.json
@@ -3088,7 +3088,7 @@
3088 "line": 14 3088 "line": 14
3089 }, 3089 },
3090 "file": "src/features/announcements/Component.js", 3090 "file": "src/features/announcements/Component.js",
3091 "id": "feature.announcements.headline", 3091 "id": "feature.announcements.changelog.headline",
3092 "start": { 3092 "start": {
3093 "column": 12, 3093 "column": 12,
3094 "line": 11 3094 "line": 11
@@ -3106,7 +3106,7 @@
3106 "line": 14 3106 "line": 14
3107 }, 3107 },
3108 "file": "src/features/announcements/components/AnnouncementScreen.js", 3108 "file": "src/features/announcements/components/AnnouncementScreen.js",
3109 "id": "feature.announcements.headline", 3109 "id": "feature.announcements.changelog.headline",
3110 "start": { 3110 "start": {
3111 "column": 12, 3111 "column": 12,
3112 "line": 11 3112 "line": 11
@@ -4470,4 +4470,4 @@
4470 ], 4470 ],
4471 "path": "src/lib/Menu.json" 4471 "path": "src/lib/Menu.json"
4472 } 4472 }
4473] \ No newline at end of file 4473]
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 13775d758..958b6fb6a 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -1,7 +1,7 @@
1{ 1{
2 "app.errorHandler.action": "Reload", 2 "app.errorHandler.action": "Reload",
3 "app.errorHandler.headline": "Something went wrong", 3 "app.errorHandler.headline": "Something went wrong",
4 "feature.announcements.headline": "What's new in Franz {version}?", 4 "feature.announcements.changelog.headline": "Changes in Franz {version}",
5 "feature.delayApp.action": "Get a Franz Supporter License", 5 "feature.delayApp.action": "Get a Franz Supporter License",
6 "feature.delayApp.headline": "Please purchase a Franz Supporter License to skip waiting", 6 "feature.delayApp.headline": "Please purchase a Franz Supporter License to skip waiting",
7 "feature.delayApp.text": "Franz will continue in {seconds} seconds.", 7 "feature.delayApp.text": "Franz will continue in {seconds} seconds.",
@@ -44,7 +44,7 @@
44 "login.submit.label": "Sign in", 44 "login.submit.label": "Sign in",
45 "login.tokenExpired": "Your session expired, please login again.", 45 "login.tokenExpired": "Your session expired, please login again.",
46 "menu.app.about": "About Franz", 46 "menu.app.about": "About Franz",
47 "menu.app.announcement": "What's new in Franz?", 47 "menu.app.announcement": "What's new?",
48 "menu.app.hide": "Hide", 48 "menu.app.hide": "Hide",
49 "menu.app.hideOthers": "Hide Others", 49 "menu.app.hideOthers": "Hide Others",
50 "menu.app.quit": "Quit", 50 "menu.app.quit": "Quit",
@@ -319,4 +319,4 @@
319 "workspaceDrawer.workspaceFeatureInfo": "<p>Franz 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>", 319 "workspaceDrawer.workspaceFeatureInfo": "<p>Franz 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>",
320 "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings", 320 "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings",
321 "workspaces.switchingIndicator.switchingTo": "Switching to" 321 "workspaces.switchingIndicator.switchingTo": "Switching to"
322} \ No newline at end of file 322}
diff --git a/src/i18n/messages/src/features/announcements/Component.json b/src/i18n/messages/src/features/announcements/Component.json
index 18e1b84c5..c31c35fc7 100644
--- a/src/i18n/messages/src/features/announcements/Component.json
+++ b/src/i18n/messages/src/features/announcements/Component.json
@@ -1,6 +1,6 @@
1[ 1[
2 { 2 {
3 "id": "feature.announcements.headline", 3 "id": "feature.announcements.changelog.headline",
4 "defaultMessage": "!!!What's new in Franz {version}?", 4 "defaultMessage": "!!!What's new in Franz {version}?",
5 "file": "src/features/announcements/Component.js", 5 "file": "src/features/announcements/Component.js",
6 "start": { 6 "start": {
@@ -12,4 +12,4 @@
12 "column": 3 12 "column": 3
13 } 13 }
14 } 14 }
15] \ No newline at end of file 15]
diff --git a/src/i18n/messages/src/features/announcements/components/AnnouncementScreen.json b/src/i18n/messages/src/features/announcements/components/AnnouncementScreen.json
index 225670ee2..874c9dd9d 100644
--- a/src/i18n/messages/src/features/announcements/components/AnnouncementScreen.json
+++ b/src/i18n/messages/src/features/announcements/components/AnnouncementScreen.json
@@ -1,14 +1,14 @@
1[ 1[
2 { 2 {
3 "id": "feature.announcements.headline", 3 "id": "feature.announcements.changelog.headline",
4 "defaultMessage": "!!!What's new in Franz {version}?", 4 "defaultMessage": "!!!Changes in Franz {version}",
5 "file": "src/features/announcements/components/AnnouncementScreen.js", 5 "file": "src/features/announcements/components/AnnouncementScreen.js",
6 "start": { 6 "start": {
7 "line": 11, 7 "line": 13,
8 "column": 12 8 "column": 12
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 14, 11 "line": 16,
12 "column": 3 12 "column": 3
13 } 13 }
14 } 14 }
diff --git a/src/i18n/messages/src/lib/Menu.json b/src/i18n/messages/src/lib/Menu.json
index f4cd35582..a2ce34cd4 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": 13, 7 "line": 14,
8 "column": 8 8 "column": 8
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 16, 11 "line": 17,
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": 17, 20 "line": 18,
21 "column": 8 21 "column": 8
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 20, 24 "line": 21,
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": 21, 33 "line": 22,
34 "column": 8 34 "column": 8
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 24, 37 "line": 25,
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": 25, 46 "line": 26,
47 "column": 7 47 "column": 7
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 28, 50 "line": 29,
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": 29, 59 "line": 30,
60 "column": 8 60 "column": 8
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 32, 63 "line": 33,
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": 33, 72 "line": 34,
73 "column": 9 73 "column": 9
74 }, 74 },
75 "end": { 75 "end": {
76 "line": 36, 76 "line": 37,
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": 37, 85 "line": 38,
86 "column": 22 86 "column": 22
87 }, 87 },
88 "end": { 88 "end": {
89 "line": 40, 89 "line": 41,
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": 41, 98 "line": 42,
99 "column": 10 99 "column": 10
100 }, 100 },
101 "end": { 101 "end": {
102 "line": 44, 102 "line": 45,
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": 45, 111 "line": 46,
112 "column": 13 112 "column": 13
113 }, 113 },
114 "end": { 114 "end": {
115 "line": 48, 115 "line": 49,
116 "column": 3 116 "column": 3
117 } 117 }
118 }, 118 },
@@ -121,11 +121,11 @@
121 "defaultMessage": "!!!Speech", 121 "defaultMessage": "!!!Speech",
122 "file": "src/lib/Menu.js", 122 "file": "src/lib/Menu.js",
123 "start": { 123 "start": {
124 "line": 49, 124 "line": 50,
125 "column": 10 125 "column": 10
126 }, 126 },
127 "end": { 127 "end": {
128 "line": 52, 128 "line": 53,
129 "column": 3 129 "column": 3
130 } 130 }
131 }, 131 },
@@ -134,11 +134,11 @@
134 "defaultMessage": "!!!Start Speaking", 134 "defaultMessage": "!!!Start Speaking",
135 "file": "src/lib/Menu.js", 135 "file": "src/lib/Menu.js",
136 "start": { 136 "start": {
137 "line": 53, 137 "line": 54,
138 "column": 17 138 "column": 17
139 }, 139 },
140 "end": { 140 "end": {
141 "line": 56, 141 "line": 57,
142 "column": 3 142 "column": 3
143 } 143 }
144 }, 144 },
@@ -147,11 +147,11 @@
147 "defaultMessage": "!!!Stop Speaking", 147 "defaultMessage": "!!!Stop Speaking",
148 "file": "src/lib/Menu.js", 148 "file": "src/lib/Menu.js",
149 "start": { 149 "start": {
150 "line": 57, 150 "line": 58,
151 "column": 16 151 "column": 16
152 }, 152 },
153 "end": { 153 "end": {
154 "line": 60, 154 "line": 61,
155 "column": 3 155 "column": 3
156 } 156 }
157 }, 157 },
@@ -160,11 +160,11 @@
160 "defaultMessage": "!!!Start Dictation", 160 "defaultMessage": "!!!Start Dictation",
161 "file": "src/lib/Menu.js", 161 "file": "src/lib/Menu.js",
162 "start": { 162 "start": {
163 "line": 61, 163 "line": 62,
164 "column": 18 164 "column": 18
165 }, 165 },
166 "end": { 166 "end": {
167 "line": 64, 167 "line": 65,
168 "column": 3 168 "column": 3
169 } 169 }
170 }, 170 },
@@ -173,11 +173,11 @@
173 "defaultMessage": "!!!Emoji & Symbols", 173 "defaultMessage": "!!!Emoji & Symbols",
174 "file": "src/lib/Menu.js", 174 "file": "src/lib/Menu.js",
175 "start": { 175 "start": {
176 "line": 65, 176 "line": 66,
177 "column": 16 177 "column": 16
178 }, 178 },
179 "end": { 179 "end": {
180 "line": 68, 180 "line": 69,
181 "column": 3 181 "column": 3
182 } 182 }
183 }, 183 },
@@ -186,11 +186,11 @@
186 "defaultMessage": "!!!Actual Size", 186 "defaultMessage": "!!!Actual Size",
187 "file": "src/lib/Menu.js", 187 "file": "src/lib/Menu.js",
188 "start": { 188 "start": {
189 "line": 69, 189 "line": 70,
190 "column": 13 190 "column": 13
191 }, 191 },
192 "end": { 192 "end": {
193 "line": 72, 193 "line": 73,
194 "column": 3 194 "column": 3
195 } 195 }
196 }, 196 },
@@ -199,11 +199,11 @@
199 "defaultMessage": "!!!Zoom In", 199 "defaultMessage": "!!!Zoom In",
200 "file": "src/lib/Menu.js", 200 "file": "src/lib/Menu.js",
201 "start": { 201 "start": {
202 "line": 73, 202 "line": 74,
203 "column": 10 203 "column": 10
204 }, 204 },
205 "end": { 205 "end": {
206 "line": 76, 206 "line": 77,
207 "column": 3 207 "column": 3
208 } 208 }
209 }, 209 },
@@ -212,11 +212,11 @@
212 "defaultMessage": "!!!Zoom Out", 212 "defaultMessage": "!!!Zoom Out",
213 "file": "src/lib/Menu.js", 213 "file": "src/lib/Menu.js",
214 "start": { 214 "start": {
215 "line": 77, 215 "line": 78,
216 "column": 11 216 "column": 11
217 }, 217 },
218 "end": { 218 "end": {
219 "line": 80, 219 "line": 81,
220 "column": 3 220 "column": 3
221 } 221 }
222 }, 222 },
@@ -225,11 +225,11 @@
225 "defaultMessage": "!!!Enter Full Screen", 225 "defaultMessage": "!!!Enter Full Screen",
226 "file": "src/lib/Menu.js", 226 "file": "src/lib/Menu.js",
227 "start": { 227 "start": {
228 "line": 81, 228 "line": 82,
229 "column": 19 229 "column": 19
230 }, 230 },
231 "end": { 231 "end": {
232 "line": 84, 232 "line": 85,
233 "column": 3 233 "column": 3
234 } 234 }
235 }, 235 },
@@ -238,11 +238,11 @@
238 "defaultMessage": "!!!Exit Full Screen", 238 "defaultMessage": "!!!Exit Full Screen",
239 "file": "src/lib/Menu.js", 239 "file": "src/lib/Menu.js",
240 "start": { 240 "start": {
241 "line": 85, 241 "line": 86,
242 "column": 18 242 "column": 18
243 }, 243 },
244 "end": { 244 "end": {
245 "line": 88, 245 "line": 89,
246 "column": 3 246 "column": 3
247 } 247 }
248 }, 248 },
@@ -251,11 +251,11 @@
251 "defaultMessage": "!!!Toggle Full Screen", 251 "defaultMessage": "!!!Toggle Full Screen",
252 "file": "src/lib/Menu.js", 252 "file": "src/lib/Menu.js",
253 "start": { 253 "start": {
254 "line": 89, 254 "line": 90,
255 "column": 20 255 "column": 20
256 }, 256 },
257 "end": { 257 "end": {
258 "line": 92, 258 "line": 93,
259 "column": 3 259 "column": 3
260 } 260 }
261 }, 261 },
@@ -264,11 +264,11 @@
264 "defaultMessage": "!!!Toggle Developer Tools", 264 "defaultMessage": "!!!Toggle Developer Tools",
265 "file": "src/lib/Menu.js", 265 "file": "src/lib/Menu.js",
266 "start": { 266 "start": {
267 "line": 93, 267 "line": 94,
268 "column": 18 268 "column": 18
269 }, 269 },
270 "end": { 270 "end": {
271 "line": 96, 271 "line": 97,
272 "column": 3 272 "column": 3
273 } 273 }
274 }, 274 },
@@ -277,11 +277,11 @@
277 "defaultMessage": "!!!Toggle Service Developer Tools", 277 "defaultMessage": "!!!Toggle Service Developer Tools",
278 "file": "src/lib/Menu.js", 278 "file": "src/lib/Menu.js",
279 "start": { 279 "start": {
280 "line": 97, 280 "line": 98,
281 "column": 25 281 "column": 25
282 }, 282 },
283 "end": { 283 "end": {
284 "line": 100, 284 "line": 101,
285 "column": 3 285 "column": 3
286 } 286 }
287 }, 287 },
@@ -290,11 +290,11 @@
290 "defaultMessage": "!!!Reload Service", 290 "defaultMessage": "!!!Reload Service",
291 "file": "src/lib/Menu.js", 291 "file": "src/lib/Menu.js",
292 "start": { 292 "start": {
293 "line": 101, 293 "line": 102,
294 "column": 17 294 "column": 17
295 }, 295 },
296 "end": { 296 "end": {
297 "line": 104, 297 "line": 105,
298 "column": 3 298 "column": 3
299 } 299 }
300 }, 300 },
@@ -303,11 +303,11 @@
303 "defaultMessage": "!!!Reload Franz", 303 "defaultMessage": "!!!Reload Franz",
304 "file": "src/lib/Menu.js", 304 "file": "src/lib/Menu.js",
305 "start": { 305 "start": {
306 "line": 105, 306 "line": 106,
307 "column": 15 307 "column": 15
308 }, 308 },
309 "end": { 309 "end": {
310 "line": 108, 310 "line": 109,
311 "column": 3 311 "column": 3
312 } 312 }
313 }, 313 },
@@ -316,11 +316,11 @@
316 "defaultMessage": "!!!Minimize", 316 "defaultMessage": "!!!Minimize",
317 "file": "src/lib/Menu.js", 317 "file": "src/lib/Menu.js",
318 "start": { 318 "start": {
319 "line": 109, 319 "line": 110,
320 "column": 12 320 "column": 12
321 }, 321 },
322 "end": { 322 "end": {
323 "line": 112, 323 "line": 113,
324 "column": 3 324 "column": 3
325 } 325 }
326 }, 326 },
@@ -329,11 +329,11 @@
329 "defaultMessage": "!!!Close", 329 "defaultMessage": "!!!Close",
330 "file": "src/lib/Menu.js", 330 "file": "src/lib/Menu.js",
331 "start": { 331 "start": {
332 "line": 113, 332 "line": 114,
333 "column": 9 333 "column": 9
334 }, 334 },
335 "end": { 335 "end": {
336 "line": 116, 336 "line": 117,
337 "column": 3 337 "column": 3
338 } 338 }
339 }, 339 },
@@ -342,11 +342,11 @@
342 "defaultMessage": "!!!Learn More", 342 "defaultMessage": "!!!Learn More",
343 "file": "src/lib/Menu.js", 343 "file": "src/lib/Menu.js",
344 "start": { 344 "start": {
345 "line": 117, 345 "line": 118,
346 "column": 13 346 "column": 13
347 }, 347 },
348 "end": { 348 "end": {
349 "line": 120, 349 "line": 121,
350 "column": 3 350 "column": 3
351 } 351 }
352 }, 352 },
@@ -355,11 +355,11 @@
355 "defaultMessage": "!!!Changelog", 355 "defaultMessage": "!!!Changelog",
356 "file": "src/lib/Menu.js", 356 "file": "src/lib/Menu.js",
357 "start": { 357 "start": {
358 "line": 121, 358 "line": 122,
359 "column": 13 359 "column": 13
360 }, 360 },
361 "end": { 361 "end": {
362 "line": 124, 362 "line": 125,
363 "column": 3 363 "column": 3
364 } 364 }
365 }, 365 },
@@ -368,11 +368,11 @@
368 "defaultMessage": "!!!Support", 368 "defaultMessage": "!!!Support",
369 "file": "src/lib/Menu.js", 369 "file": "src/lib/Menu.js",
370 "start": { 370 "start": {
371 "line": 125, 371 "line": 126,
372 "column": 11 372 "column": 11
373 }, 373 },
374 "end": { 374 "end": {
375 "line": 128, 375 "line": 129,
376 "column": 3 376 "column": 3
377 } 377 }
378 }, 378 },
@@ -381,11 +381,11 @@
381 "defaultMessage": "!!!Terms of Service", 381 "defaultMessage": "!!!Terms of Service",
382 "file": "src/lib/Menu.js", 382 "file": "src/lib/Menu.js",
383 "start": { 383 "start": {
384 "line": 129, 384 "line": 130,
385 "column": 7 385 "column": 7
386 }, 386 },
387 "end": { 387 "end": {
388 "line": 132, 388 "line": 133,
389 "column": 3 389 "column": 3
390 } 390 }
391 }, 391 },
@@ -394,11 +394,11 @@
394 "defaultMessage": "!!!Privacy Statement", 394 "defaultMessage": "!!!Privacy Statement",
395 "file": "src/lib/Menu.js", 395 "file": "src/lib/Menu.js",
396 "start": { 396 "start": {
397 "line": 133, 397 "line": 134,
398 "column": 11 398 "column": 11
399 }, 399 },
400 "end": { 400 "end": {
401 "line": 136, 401 "line": 137,
402 "column": 3 402 "column": 3
403 } 403 }
404 }, 404 },
@@ -407,11 +407,11 @@
407 "defaultMessage": "!!!File", 407 "defaultMessage": "!!!File",
408 "file": "src/lib/Menu.js", 408 "file": "src/lib/Menu.js",
409 "start": { 409 "start": {
410 "line": 137, 410 "line": 138,
411 "column": 8 411 "column": 8
412 }, 412 },
413 "end": { 413 "end": {
414 "line": 140, 414 "line": 141,
415 "column": 3 415 "column": 3
416 } 416 }
417 }, 417 },
@@ -420,11 +420,11 @@
420 "defaultMessage": "!!!View", 420 "defaultMessage": "!!!View",
421 "file": "src/lib/Menu.js", 421 "file": "src/lib/Menu.js",
422 "start": { 422 "start": {
423 "line": 141, 423 "line": 142,
424 "column": 8 424 "column": 8
425 }, 425 },
426 "end": { 426 "end": {
427 "line": 144, 427 "line": 145,
428 "column": 3 428 "column": 3
429 } 429 }
430 }, 430 },
@@ -433,11 +433,11 @@
433 "defaultMessage": "!!!Services", 433 "defaultMessage": "!!!Services",
434 "file": "src/lib/Menu.js", 434 "file": "src/lib/Menu.js",
435 "start": { 435 "start": {
436 "line": 145, 436 "line": 146,
437 "column": 12 437 "column": 12
438 }, 438 },
439 "end": { 439 "end": {
440 "line": 148, 440 "line": 149,
441 "column": 3 441 "column": 3
442 } 442 }
443 }, 443 },
@@ -446,11 +446,11 @@
446 "defaultMessage": "!!!Window", 446 "defaultMessage": "!!!Window",
447 "file": "src/lib/Menu.js", 447 "file": "src/lib/Menu.js",
448 "start": { 448 "start": {
449 "line": 149, 449 "line": 150,
450 "column": 10 450 "column": 10
451 }, 451 },
452 "end": { 452 "end": {
453 "line": 152, 453 "line": 153,
454 "column": 3 454 "column": 3
455 } 455 }
456 }, 456 },
@@ -459,11 +459,11 @@
459 "defaultMessage": "!!!Help", 459 "defaultMessage": "!!!Help",
460 "file": "src/lib/Menu.js", 460 "file": "src/lib/Menu.js",
461 "start": { 461 "start": {
462 "line": 153, 462 "line": 154,
463 "column": 8 463 "column": 8
464 }, 464 },
465 "end": { 465 "end": {
466 "line": 156, 466 "line": 157,
467 "column": 3 467 "column": 3
468 } 468 }
469 }, 469 },
@@ -472,24 +472,24 @@
472 "defaultMessage": "!!!About Franz", 472 "defaultMessage": "!!!About Franz",
473 "file": "src/lib/Menu.js", 473 "file": "src/lib/Menu.js",
474 "start": { 474 "start": {
475 "line": 157, 475 "line": 158,
476 "column": 9 476 "column": 9
477 }, 477 },
478 "end": { 478 "end": {
479 "line": 160, 479 "line": 161,
480 "column": 3 480 "column": 3
481 } 481 }
482 }, 482 },
483 { 483 {
484 "id": "menu.app.announcement", 484 "id": "menu.app.announcement",
485 "defaultMessage": "!!!What's new in Franz?", 485 "defaultMessage": "!!!What's new?",
486 "file": "src/lib/Menu.js", 486 "file": "src/lib/Menu.js",
487 "start": { 487 "start": {
488 "line": 161, 488 "line": 162,
489 "column": 16 489 "column": 16
490 }, 490 },
491 "end": { 491 "end": {
492 "line": 164, 492 "line": 165,
493 "column": 3 493 "column": 3
494 } 494 }
495 }, 495 },
@@ -498,11 +498,11 @@
498 "defaultMessage": "!!!Settings", 498 "defaultMessage": "!!!Settings",
499 "file": "src/lib/Menu.js", 499 "file": "src/lib/Menu.js",
500 "start": { 500 "start": {
501 "line": 165, 501 "line": 166,
502 "column": 12 502 "column": 12
503 }, 503 },
504 "end": { 504 "end": {
505 "line": 168, 505 "line": 169,
506 "column": 3 506 "column": 3
507 } 507 }
508 }, 508 },
@@ -511,11 +511,11 @@
511 "defaultMessage": "!!!Hide", 511 "defaultMessage": "!!!Hide",
512 "file": "src/lib/Menu.js", 512 "file": "src/lib/Menu.js",
513 "start": { 513 "start": {
514 "line": 169, 514 "line": 170,
515 "column": 8 515 "column": 8
516 }, 516 },
517 "end": { 517 "end": {
518 "line": 172, 518 "line": 173,
519 "column": 3 519 "column": 3
520 } 520 }
521 }, 521 },
@@ -524,11 +524,11 @@
524 "defaultMessage": "!!!Hide Others", 524 "defaultMessage": "!!!Hide Others",
525 "file": "src/lib/Menu.js", 525 "file": "src/lib/Menu.js",
526 "start": { 526 "start": {
527 "line": 173, 527 "line": 174,
528 "column": 14 528 "column": 14
529 }, 529 },
530 "end": { 530 "end": {
531 "line": 176, 531 "line": 177,
532 "column": 3 532 "column": 3
533 } 533 }
534 }, 534 },
@@ -537,11 +537,11 @@
537 "defaultMessage": "!!!Unhide", 537 "defaultMessage": "!!!Unhide",
538 "file": "src/lib/Menu.js", 538 "file": "src/lib/Menu.js",
539 "start": { 539 "start": {
540 "line": 177, 540 "line": 178,
541 "column": 10 541 "column": 10
542 }, 542 },
543 "end": { 543 "end": {
544 "line": 180, 544 "line": 181,
545 "column": 3 545 "column": 3
546 } 546 }
547 }, 547 },
@@ -550,11 +550,11 @@
550 "defaultMessage": "!!!Quit", 550 "defaultMessage": "!!!Quit",
551 "file": "src/lib/Menu.js", 551 "file": "src/lib/Menu.js",
552 "start": { 552 "start": {
553 "line": 181, 553 "line": 182,
554 "column": 8 554 "column": 8
555 }, 555 },
556 "end": { 556 "end": {
557 "line": 184, 557 "line": 185,
558 "column": 3 558 "column": 3
559 } 559 }
560 }, 560 },
@@ -563,11 +563,11 @@
563 "defaultMessage": "!!!Add New Service...", 563 "defaultMessage": "!!!Add New Service...",
564 "file": "src/lib/Menu.js", 564 "file": "src/lib/Menu.js",
565 "start": { 565 "start": {
566 "line": 185, 566 "line": 186,
567 "column": 17 567 "column": 17
568 }, 568 },
569 "end": { 569 "end": {
570 "line": 188, 570 "line": 189,
571 "column": 3 571 "column": 3
572 } 572 }
573 }, 573 },
@@ -576,11 +576,11 @@
576 "defaultMessage": "!!!Add New Workspace...", 576 "defaultMessage": "!!!Add New Workspace...",
577 "file": "src/lib/Menu.js", 577 "file": "src/lib/Menu.js",
578 "start": { 578 "start": {
579 "line": 189, 579 "line": 190,
580 "column": 19 580 "column": 19
581 }, 581 },
582 "end": { 582 "end": {
583 "line": 192, 583 "line": 193,
584 "column": 3 584 "column": 3
585 } 585 }
586 }, 586 },
@@ -589,11 +589,11 @@
589 "defaultMessage": "!!!Open workspace drawer", 589 "defaultMessage": "!!!Open workspace drawer",
590 "file": "src/lib/Menu.js", 590 "file": "src/lib/Menu.js",
591 "start": { 591 "start": {
592 "line": 193, 592 "line": 194,
593 "column": 23 593 "column": 23
594 }, 594 },
595 "end": { 595 "end": {
596 "line": 196, 596 "line": 197,
597 "column": 3 597 "column": 3
598 } 598 }
599 }, 599 },
@@ -602,11 +602,11 @@
602 "defaultMessage": "!!!Close workspace drawer", 602 "defaultMessage": "!!!Close workspace drawer",
603 "file": "src/lib/Menu.js", 603 "file": "src/lib/Menu.js",
604 "start": { 604 "start": {
605 "line": 197, 605 "line": 198,
606 "column": 24 606 "column": 24
607 }, 607 },
608 "end": { 608 "end": {
609 "line": 200, 609 "line": 201,
610 "column": 3 610 "column": 3
611 } 611 }
612 }, 612 },
@@ -615,11 +615,11 @@
615 "defaultMessage": "!!!Activate next service...", 615 "defaultMessage": "!!!Activate next service...",
616 "file": "src/lib/Menu.js", 616 "file": "src/lib/Menu.js",
617 "start": { 617 "start": {
618 "line": 201, 618 "line": 202,
619 "column": 23 619 "column": 23
620 }, 620 },
621 "end": { 621 "end": {
622 "line": 204, 622 "line": 205,
623 "column": 3 623 "column": 3
624 } 624 }
625 }, 625 },
@@ -628,11 +628,11 @@
628 "defaultMessage": "!!!Activate previous service...", 628 "defaultMessage": "!!!Activate previous service...",
629 "file": "src/lib/Menu.js", 629 "file": "src/lib/Menu.js",
630 "start": { 630 "start": {
631 "line": 205, 631 "line": 206,
632 "column": 27 632 "column": 27
633 }, 633 },
634 "end": { 634 "end": {
635 "line": 208, 635 "line": 209,
636 "column": 3 636 "column": 3
637 } 637 }
638 }, 638 },
@@ -641,11 +641,11 @@
641 "defaultMessage": "!!!Disable notifications & audio", 641 "defaultMessage": "!!!Disable notifications & audio",
642 "file": "src/lib/Menu.js", 642 "file": "src/lib/Menu.js",
643 "start": { 643 "start": {
644 "line": 209, 644 "line": 210,
645 "column": 11 645 "column": 11
646 }, 646 },
647 "end": { 647 "end": {
648 "line": 212, 648 "line": 213,
649 "column": 3 649 "column": 3
650 } 650 }
651 }, 651 },
@@ -654,11 +654,11 @@
654 "defaultMessage": "!!!Enable notifications & audio", 654 "defaultMessage": "!!!Enable notifications & audio",
655 "file": "src/lib/Menu.js", 655 "file": "src/lib/Menu.js",
656 "start": { 656 "start": {
657 "line": 213, 657 "line": 214,
658 "column": 13 658 "column": 13
659 }, 659 },
660 "end": { 660 "end": {
661 "line": 216, 661 "line": 217,
662 "column": 3 662 "column": 3
663 } 663 }
664 }, 664 },
@@ -667,11 +667,11 @@
667 "defaultMessage": "!!!Workspaces", 667 "defaultMessage": "!!!Workspaces",
668 "file": "src/lib/Menu.js", 668 "file": "src/lib/Menu.js",
669 "start": { 669 "start": {
670 "line": 217, 670 "line": 218,
671 "column": 14 671 "column": 14
672 }, 672 },
673 "end": { 673 "end": {
674 "line": 220, 674 "line": 221,
675 "column": 3 675 "column": 3
676 } 676 }
677 }, 677 },
@@ -680,11 +680,11 @@
680 "defaultMessage": "!!!Default", 680 "defaultMessage": "!!!Default",
681 "file": "src/lib/Menu.js", 681 "file": "src/lib/Menu.js",
682 "start": { 682 "start": {
683 "line": 221, 683 "line": 222,
684 "column": 20 684 "column": 20
685 }, 685 },
686 "end": { 686 "end": {
687 "line": 224, 687 "line": 225,
688 "column": 3 688 "column": 3
689 } 689 }
690 } 690 }
diff --git a/src/lib/Menu.js b/src/lib/Menu.js
index 46a347237..3df06e05a 100644
--- a/src/lib/Menu.js
+++ b/src/lib/Menu.js
@@ -6,6 +6,7 @@ import { isMac, ctrlKey, cmdKey } from '../environment';
6import { GA_CATEGORY_WORKSPACES, workspaceStore } from '../features/workspaces/index'; 6import { GA_CATEGORY_WORKSPACES, workspaceStore } from '../features/workspaces/index';
7import { workspaceActions } from '../features/workspaces/actions'; 7import { workspaceActions } from '../features/workspaces/actions';
8import { gaEvent } from './analytics'; 8import { gaEvent } from './analytics';
9import announcementActions from '../features/announcements/actions';
9 10
10const { app, Menu, dialog } = remote; 11const { app, Menu, dialog } = remote;
11 12
@@ -160,7 +161,7 @@ const menuItems = defineMessages({
160 }, 161 },
161 announcement: { 162 announcement: {
162 id: 'menu.app.announcement', 163 id: 'menu.app.announcement',
163 defaultMessage: '!!!What\'s new in Franz?', 164 defaultMessage: '!!!What\'s new?',
164 }, 165 },
165 settings: { 166 settings: {
166 id: 'menu.app.settings', 167 id: 'menu.app.settings',
@@ -352,8 +353,10 @@ const _templateFactory = intl => [
352 click() { shell.openExternal('https://meetfranz.com'); }, 353 click() { shell.openExternal('https://meetfranz.com'); },
353 }, 354 },
354 { 355 {
355 label: intl.formatMessage(menuItems.changelog), 356 label: intl.formatMessage(menuItems.announcement),
356 click() { shell.openExternal('https://github.com/meetfranz/franz/blob/master/CHANGELOG.md'); }, 357 click: () => {
358 announcementActions.show();
359 },
357 }, 360 },
358 { 361 {
359 type: 'separator', 362 type: 'separator',
@@ -622,12 +625,6 @@ export default class FranzMenu {
622 role: 'about', 625 role: 'about',
623 }, 626 },
624 { 627 {
625 label: intl.formatMessage(menuItems.announcement),
626 click: () => {
627 this.actions.announcements.show();
628 },
629 },
630 {
631 type: 'separator', 628 type: 'separator',
632 }, 629 },
633 { 630 {
diff --git a/src/stores/index.js b/src/stores/index.js
index 96b844c95..1912418a2 100644
--- a/src/stores/index.js
+++ b/src/stores/index.js
@@ -10,6 +10,8 @@ import PaymentStore from './PaymentStore';
10import NewsStore from './NewsStore'; 10import NewsStore from './NewsStore';
11import RequestStore from './RequestStore'; 11import RequestStore from './RequestStore';
12import GlobalErrorStore from './GlobalErrorStore'; 12import GlobalErrorStore from './GlobalErrorStore';
13import { workspaceStore } from '../features/workspaces';
14import { announcementsStore } from '../features/announcements';
13 15
14export default (api, actions, router) => { 16export default (api, actions, router) => {
15 const stores = {}; 17 const stores = {};
@@ -27,6 +29,8 @@ export default (api, actions, router) => {
27 news: new NewsStore(stores, api, actions), 29 news: new NewsStore(stores, api, actions),
28 requests: new RequestStore(stores, api, actions), 30 requests: new RequestStore(stores, api, actions),
29 globalError: new GlobalErrorStore(stores, api, actions), 31 globalError: new GlobalErrorStore(stores, api, actions),
32 workspaces: workspaceStore,
33 announcements: announcementsStore,
30 }); 34 });
31 // Initialize all stores 35 // Initialize all stores
32 Object.keys(stores).forEach((name) => { 36 Object.keys(stores).forEach((name) => {