diff options
author | 2019-03-12 21:36:10 +0100 | |
---|---|---|
committer | 2019-03-12 21:37:33 +0100 | |
commit | 6fb07bcb716af76ec2e96345f37624d12d0d1af0 (patch) | |
tree | 276191a782dc1d44f78331e548e43ff71758baca /src | |
parent | refactor server api even more (diff) | |
download | ferdium-app-6fb07bcb716af76ec2e96345f37624d12d0d1af0.tar.gz ferdium-app-6fb07bcb716af76ec2e96345f37624d12d0d1af0.tar.zst ferdium-app-6fb07bcb716af76ec2e96345f37624d12d0d1af0.zip |
implements basic release announcement feature
Diffstat (limited to 'src')
-rw-r--r-- | src/actions/index.js | 6 | ||||
-rw-r--r-- | src/actions/service.js | 1 | ||||
-rw-r--r-- | src/components/layout/AppLayout.js | 4 | ||||
-rw-r--r-- | src/config.js | 1 | ||||
-rw-r--r-- | src/containers/layout/AppLayoutContainer.js | 2 | ||||
-rw-r--r-- | src/features/announcements/Component.js | 77 | ||||
-rw-r--r-- | src/features/announcements/actions.js | 8 | ||||
-rw-r--r-- | src/features/announcements/api.js | 19 | ||||
-rw-r--r-- | src/features/announcements/index.js | 37 | ||||
-rw-r--r-- | src/features/announcements/state.js | 17 | ||||
-rw-r--r-- | src/features/announcements/store.js | 95 | ||||
-rw-r--r-- | src/i18n/locales/defaultMessages.json | 95 | ||||
-rw-r--r-- | src/i18n/locales/en-US.json | 2 | ||||
-rw-r--r-- | src/i18n/messages/src/components/layout/AppLayout.json | 24 | ||||
-rw-r--r-- | src/i18n/messages/src/features/announcements/Component.json | 15 | ||||
-rw-r--r-- | src/i18n/messages/src/lib/Menu.json | 53 | ||||
-rw-r--r-- | src/lib/Menu.js | 10 | ||||
-rw-r--r-- | src/stores/FeaturesStore.js | 2 | ||||
-rw-r--r-- | src/stores/ServicesStore.js | 6 |
19 files changed, 409 insertions, 65 deletions
diff --git a/src/actions/index.js b/src/actions/index.js index 59acabb0b..dc1d3b6b2 100644 --- a/src/actions/index.js +++ b/src/actions/index.js | |||
@@ -11,6 +11,7 @@ import payment from './payment'; | |||
11 | import news from './news'; | 11 | import news from './news'; |
12 | import settings from './settings'; | 12 | import settings from './settings'; |
13 | import requests from './requests'; | 13 | import requests from './requests'; |
14 | import announcements from '../features/announcements/actions'; | ||
14 | 15 | ||
15 | const actions = Object.assign({}, { | 16 | const actions = Object.assign({}, { |
16 | service, | 17 | service, |
@@ -25,4 +26,7 @@ const actions = Object.assign({}, { | |||
25 | requests, | 26 | requests, |
26 | }); | 27 | }); |
27 | 28 | ||
28 | export default defineActions(actions, PropTypes.checkPropTypes); | 29 | export default Object.assign( |
30 | defineActions(actions, PropTypes.checkPropTypes), | ||
31 | { announcements }, | ||
32 | ); | ||
diff --git a/src/actions/service.js b/src/actions/service.js index ceaabc31e..ce62560a9 100644 --- a/src/actions/service.js +++ b/src/actions/service.js | |||
@@ -5,6 +5,7 @@ export default { | |||
5 | setActive: { | 5 | setActive: { |
6 | serviceId: PropTypes.string.isRequired, | 6 | serviceId: PropTypes.string.isRequired, |
7 | }, | 7 | }, |
8 | blurActive: {}, | ||
8 | setActiveNext: {}, | 9 | setActiveNext: {}, |
9 | setActivePrev: {}, | 10 | setActivePrev: {}, |
10 | showAddServiceInterface: { | 11 | showAddServiceInterface: { |
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js index 593149e72..2bda91f73 100644 --- a/src/components/layout/AppLayout.js +++ b/src/components/layout/AppLayout.js | |||
@@ -13,6 +13,7 @@ import ErrorBoundary from '../util/ErrorBoundary'; | |||
13 | // import globalMessages from '../../i18n/globalMessages'; | 13 | // import globalMessages from '../../i18n/globalMessages'; |
14 | 14 | ||
15 | import { isWindows } from '../../environment'; | 15 | import { isWindows } from '../../environment'; |
16 | import AnnouncementScreen from '../../features/announcements/Component'; | ||
16 | 17 | ||
17 | function createMarkup(HTMLString) { | 18 | function createMarkup(HTMLString) { |
18 | return { __html: HTMLString }; | 19 | return { __html: HTMLString }; |
@@ -64,6 +65,7 @@ export default @observer class AppLayout extends Component { | |||
64 | areRequiredRequestsLoading: PropTypes.bool.isRequired, | 65 | areRequiredRequestsLoading: PropTypes.bool.isRequired, |
65 | darkMode: PropTypes.bool.isRequired, | 66 | darkMode: PropTypes.bool.isRequired, |
66 | isDelayAppScreenVisible: PropTypes.bool.isRequired, | 67 | isDelayAppScreenVisible: PropTypes.bool.isRequired, |
68 | isAnnouncementVisible: PropTypes.bool.isRequired, | ||
67 | }; | 69 | }; |
68 | 70 | ||
69 | static defaultProps = { | 71 | static defaultProps = { |
@@ -93,6 +95,7 @@ export default @observer class AppLayout extends Component { | |||
93 | areRequiredRequestsLoading, | 95 | areRequiredRequestsLoading, |
94 | darkMode, | 96 | darkMode, |
95 | isDelayAppScreenVisible, | 97 | isDelayAppScreenVisible, |
98 | isAnnouncementVisible, | ||
96 | } = this.props; | 99 | } = this.props; |
97 | 100 | ||
98 | const { intl } = this.context; | 101 | const { intl } = this.context; |
@@ -166,6 +169,7 @@ export default @observer class AppLayout extends Component { | |||
166 | {isDelayAppScreenVisible && (<DelayApp />)} | 169 | {isDelayAppScreenVisible && (<DelayApp />)} |
167 | <BasicAuth /> | 170 | <BasicAuth /> |
168 | <ShareFranz /> | 171 | <ShareFranz /> |
172 | {isAnnouncementVisible && (<AnnouncementScreen />)} | ||
169 | {services} | 173 | {services} |
170 | </div> | 174 | </div> |
171 | </div> | 175 | </div> |
diff --git a/src/config.js b/src/config.js index 479572edb..47d22ca7d 100644 --- a/src/config.js +++ b/src/config.js | |||
@@ -41,6 +41,7 @@ export const DEFAULT_FEATURES_CONFIG = { | |||
41 | }, | 41 | }, |
42 | isServiceProxyEnabled: false, | 42 | isServiceProxyEnabled: false, |
43 | isServiceProxyPremiumFeature: true, | 43 | isServiceProxyPremiumFeature: true, |
44 | isAnnouncementsEnabled: true, | ||
44 | }; | 45 | }; |
45 | 46 | ||
46 | export const DEFAULT_WINDOW_OPTIONS = { | 47 | export const DEFAULT_WINDOW_OPTIONS = { |
diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js index 5a05ce431..f26e51517 100644 --- a/src/containers/layout/AppLayoutContainer.js +++ b/src/containers/layout/AppLayoutContainer.js | |||
@@ -20,6 +20,7 @@ import Services from '../../components/services/content/Services'; | |||
20 | import AppLoader from '../../components/ui/AppLoader'; | 20 | import AppLoader from '../../components/ui/AppLoader'; |
21 | 21 | ||
22 | import { state as delayAppState } from '../../features/delayApp'; | 22 | import { state as delayAppState } from '../../features/delayApp'; |
23 | import { announcementsState } from '../../features/announcements/state'; | ||
23 | 24 | ||
24 | export default @inject('stores', 'actions') @observer class AppLayoutContainer extends Component { | 25 | export default @inject('stores', 'actions') @observer class AppLayoutContainer extends Component { |
25 | static defaultProps = { | 26 | static defaultProps = { |
@@ -134,6 +135,7 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e | |||
134 | areRequiredRequestsLoading={requests.areRequiredRequestsLoading} | 135 | areRequiredRequestsLoading={requests.areRequiredRequestsLoading} |
135 | darkMode={settings.all.app.darkMode} | 136 | darkMode={settings.all.app.darkMode} |
136 | isDelayAppScreenVisible={delayAppState.isDelayAppScreenVisible} | 137 | isDelayAppScreenVisible={delayAppState.isDelayAppScreenVisible} |
138 | isAnnouncementVisible={announcementsState.isAnnouncementVisible} | ||
137 | > | 139 | > |
138 | {React.Children.count(children) > 0 ? children : null} | 140 | {React.Children.count(children) > 0 ? children : null} |
139 | </AppLayout> | 141 | </AppLayout> |
diff --git a/src/features/announcements/Component.js b/src/features/announcements/Component.js new file mode 100644 index 000000000..5d95f5d84 --- /dev/null +++ b/src/features/announcements/Component.js | |||
@@ -0,0 +1,77 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import marked from 'marked'; | ||
3 | import PropTypes from 'prop-types'; | ||
4 | import { inject, observer } from 'mobx-react'; | ||
5 | import { defineMessages, intlShape } from 'react-intl'; | ||
6 | import injectSheet from 'react-jss'; | ||
7 | import { themeSidebarWidth } from '@meetfranz/theme/lib/themes/legacy'; | ||
8 | import state from './state'; | ||
9 | |||
10 | const messages = defineMessages({ | ||
11 | headline: { | ||
12 | id: 'feature.announcements.headline', | ||
13 | defaultMessage: '!!!What\'s new in Franz {version}?', | ||
14 | }, | ||
15 | }); | ||
16 | |||
17 | const styles = theme => ({ | ||
18 | container: { | ||
19 | background: theme.colorBackground, | ||
20 | position: 'absolute', | ||
21 | top: 0, | ||
22 | zIndex: 140, | ||
23 | width: `calc(100% - ${themeSidebarWidth})`, | ||
24 | display: 'flex', | ||
25 | 'flex-direction': 'column', | ||
26 | 'align-items': 'center', | ||
27 | 'justify-content': 'center', | ||
28 | }, | ||
29 | headline: { | ||
30 | color: theme.colorHeadline, | ||
31 | margin: [25, 0, 40], | ||
32 | 'max-width': 500, | ||
33 | 'text-align': 'center', | ||
34 | 'line-height': '1.3em', | ||
35 | }, | ||
36 | body: { | ||
37 | '& h3': { | ||
38 | fontSize: '24px', | ||
39 | margin: '1.5em 0 1em 0', | ||
40 | }, | ||
41 | '& li': { | ||
42 | marginBottom: '1em', | ||
43 | }, | ||
44 | }, | ||
45 | }); | ||
46 | |||
47 | |||
48 | @inject('actions') @injectSheet(styles) @observer | ||
49 | class AnnouncementScreen extends Component { | ||
50 | static propTypes = { | ||
51 | classes: PropTypes.object.isRequired, | ||
52 | }; | ||
53 | |||
54 | static contextTypes = { | ||
55 | intl: intlShape, | ||
56 | }; | ||
57 | |||
58 | render() { | ||
59 | const { classes } = this.props; | ||
60 | const { intl } = this.context; | ||
61 | return ( | ||
62 | <div className={`${classes.container}`}> | ||
63 | <h1 className={classes.headline}> | ||
64 | {intl.formatMessage(messages.headline, { version: state.currentVersion })} | ||
65 | </h1> | ||
66 | <div | ||
67 | className={classes.body} | ||
68 | dangerouslySetInnerHTML={{ | ||
69 | __html: marked(state.announcement, { sanitize: true }), | ||
70 | }} | ||
71 | /> | ||
72 | </div> | ||
73 | ); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | export default AnnouncementScreen; | ||
diff --git a/src/features/announcements/actions.js b/src/features/announcements/actions.js new file mode 100644 index 000000000..68b262ded --- /dev/null +++ b/src/features/announcements/actions.js | |||
@@ -0,0 +1,8 @@ | |||
1 | import PropTypes from 'prop-types'; | ||
2 | import { createActionsFromDefinitions } from '../../actions/lib/actions'; | ||
3 | |||
4 | export const announcementActions = createActionsFromDefinitions({ | ||
5 | show: {}, | ||
6 | }, PropTypes.checkPropTypes); | ||
7 | |||
8 | export default announcementActions; | ||
diff --git a/src/features/announcements/api.js b/src/features/announcements/api.js new file mode 100644 index 000000000..ec16066a6 --- /dev/null +++ b/src/features/announcements/api.js | |||
@@ -0,0 +1,19 @@ | |||
1 | import { remote } from 'electron'; | ||
2 | |||
3 | const debug = require('debug')('Franz:feature:announcements:api'); | ||
4 | |||
5 | export default { | ||
6 | async getCurrentVersion() { | ||
7 | debug('getting current version of electron app'); | ||
8 | return Promise.resolve(remote.app.getVersion()); | ||
9 | }, | ||
10 | |||
11 | async getAnnouncementForVersion(version) { | ||
12 | debug('fetching release announcement from Github'); | ||
13 | const url = `https://api.github.com/repos/meetfranz/franz/releases/tags/v${version}`; | ||
14 | const request = await window.fetch(url, { method: 'GET' }); | ||
15 | if (!request.ok) throw request; | ||
16 | const data = await request.json(); | ||
17 | return data.body; | ||
18 | }, | ||
19 | }; | ||
diff --git a/src/features/announcements/index.js b/src/features/announcements/index.js new file mode 100644 index 000000000..5ea74e0af --- /dev/null +++ b/src/features/announcements/index.js | |||
@@ -0,0 +1,37 @@ | |||
1 | import { reaction, runInAction } from 'mobx'; | ||
2 | import { AnnouncementsStore } from './store'; | ||
3 | import api from './api'; | ||
4 | import state, { resetState } from './state'; | ||
5 | |||
6 | const debug = require('debug')('Franz:feature:announcements'); | ||
7 | |||
8 | let store = null; | ||
9 | |||
10 | export default function initAnnouncements(stores, actions) { | ||
11 | // const { features } = stores; | ||
12 | |||
13 | // Toggle workspace feature | ||
14 | reaction( | ||
15 | () => ( | ||
16 | true | ||
17 | // features.features.isAnnouncementsEnabled | ||
18 | ), | ||
19 | (isEnabled) => { | ||
20 | if (isEnabled) { | ||
21 | debug('Initializing `announcements` feature'); | ||
22 | store = new AnnouncementsStore(stores, api, actions, state); | ||
23 | store.initialize(); | ||
24 | runInAction(() => { state.isFeatureActive = true; }); | ||
25 | } else if (store) { | ||
26 | debug('Disabling `announcements` feature'); | ||
27 | runInAction(() => { state.isFeatureActive = false; }); | ||
28 | store.teardown(); | ||
29 | store = null; | ||
30 | resetState(); // Reset state to default | ||
31 | } | ||
32 | }, | ||
33 | { | ||
34 | fireImmediately: true, | ||
35 | }, | ||
36 | ); | ||
37 | } | ||
diff --git a/src/features/announcements/state.js b/src/features/announcements/state.js new file mode 100644 index 000000000..81b632253 --- /dev/null +++ b/src/features/announcements/state.js | |||
@@ -0,0 +1,17 @@ | |||
1 | import { observable } from 'mobx'; | ||
2 | |||
3 | const defaultState = { | ||
4 | announcement: null, | ||
5 | currentVersion: null, | ||
6 | lastUsedVersion: null, | ||
7 | isAnnouncementVisible: false, | ||
8 | isFeatureActive: false, | ||
9 | }; | ||
10 | |||
11 | export const announcementsState = observable(defaultState); | ||
12 | |||
13 | export function resetState() { | ||
14 | Object.assign(announcementsState, defaultState); | ||
15 | } | ||
16 | |||
17 | export default announcementsState; | ||
diff --git a/src/features/announcements/store.js b/src/features/announcements/store.js new file mode 100644 index 000000000..004a44062 --- /dev/null +++ b/src/features/announcements/store.js | |||
@@ -0,0 +1,95 @@ | |||
1 | import { action, observable, reaction } from 'mobx'; | ||
2 | import semver from 'semver'; | ||
3 | |||
4 | import Request from '../../stores/lib/Request'; | ||
5 | import Store from '../../stores/lib/Store'; | ||
6 | |||
7 | const debug = require('debug')('Franz:feature:announcements:store'); | ||
8 | |||
9 | export class AnnouncementsStore extends Store { | ||
10 | @observable getCurrentVersion = new Request(this.api, 'getCurrentVersion'); | ||
11 | |||
12 | @observable getAnnouncement = new Request(this.api, 'getAnnouncementForVersion'); | ||
13 | |||
14 | constructor(stores, api, actions, state) { | ||
15 | super(stores, api, actions); | ||
16 | this.state = state; | ||
17 | } | ||
18 | |||
19 | async setup() { | ||
20 | await this.fetchLastUsedVersion(); | ||
21 | await this.fetchCurrentVersion(); | ||
22 | await this.fetchReleaseAnnouncement(); | ||
23 | this.showAnnouncementIfNotSeenYet(); | ||
24 | |||
25 | this.actions.announcements.show.listen(this._showAnnouncement.bind(this)); | ||
26 | } | ||
27 | |||
28 | // ====== PUBLIC ====== | ||
29 | |||
30 | async fetchLastUsedVersion() { | ||
31 | debug('getting last used version from local storage'); | ||
32 | const lastUsedVersion = window.localStorage.getItem('lastUsedVersion'); | ||
33 | this._setLastUsedVersion(lastUsedVersion == null ? '0.0.0' : lastUsedVersion); | ||
34 | } | ||
35 | |||
36 | async fetchCurrentVersion() { | ||
37 | debug('getting current version from api'); | ||
38 | const version = await this.getCurrentVersion.execute(); | ||
39 | this._setCurrentVersion(version); | ||
40 | } | ||
41 | |||
42 | async fetchReleaseAnnouncement() { | ||
43 | debug('getting release announcement from api'); | ||
44 | try { | ||
45 | const announcement = await this.getAnnouncement.execute(this.state.currentVersion); | ||
46 | this._setAnnouncement(announcement); | ||
47 | } catch (error) { | ||
48 | this._setAnnouncement(null); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | showAnnouncementIfNotSeenYet() { | ||
53 | const { announcement, currentVersion, lastUsedVersion } = this.state; | ||
54 | if (announcement && semver.gt(currentVersion, lastUsedVersion)) { | ||
55 | debug(`${currentVersion} < ${lastUsedVersion}: announcement is shown`); | ||
56 | this._showAnnouncement(); | ||
57 | } else { | ||
58 | debug(`${currentVersion} >= ${lastUsedVersion}: announcement is hidden`); | ||
59 | this._hideAnnouncement(); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | // ====== PRIVATE ====== | ||
64 | |||
65 | @action _setCurrentVersion(version) { | ||
66 | debug(`setting current version to ${version}`); | ||
67 | this.state.currentVersion = version; | ||
68 | } | ||
69 | |||
70 | @action _setLastUsedVersion(version) { | ||
71 | debug(`setting last used version to ${version}`); | ||
72 | this.state.lastUsedVersion = version; | ||
73 | } | ||
74 | |||
75 | @action _setAnnouncement(announcement) { | ||
76 | debug(`setting announcement to ${announcement}`); | ||
77 | this.state.announcement = announcement; | ||
78 | } | ||
79 | |||
80 | @action _showAnnouncement() { | ||
81 | this.state.isAnnouncementVisible = true; | ||
82 | this.actions.service.blurActive(); | ||
83 | const dispose = reaction( | ||
84 | () => this.stores.services.active, | ||
85 | () => { | ||
86 | this._hideAnnouncement(); | ||
87 | dispose(); | ||
88 | }, | ||
89 | ); | ||
90 | } | ||
91 | |||
92 | @action _hideAnnouncement() { | ||
93 | this.state.isAnnouncementVisible = false; | ||
94 | } | ||
95 | } | ||
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json index 0641c510c..fcd24c7ef 100644 --- a/src/i18n/locales/defaultMessages.json +++ b/src/i18n/locales/defaultMessages.json | |||
@@ -625,78 +625,78 @@ | |||
625 | "defaultMessage": "!!!Your services have been updated.", | 625 | "defaultMessage": "!!!Your services have been updated.", |
626 | "end": { | 626 | "end": { |
627 | "column": 3, | 627 | "column": 3, |
628 | "line": 25 | 628 | "line": 26 |
629 | }, | 629 | }, |
630 | "file": "src/components/layout/AppLayout.js", | 630 | "file": "src/components/layout/AppLayout.js", |
631 | "id": "infobar.servicesUpdated", | 631 | "id": "infobar.servicesUpdated", |
632 | "start": { | 632 | "start": { |
633 | "column": 19, | 633 | "column": 19, |
634 | "line": 22 | 634 | "line": 23 |
635 | } | 635 | } |
636 | }, | 636 | }, |
637 | { | 637 | { |
638 | "defaultMessage": "!!!A new update for Franz is available.", | 638 | "defaultMessage": "!!!A new update for Franz is available.", |
639 | "end": { | 639 | "end": { |
640 | "column": 3, | 640 | "column": 3, |
641 | "line": 29 | 641 | "line": 30 |
642 | }, | 642 | }, |
643 | "file": "src/components/layout/AppLayout.js", | 643 | "file": "src/components/layout/AppLayout.js", |
644 | "id": "infobar.updateAvailable", | 644 | "id": "infobar.updateAvailable", |
645 | "start": { | 645 | "start": { |
646 | "column": 19, | 646 | "column": 19, |
647 | "line": 26 | 647 | "line": 27 |
648 | } | 648 | } |
649 | }, | 649 | }, |
650 | { | 650 | { |
651 | "defaultMessage": "!!!Reload services", | 651 | "defaultMessage": "!!!Reload services", |
652 | "end": { | 652 | "end": { |
653 | "column": 3, | 653 | "column": 3, |
654 | "line": 33 | 654 | "line": 34 |
655 | }, | 655 | }, |
656 | "file": "src/components/layout/AppLayout.js", | 656 | "file": "src/components/layout/AppLayout.js", |
657 | "id": "infobar.buttonReloadServices", | 657 | "id": "infobar.buttonReloadServices", |
658 | "start": { | 658 | "start": { |
659 | "column": 24, | 659 | "column": 24, |
660 | "line": 30 | 660 | "line": 31 |
661 | } | 661 | } |
662 | }, | 662 | }, |
663 | { | 663 | { |
664 | "defaultMessage": "!!!Changelog", | 664 | "defaultMessage": "!!!Changelog", |
665 | "end": { | 665 | "end": { |
666 | "column": 3, | 666 | "column": 3, |
667 | "line": 37 | 667 | "line": 38 |
668 | }, | 668 | }, |
669 | "file": "src/components/layout/AppLayout.js", | 669 | "file": "src/components/layout/AppLayout.js", |
670 | "id": "infobar.buttonChangelog", | 670 | "id": "infobar.buttonChangelog", |
671 | "start": { | 671 | "start": { |
672 | "column": 13, | 672 | "column": 13, |
673 | "line": 34 | 673 | "line": 35 |
674 | } | 674 | } |
675 | }, | 675 | }, |
676 | { | 676 | { |
677 | "defaultMessage": "!!!Restart & install update", | 677 | "defaultMessage": "!!!Restart & install update", |
678 | "end": { | 678 | "end": { |
679 | "column": 3, | 679 | "column": 3, |
680 | "line": 41 | 680 | "line": 42 |
681 | }, | 681 | }, |
682 | "file": "src/components/layout/AppLayout.js", | 682 | "file": "src/components/layout/AppLayout.js", |
683 | "id": "infobar.buttonInstallUpdate", | 683 | "id": "infobar.buttonInstallUpdate", |
684 | "start": { | 684 | "start": { |
685 | "column": 23, | 685 | "column": 23, |
686 | "line": 38 | 686 | "line": 39 |
687 | } | 687 | } |
688 | }, | 688 | }, |
689 | { | 689 | { |
690 | "defaultMessage": "!!!Could not load services and user information", | 690 | "defaultMessage": "!!!Could not load services and user information", |
691 | "end": { | 691 | "end": { |
692 | "column": 3, | 692 | "column": 3, |
693 | "line": 45 | 693 | "line": 46 |
694 | }, | 694 | }, |
695 | "file": "src/components/layout/AppLayout.js", | 695 | "file": "src/components/layout/AppLayout.js", |
696 | "id": "infobar.requiredRequestsFailed", | 696 | "id": "infobar.requiredRequestsFailed", |
697 | "start": { | 697 | "start": { |
698 | "column": 26, | 698 | "column": 26, |
699 | "line": 42 | 699 | "line": 43 |
700 | } | 700 | } |
701 | } | 701 | } |
702 | ], | 702 | ], |
@@ -3025,6 +3025,24 @@ | |||
3025 | { | 3025 | { |
3026 | "descriptors": [ | 3026 | "descriptors": [ |
3027 | { | 3027 | { |
3028 | "defaultMessage": "!!!What's new in Franz {version}?", | ||
3029 | "end": { | ||
3030 | "column": 3, | ||
3031 | "line": 14 | ||
3032 | }, | ||
3033 | "file": "src/features/announcements/Component.js", | ||
3034 | "id": "feature.announcements.headline", | ||
3035 | "start": { | ||
3036 | "column": 12, | ||
3037 | "line": 11 | ||
3038 | } | ||
3039 | } | ||
3040 | ], | ||
3041 | "path": "src/features/announcements/Component.json" | ||
3042 | }, | ||
3043 | { | ||
3044 | "descriptors": [ | ||
3045 | { | ||
3028 | "defaultMessage": "!!!Please purchase license to skip waiting", | 3046 | "defaultMessage": "!!!Please purchase license to skip waiting", |
3029 | "end": { | 3047 | "end": { |
3030 | "column": 3, | 3048 | "column": 3, |
@@ -3799,133 +3817,146 @@ | |||
3799 | } | 3817 | } |
3800 | }, | 3818 | }, |
3801 | { | 3819 | { |
3802 | "defaultMessage": "!!!Settings", | 3820 | "defaultMessage": "!!!What's new in Franz?", |
3803 | "end": { | 3821 | "end": { |
3804 | "column": 3, | 3822 | "column": 3, |
3805 | "line": 161 | 3823 | "line": 161 |
3806 | }, | 3824 | }, |
3807 | "file": "src/lib/Menu.js", | 3825 | "file": "src/lib/Menu.js", |
3826 | "id": "menu.app.announcement", | ||
3827 | "start": { | ||
3828 | "column": 16, | ||
3829 | "line": 158 | ||
3830 | } | ||
3831 | }, | ||
3832 | { | ||
3833 | "defaultMessage": "!!!Settings", | ||
3834 | "end": { | ||
3835 | "column": 3, | ||
3836 | "line": 165 | ||
3837 | }, | ||
3838 | "file": "src/lib/Menu.js", | ||
3808 | "id": "menu.app.settings", | 3839 | "id": "menu.app.settings", |
3809 | "start": { | 3840 | "start": { |
3810 | "column": 12, | 3841 | "column": 12, |
3811 | "line": 158 | 3842 | "line": 162 |
3812 | } | 3843 | } |
3813 | }, | 3844 | }, |
3814 | { | 3845 | { |
3815 | "defaultMessage": "!!!Hide", | 3846 | "defaultMessage": "!!!Hide", |
3816 | "end": { | 3847 | "end": { |
3817 | "column": 3, | 3848 | "column": 3, |
3818 | "line": 165 | 3849 | "line": 169 |
3819 | }, | 3850 | }, |
3820 | "file": "src/lib/Menu.js", | 3851 | "file": "src/lib/Menu.js", |
3821 | "id": "menu.app.hide", | 3852 | "id": "menu.app.hide", |
3822 | "start": { | 3853 | "start": { |
3823 | "column": 8, | 3854 | "column": 8, |
3824 | "line": 162 | 3855 | "line": 166 |
3825 | } | 3856 | } |
3826 | }, | 3857 | }, |
3827 | { | 3858 | { |
3828 | "defaultMessage": "!!!Hide Others", | 3859 | "defaultMessage": "!!!Hide Others", |
3829 | "end": { | 3860 | "end": { |
3830 | "column": 3, | 3861 | "column": 3, |
3831 | "line": 169 | 3862 | "line": 173 |
3832 | }, | 3863 | }, |
3833 | "file": "src/lib/Menu.js", | 3864 | "file": "src/lib/Menu.js", |
3834 | "id": "menu.app.hideOthers", | 3865 | "id": "menu.app.hideOthers", |
3835 | "start": { | 3866 | "start": { |
3836 | "column": 14, | 3867 | "column": 14, |
3837 | "line": 166 | 3868 | "line": 170 |
3838 | } | 3869 | } |
3839 | }, | 3870 | }, |
3840 | { | 3871 | { |
3841 | "defaultMessage": "!!!Unhide", | 3872 | "defaultMessage": "!!!Unhide", |
3842 | "end": { | 3873 | "end": { |
3843 | "column": 3, | 3874 | "column": 3, |
3844 | "line": 173 | 3875 | "line": 177 |
3845 | }, | 3876 | }, |
3846 | "file": "src/lib/Menu.js", | 3877 | "file": "src/lib/Menu.js", |
3847 | "id": "menu.app.unhide", | 3878 | "id": "menu.app.unhide", |
3848 | "start": { | 3879 | "start": { |
3849 | "column": 10, | 3880 | "column": 10, |
3850 | "line": 170 | 3881 | "line": 174 |
3851 | } | 3882 | } |
3852 | }, | 3883 | }, |
3853 | { | 3884 | { |
3854 | "defaultMessage": "!!!Quit", | 3885 | "defaultMessage": "!!!Quit", |
3855 | "end": { | 3886 | "end": { |
3856 | "column": 3, | 3887 | "column": 3, |
3857 | "line": 177 | 3888 | "line": 181 |
3858 | }, | 3889 | }, |
3859 | "file": "src/lib/Menu.js", | 3890 | "file": "src/lib/Menu.js", |
3860 | "id": "menu.app.quit", | 3891 | "id": "menu.app.quit", |
3861 | "start": { | 3892 | "start": { |
3862 | "column": 8, | 3893 | "column": 8, |
3863 | "line": 174 | 3894 | "line": 178 |
3864 | } | 3895 | } |
3865 | }, | 3896 | }, |
3866 | { | 3897 | { |
3867 | "defaultMessage": "!!!Add New Service...", | 3898 | "defaultMessage": "!!!Add New Service...", |
3868 | "end": { | 3899 | "end": { |
3869 | "column": 3, | 3900 | "column": 3, |
3870 | "line": 181 | 3901 | "line": 185 |
3871 | }, | 3902 | }, |
3872 | "file": "src/lib/Menu.js", | 3903 | "file": "src/lib/Menu.js", |
3873 | "id": "menu.services.addNewService", | 3904 | "id": "menu.services.addNewService", |
3874 | "start": { | 3905 | "start": { |
3875 | "column": 17, | 3906 | "column": 17, |
3876 | "line": 178 | 3907 | "line": 182 |
3877 | } | 3908 | } |
3878 | }, | 3909 | }, |
3879 | { | 3910 | { |
3880 | "defaultMessage": "!!!Activate next service...", | 3911 | "defaultMessage": "!!!Activate next service...", |
3881 | "end": { | 3912 | "end": { |
3882 | "column": 3, | 3913 | "column": 3, |
3883 | "line": 185 | 3914 | "line": 189 |
3884 | }, | 3915 | }, |
3885 | "file": "src/lib/Menu.js", | 3916 | "file": "src/lib/Menu.js", |
3886 | "id": "menu.services.setNextServiceActive", | 3917 | "id": "menu.services.setNextServiceActive", |
3887 | "start": { | 3918 | "start": { |
3888 | "column": 23, | 3919 | "column": 23, |
3889 | "line": 182 | 3920 | "line": 186 |
3890 | } | 3921 | } |
3891 | }, | 3922 | }, |
3892 | { | 3923 | { |
3893 | "defaultMessage": "!!!Activate previous service...", | 3924 | "defaultMessage": "!!!Activate previous service...", |
3894 | "end": { | 3925 | "end": { |
3895 | "column": 3, | 3926 | "column": 3, |
3896 | "line": 189 | 3927 | "line": 193 |
3897 | }, | 3928 | }, |
3898 | "file": "src/lib/Menu.js", | 3929 | "file": "src/lib/Menu.js", |
3899 | "id": "menu.services.activatePreviousService", | 3930 | "id": "menu.services.activatePreviousService", |
3900 | "start": { | 3931 | "start": { |
3901 | "column": 27, | 3932 | "column": 27, |
3902 | "line": 186 | 3933 | "line": 190 |
3903 | } | 3934 | } |
3904 | }, | 3935 | }, |
3905 | { | 3936 | { |
3906 | "defaultMessage": "!!!Disable notifications & audio", | 3937 | "defaultMessage": "!!!Disable notifications & audio", |
3907 | "end": { | 3938 | "end": { |
3908 | "column": 3, | 3939 | "column": 3, |
3909 | "line": 193 | 3940 | "line": 197 |
3910 | }, | 3941 | }, |
3911 | "file": "src/lib/Menu.js", | 3942 | "file": "src/lib/Menu.js", |
3912 | "id": "sidebar.muteApp", | 3943 | "id": "sidebar.muteApp", |
3913 | "start": { | 3944 | "start": { |
3914 | "column": 11, | 3945 | "column": 11, |
3915 | "line": 190 | 3946 | "line": 194 |
3916 | } | 3947 | } |
3917 | }, | 3948 | }, |
3918 | { | 3949 | { |
3919 | "defaultMessage": "!!!Enable notifications & audio", | 3950 | "defaultMessage": "!!!Enable notifications & audio", |
3920 | "end": { | 3951 | "end": { |
3921 | "column": 3, | 3952 | "column": 3, |
3922 | "line": 197 | 3953 | "line": 201 |
3923 | }, | 3954 | }, |
3924 | "file": "src/lib/Menu.js", | 3955 | "file": "src/lib/Menu.js", |
3925 | "id": "sidebar.unmuteApp", | 3956 | "id": "sidebar.unmuteApp", |
3926 | "start": { | 3957 | "start": { |
3927 | "column": 13, | 3958 | "column": 13, |
3928 | "line": 194 | 3959 | "line": 198 |
3929 | } | 3960 | } |
3930 | } | 3961 | } |
3931 | ], | 3962 | ], |
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 7543d38bd..573231c45 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json | |||
@@ -1,6 +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.delayApp.action": "Get a Franz Supporter License", | 5 | "feature.delayApp.action": "Get a Franz Supporter License", |
5 | "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", |
6 | "feature.delayApp.text": "Franz will continue in {seconds} seconds.", | 7 | "feature.delayApp.text": "Franz will continue in {seconds} seconds.", |
@@ -43,6 +44,7 @@ | |||
43 | "login.submit.label": "Sign in", | 44 | "login.submit.label": "Sign in", |
44 | "login.tokenExpired": "Your session expired, please login again.", | 45 | "login.tokenExpired": "Your session expired, please login again.", |
45 | "menu.app.about": "About Franz", | 46 | "menu.app.about": "About Franz", |
47 | "menu.app.announcement": "What's new in Franz?", | ||
46 | "menu.app.hide": "Hide", | 48 | "menu.app.hide": "Hide", |
47 | "menu.app.hideOthers": "Hide Others", | 49 | "menu.app.hideOthers": "Hide Others", |
48 | "menu.app.quit": "Quit", | 50 | "menu.app.quit": "Quit", |
diff --git a/src/i18n/messages/src/components/layout/AppLayout.json b/src/i18n/messages/src/components/layout/AppLayout.json index 07603d062..384d4b441 100644 --- a/src/i18n/messages/src/components/layout/AppLayout.json +++ b/src/i18n/messages/src/components/layout/AppLayout.json | |||
@@ -4,11 +4,11 @@ | |||
4 | "defaultMessage": "!!!Your services have been updated.", | 4 | "defaultMessage": "!!!Your services have been updated.", |
5 | "file": "src/components/layout/AppLayout.js", | 5 | "file": "src/components/layout/AppLayout.js", |
6 | "start": { | 6 | "start": { |
7 | "line": 22, | 7 | "line": 23, |
8 | "column": 19 | 8 | "column": 19 |
9 | }, | 9 | }, |
10 | "end": { | 10 | "end": { |
11 | "line": 25, | 11 | "line": 26, |
12 | "column": 3 | 12 | "column": 3 |
13 | } | 13 | } |
14 | }, | 14 | }, |
@@ -17,11 +17,11 @@ | |||
17 | "defaultMessage": "!!!A new update for Franz is available.", | 17 | "defaultMessage": "!!!A new update for Franz is available.", |
18 | "file": "src/components/layout/AppLayout.js", | 18 | "file": "src/components/layout/AppLayout.js", |
19 | "start": { | 19 | "start": { |
20 | "line": 26, | 20 | "line": 27, |
21 | "column": 19 | 21 | "column": 19 |
22 | }, | 22 | }, |
23 | "end": { | 23 | "end": { |
24 | "line": 29, | 24 | "line": 30, |
25 | "column": 3 | 25 | "column": 3 |
26 | } | 26 | } |
27 | }, | 27 | }, |
@@ -30,11 +30,11 @@ | |||
30 | "defaultMessage": "!!!Reload services", | 30 | "defaultMessage": "!!!Reload services", |
31 | "file": "src/components/layout/AppLayout.js", | 31 | "file": "src/components/layout/AppLayout.js", |
32 | "start": { | 32 | "start": { |
33 | "line": 30, | 33 | "line": 31, |
34 | "column": 24 | 34 | "column": 24 |
35 | }, | 35 | }, |
36 | "end": { | 36 | "end": { |
37 | "line": 33, | 37 | "line": 34, |
38 | "column": 3 | 38 | "column": 3 |
39 | } | 39 | } |
40 | }, | 40 | }, |
@@ -43,11 +43,11 @@ | |||
43 | "defaultMessage": "!!!Changelog", | 43 | "defaultMessage": "!!!Changelog", |
44 | "file": "src/components/layout/AppLayout.js", | 44 | "file": "src/components/layout/AppLayout.js", |
45 | "start": { | 45 | "start": { |
46 | "line": 34, | 46 | "line": 35, |
47 | "column": 13 | 47 | "column": 13 |
48 | }, | 48 | }, |
49 | "end": { | 49 | "end": { |
50 | "line": 37, | 50 | "line": 38, |
51 | "column": 3 | 51 | "column": 3 |
52 | } | 52 | } |
53 | }, | 53 | }, |
@@ -56,11 +56,11 @@ | |||
56 | "defaultMessage": "!!!Restart & install update", | 56 | "defaultMessage": "!!!Restart & install update", |
57 | "file": "src/components/layout/AppLayout.js", | 57 | "file": "src/components/layout/AppLayout.js", |
58 | "start": { | 58 | "start": { |
59 | "line": 38, | 59 | "line": 39, |
60 | "column": 23 | 60 | "column": 23 |
61 | }, | 61 | }, |
62 | "end": { | 62 | "end": { |
63 | "line": 41, | 63 | "line": 42, |
64 | "column": 3 | 64 | "column": 3 |
65 | } | 65 | } |
66 | }, | 66 | }, |
@@ -69,11 +69,11 @@ | |||
69 | "defaultMessage": "!!!Could not load services and user information", | 69 | "defaultMessage": "!!!Could not load services and user information", |
70 | "file": "src/components/layout/AppLayout.js", | 70 | "file": "src/components/layout/AppLayout.js", |
71 | "start": { | 71 | "start": { |
72 | "line": 42, | 72 | "line": 43, |
73 | "column": 26 | 73 | "column": 26 |
74 | }, | 74 | }, |
75 | "end": { | 75 | "end": { |
76 | "line": 45, | 76 | "line": 46, |
77 | "column": 3 | 77 | "column": 3 |
78 | } | 78 | } |
79 | } | 79 | } |
diff --git a/src/i18n/messages/src/features/announcements/Component.json b/src/i18n/messages/src/features/announcements/Component.json new file mode 100644 index 000000000..18e1b84c5 --- /dev/null +++ b/src/i18n/messages/src/features/announcements/Component.json | |||
@@ -0,0 +1,15 @@ | |||
1 | [ | ||
2 | { | ||
3 | "id": "feature.announcements.headline", | ||
4 | "defaultMessage": "!!!What's new in Franz {version}?", | ||
5 | "file": "src/features/announcements/Component.js", | ||
6 | "start": { | ||
7 | "line": 11, | ||
8 | "column": 12 | ||
9 | }, | ||
10 | "end": { | ||
11 | "line": 14, | ||
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 9314f5cce..0db994871 100644 --- a/src/i18n/messages/src/lib/Menu.json +++ b/src/i18n/messages/src/lib/Menu.json | |||
@@ -481,15 +481,28 @@ | |||
481 | } | 481 | } |
482 | }, | 482 | }, |
483 | { | 483 | { |
484 | "id": "menu.app.announcement", | ||
485 | "defaultMessage": "!!!What's new in Franz?", | ||
486 | "file": "src/lib/Menu.js", | ||
487 | "start": { | ||
488 | "line": 158, | ||
489 | "column": 16 | ||
490 | }, | ||
491 | "end": { | ||
492 | "line": 161, | ||
493 | "column": 3 | ||
494 | } | ||
495 | }, | ||
496 | { | ||
484 | "id": "menu.app.settings", | 497 | "id": "menu.app.settings", |
485 | "defaultMessage": "!!!Settings", | 498 | "defaultMessage": "!!!Settings", |
486 | "file": "src/lib/Menu.js", | 499 | "file": "src/lib/Menu.js", |
487 | "start": { | 500 | "start": { |
488 | "line": 158, | 501 | "line": 162, |
489 | "column": 12 | 502 | "column": 12 |
490 | }, | 503 | }, |
491 | "end": { | 504 | "end": { |
492 | "line": 161, | 505 | "line": 165, |
493 | "column": 3 | 506 | "column": 3 |
494 | } | 507 | } |
495 | }, | 508 | }, |
@@ -498,11 +511,11 @@ | |||
498 | "defaultMessage": "!!!Hide", | 511 | "defaultMessage": "!!!Hide", |
499 | "file": "src/lib/Menu.js", | 512 | "file": "src/lib/Menu.js", |
500 | "start": { | 513 | "start": { |
501 | "line": 162, | 514 | "line": 166, |
502 | "column": 8 | 515 | "column": 8 |
503 | }, | 516 | }, |
504 | "end": { | 517 | "end": { |
505 | "line": 165, | 518 | "line": 169, |
506 | "column": 3 | 519 | "column": 3 |
507 | } | 520 | } |
508 | }, | 521 | }, |
@@ -511,11 +524,11 @@ | |||
511 | "defaultMessage": "!!!Hide Others", | 524 | "defaultMessage": "!!!Hide Others", |
512 | "file": "src/lib/Menu.js", | 525 | "file": "src/lib/Menu.js", |
513 | "start": { | 526 | "start": { |
514 | "line": 166, | 527 | "line": 170, |
515 | "column": 14 | 528 | "column": 14 |
516 | }, | 529 | }, |
517 | "end": { | 530 | "end": { |
518 | "line": 169, | 531 | "line": 173, |
519 | "column": 3 | 532 | "column": 3 |
520 | } | 533 | } |
521 | }, | 534 | }, |
@@ -524,11 +537,11 @@ | |||
524 | "defaultMessage": "!!!Unhide", | 537 | "defaultMessage": "!!!Unhide", |
525 | "file": "src/lib/Menu.js", | 538 | "file": "src/lib/Menu.js", |
526 | "start": { | 539 | "start": { |
527 | "line": 170, | 540 | "line": 174, |
528 | "column": 10 | 541 | "column": 10 |
529 | }, | 542 | }, |
530 | "end": { | 543 | "end": { |
531 | "line": 173, | 544 | "line": 177, |
532 | "column": 3 | 545 | "column": 3 |
533 | } | 546 | } |
534 | }, | 547 | }, |
@@ -537,11 +550,11 @@ | |||
537 | "defaultMessage": "!!!Quit", | 550 | "defaultMessage": "!!!Quit", |
538 | "file": "src/lib/Menu.js", | 551 | "file": "src/lib/Menu.js", |
539 | "start": { | 552 | "start": { |
540 | "line": 174, | 553 | "line": 178, |
541 | "column": 8 | 554 | "column": 8 |
542 | }, | 555 | }, |
543 | "end": { | 556 | "end": { |
544 | "line": 177, | 557 | "line": 181, |
545 | "column": 3 | 558 | "column": 3 |
546 | } | 559 | } |
547 | }, | 560 | }, |
@@ -550,11 +563,11 @@ | |||
550 | "defaultMessage": "!!!Add New Service...", | 563 | "defaultMessage": "!!!Add New Service...", |
551 | "file": "src/lib/Menu.js", | 564 | "file": "src/lib/Menu.js", |
552 | "start": { | 565 | "start": { |
553 | "line": 178, | 566 | "line": 182, |
554 | "column": 17 | 567 | "column": 17 |
555 | }, | 568 | }, |
556 | "end": { | 569 | "end": { |
557 | "line": 181, | 570 | "line": 185, |
558 | "column": 3 | 571 | "column": 3 |
559 | } | 572 | } |
560 | }, | 573 | }, |
@@ -563,11 +576,11 @@ | |||
563 | "defaultMessage": "!!!Activate next service...", | 576 | "defaultMessage": "!!!Activate next service...", |
564 | "file": "src/lib/Menu.js", | 577 | "file": "src/lib/Menu.js", |
565 | "start": { | 578 | "start": { |
566 | "line": 182, | 579 | "line": 186, |
567 | "column": 23 | 580 | "column": 23 |
568 | }, | 581 | }, |
569 | "end": { | 582 | "end": { |
570 | "line": 185, | 583 | "line": 189, |
571 | "column": 3 | 584 | "column": 3 |
572 | } | 585 | } |
573 | }, | 586 | }, |
@@ -576,11 +589,11 @@ | |||
576 | "defaultMessage": "!!!Activate previous service...", | 589 | "defaultMessage": "!!!Activate previous service...", |
577 | "file": "src/lib/Menu.js", | 590 | "file": "src/lib/Menu.js", |
578 | "start": { | 591 | "start": { |
579 | "line": 186, | 592 | "line": 190, |
580 | "column": 27 | 593 | "column": 27 |
581 | }, | 594 | }, |
582 | "end": { | 595 | "end": { |
583 | "line": 189, | 596 | "line": 193, |
584 | "column": 3 | 597 | "column": 3 |
585 | } | 598 | } |
586 | }, | 599 | }, |
@@ -589,11 +602,11 @@ | |||
589 | "defaultMessage": "!!!Disable notifications & audio", | 602 | "defaultMessage": "!!!Disable notifications & audio", |
590 | "file": "src/lib/Menu.js", | 603 | "file": "src/lib/Menu.js", |
591 | "start": { | 604 | "start": { |
592 | "line": 190, | 605 | "line": 194, |
593 | "column": 11 | 606 | "column": 11 |
594 | }, | 607 | }, |
595 | "end": { | 608 | "end": { |
596 | "line": 193, | 609 | "line": 197, |
597 | "column": 3 | 610 | "column": 3 |
598 | } | 611 | } |
599 | }, | 612 | }, |
@@ -602,11 +615,11 @@ | |||
602 | "defaultMessage": "!!!Enable notifications & audio", | 615 | "defaultMessage": "!!!Enable notifications & audio", |
603 | "file": "src/lib/Menu.js", | 616 | "file": "src/lib/Menu.js", |
604 | "start": { | 617 | "start": { |
605 | "line": 194, | 618 | "line": 198, |
606 | "column": 13 | 619 | "column": 13 |
607 | }, | 620 | }, |
608 | "end": { | 621 | "end": { |
609 | "line": 197, | 622 | "line": 201, |
610 | "column": 3 | 623 | "column": 3 |
611 | } | 624 | } |
612 | } | 625 | } |
diff --git a/src/lib/Menu.js b/src/lib/Menu.js index 7a60c448f..70f3b2877 100644 --- a/src/lib/Menu.js +++ b/src/lib/Menu.js | |||
@@ -155,6 +155,10 @@ const menuItems = defineMessages({ | |||
155 | id: 'menu.app.about', | 155 | id: 'menu.app.about', |
156 | defaultMessage: '!!!About Franz', | 156 | defaultMessage: '!!!About Franz', |
157 | }, | 157 | }, |
158 | announcement: { | ||
159 | id: 'menu.app.announcement', | ||
160 | defaultMessage: '!!!What\'s new in Franz?', | ||
161 | }, | ||
158 | settings: { | 162 | settings: { |
159 | id: 'menu.app.settings', | 163 | id: 'menu.app.settings', |
160 | defaultMessage: '!!!Settings', | 164 | defaultMessage: '!!!Settings', |
@@ -590,6 +594,12 @@ export default class FranzMenu { | |||
590 | role: 'about', | 594 | role: 'about', |
591 | }, | 595 | }, |
592 | { | 596 | { |
597 | label: intl.formatMessage(menuItems.announcement), | ||
598 | click: () => { | ||
599 | this.actions.announcements.show(); | ||
600 | }, | ||
601 | }, | ||
602 | { | ||
593 | type: 'separator', | 603 | type: 'separator', |
594 | }, | 604 | }, |
595 | { | 605 | { |
diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js index d2842083c..1c9044b07 100644 --- a/src/stores/FeaturesStore.js +++ b/src/stores/FeaturesStore.js | |||
@@ -8,6 +8,7 @@ import spellchecker from '../features/spellchecker'; | |||
8 | import serviceProxy from '../features/serviceProxy'; | 8 | import serviceProxy from '../features/serviceProxy'; |
9 | import basicAuth from '../features/basicAuth'; | 9 | import basicAuth from '../features/basicAuth'; |
10 | import shareFranz from '../features/shareFranz'; | 10 | import shareFranz from '../features/shareFranz'; |
11 | import announcements from '../features/announcements'; | ||
11 | 12 | ||
12 | import { DEFAULT_FEATURES_CONFIG } from '../config'; | 13 | import { DEFAULT_FEATURES_CONFIG } from '../config'; |
13 | 14 | ||
@@ -58,5 +59,6 @@ export default class FeaturesStore extends Store { | |||
58 | serviceProxy(this.stores, this.actions); | 59 | serviceProxy(this.stores, this.actions); |
59 | basicAuth(this.stores, this.actions); | 60 | basicAuth(this.stores, this.actions); |
60 | shareFranz(this.stores, this.actions); | 61 | shareFranz(this.stores, this.actions); |
62 | announcements(this.stores, this.actions); | ||
61 | } | 63 | } |
62 | } | 64 | } |
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 69e616f0c..88b0331bf 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js | |||
@@ -35,6 +35,7 @@ export default class ServicesStore extends Store { | |||
35 | 35 | ||
36 | // Register action handlers | 36 | // Register action handlers |
37 | this.actions.service.setActive.listen(this._setActive.bind(this)); | 37 | this.actions.service.setActive.listen(this._setActive.bind(this)); |
38 | this.actions.service.blurActive.listen(this._blurActive.bind(this)); | ||
38 | this.actions.service.setActiveNext.listen(this._setActiveNext.bind(this)); | 39 | this.actions.service.setActiveNext.listen(this._setActiveNext.bind(this)); |
39 | this.actions.service.setActivePrev.listen(this._setActivePrev.bind(this)); | 40 | this.actions.service.setActivePrev.listen(this._setActivePrev.bind(this)); |
40 | this.actions.service.showAddServiceInterface.listen(this._showAddServiceInterface.bind(this)); | 41 | this.actions.service.showAddServiceInterface.listen(this._showAddServiceInterface.bind(this)); |
@@ -298,6 +299,11 @@ export default class ServicesStore extends Store { | |||
298 | this._focusActiveService(); | 299 | this._focusActiveService(); |
299 | } | 300 | } |
300 | 301 | ||
302 | @action _blurActive() { | ||
303 | if (!this.active) return; | ||
304 | this.active.isActive = false; | ||
305 | } | ||
306 | |||
301 | @action _setActiveNext() { | 307 | @action _setActiveNext() { |
302 | const nextIndex = this._wrapIndex(this.allDisplayed.findIndex(service => service.isActive), 1, this.allDisplayed.length); | 308 | const nextIndex = this._wrapIndex(this.allDisplayed.findIndex(service => service.isActive), 1, this.allDisplayed.length); |
303 | 309 | ||