diff options
Diffstat (limited to 'src/features/announcements/store.js')
-rw-r--r-- | src/features/announcements/store.js | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/src/features/announcements/store.js b/src/features/announcements/store.js new file mode 100644 index 000000000..ad78a0979 --- /dev/null +++ b/src/features/announcements/store.js | |||
@@ -0,0 +1,144 @@ | |||
1 | import { | ||
2 | action, | ||
3 | computed, | ||
4 | observable, | ||
5 | } from 'mobx'; | ||
6 | import semver from 'semver'; | ||
7 | import localStorage from 'mobx-localstorage'; | ||
8 | |||
9 | import { FeatureStore } from '../utils/FeatureStore'; | ||
10 | import { ANNOUNCEMENTS_ROUTES, GA_CATEGORY_ANNOUNCEMENTS } from '.'; | ||
11 | import { getAnnouncementRequest, getChangelogRequest, getCurrentVersionRequest } from './api'; | ||
12 | import { announcementActions } from './actions'; | ||
13 | import { createActionBindings } from '../utils/ActionBinding'; | ||
14 | import { createReactions } from '../../stores/lib/Reaction'; | ||
15 | import { gaEvent } from '../../lib/analytics'; | ||
16 | import { matchRoute } from '../../helpers/routing-helpers'; | ||
17 | |||
18 | const LOCAL_STORAGE_KEY = 'announcements'; | ||
19 | |||
20 | const debug = require('debug')('Franz:feature:announcements:store'); | ||
21 | |||
22 | export class AnnouncementsStore extends FeatureStore { | ||
23 | @observable targetVersion = null; | ||
24 | |||
25 | @observable isFeatureActive = false; | ||
26 | |||
27 | @computed get changelog() { | ||
28 | return getChangelogRequest.result; | ||
29 | } | ||
30 | |||
31 | @computed get announcement() { | ||
32 | if (!this.stores || !getAnnouncementRequest.result) return null; | ||
33 | const { locale, defaultLocale } = this.stores.app; | ||
34 | const announcement = getAnnouncementRequest.result; | ||
35 | // User locale | ||
36 | if (announcement[locale]) return announcement[locale]; | ||
37 | // Default locale | ||
38 | if (announcement[defaultLocale]) return announcement[defaultLocale]; | ||
39 | // No locales specified | ||
40 | return announcement; | ||
41 | } | ||
42 | |||
43 | @computed get areNewsAvailable() { | ||
44 | const isChangelogAvailable = getChangelogRequest.wasExecuted && !!this.changelog; | ||
45 | const isAnnouncementAvailable = getAnnouncementRequest.wasExecuted && !!this.announcement; | ||
46 | return isChangelogAvailable || isAnnouncementAvailable; | ||
47 | } | ||
48 | |||
49 | @computed get settings() { | ||
50 | return localStorage.getItem(LOCAL_STORAGE_KEY) || {}; | ||
51 | } | ||
52 | |||
53 | @computed get lastSeenAnnouncementVersion() { | ||
54 | return this.settings.lastSeenAnnouncementVersion || null; | ||
55 | } | ||
56 | |||
57 | @computed get currentVersion() { | ||
58 | return getCurrentVersionRequest.result; | ||
59 | } | ||
60 | |||
61 | @computed get isNewUser() { | ||
62 | return this.stores.settings.stats.appStarts <= 1; | ||
63 | } | ||
64 | |||
65 | async start(stores, actions) { | ||
66 | debug('AnnouncementsStore::start'); | ||
67 | this.stores = stores; | ||
68 | this.actions = actions; | ||
69 | getCurrentVersionRequest.execute(); | ||
70 | |||
71 | this._registerActions(createActionBindings([ | ||
72 | [announcementActions.show, this._showAnnouncement], | ||
73 | ])); | ||
74 | |||
75 | this._reactions = createReactions([ | ||
76 | this._showAnnouncementOnRouteMatch, | ||
77 | this._showAnnouncementToUsersWhoUpdatedApp, | ||
78 | this._fetchAnnouncements, | ||
79 | ]); | ||
80 | this._registerReactions(this._reactions); | ||
81 | this.isFeatureActive = true; | ||
82 | } | ||
83 | |||
84 | stop() { | ||
85 | super.stop(); | ||
86 | debug('AnnouncementsStore::stop'); | ||
87 | this.isFeatureActive = false; | ||
88 | } | ||
89 | |||
90 | // ======= HELPERS ======= // | ||
91 | |||
92 | _updateSettings = (changes) => { | ||
93 | localStorage.setItem(LOCAL_STORAGE_KEY, { | ||
94 | ...this.settings, | ||
95 | ...changes, | ||
96 | }); | ||
97 | }; | ||
98 | |||
99 | // ======= ACTIONS ======= // | ||
100 | |||
101 | @action _showAnnouncement = ({ targetVersion } = {}) => { | ||
102 | const { router } = this.stores; | ||
103 | this.targetVersion = targetVersion || this.currentVersion; | ||
104 | this._updateSettings({ | ||
105 | lastSeenAnnouncementVersion: this.currentVersion, | ||
106 | }); | ||
107 | const targetRoute = `/announcements/${this.targetVersion}`; | ||
108 | if (router.location.pathname !== targetRoute) { | ||
109 | this.stores.router.push(targetRoute); | ||
110 | } | ||
111 | gaEvent(GA_CATEGORY_ANNOUNCEMENTS, 'show'); | ||
112 | }; | ||
113 | |||
114 | // ======= REACTIONS ======== | ||
115 | |||
116 | _showAnnouncementToUsersWhoUpdatedApp = () => { | ||
117 | const { announcement, isNewUser } = this; | ||
118 | // Check if there is an announcement and don't show announcements to new users | ||
119 | if (!announcement || isNewUser) return; | ||
120 | |||
121 | // Check if the user has already used current version (= has seen the announcement) | ||
122 | const { currentVersion, lastSeenAnnouncementVersion } = this; | ||
123 | if (semver.gt(currentVersion, lastSeenAnnouncementVersion || '0.0.0')) { | ||
124 | debug(`${currentVersion} > ${lastSeenAnnouncementVersion}: announcement is shown`); | ||
125 | this._showAnnouncement(); | ||
126 | } | ||
127 | }; | ||
128 | |||
129 | _fetchAnnouncements = () => { | ||
130 | const targetVersion = this.targetVersion || this.currentVersion; | ||
131 | if (!targetVersion) return; | ||
132 | getChangelogRequest.reset().execute(targetVersion); | ||
133 | getAnnouncementRequest.reset().execute(targetVersion); | ||
134 | }; | ||
135 | |||
136 | _showAnnouncementOnRouteMatch = () => { | ||
137 | const { router } = this.stores; | ||
138 | const match = matchRoute(ANNOUNCEMENTS_ROUTES.TARGET, router.location.pathname); | ||
139 | if (match) { | ||
140 | const targetVersion = match.id; | ||
141 | this._showAnnouncement({ targetVersion }); | ||
142 | } | ||
143 | } | ||
144 | } | ||