diff options
author | 2019-09-06 09:28:12 +0200 | |
---|---|---|
committer | 2019-09-06 09:28:12 +0200 | |
commit | b5b2976f447779a584f0d2fdcce21c8e62156df3 (patch) | |
tree | 9c13cf950b28f9d8824c5a1c028a53aae4bedeef | |
parent | Update 3rd party recipe wording (diff) | |
parent | Fix race condition when team data is not yet loaded (diff) | |
download | ferdium-app-b5b2976f447779a584f0d2fdcce21c8e62156df3.tar.gz ferdium-app-b5b2976f447779a584f0d2fdcce21c8e62156df3.tar.zst ferdium-app-b5b2976f447779a584f0d2fdcce21c8e62156df3.zip |
Merge branch 'develop' of https://github.com/meetfranz/franz into develop
-rw-r--r-- | src/components/layout/AppLayout.js | 10 | ||||
-rw-r--r-- | src/features/announcements/store.js | 5 | ||||
-rw-r--r-- | src/features/delayApp/index.js | 10 | ||||
-rw-r--r-- | src/features/todos/components/TodosWebview.js | 104 | ||||
-rw-r--r-- | src/features/todos/containers/TodosScreen.js | 2 | ||||
-rw-r--r-- | src/features/todos/store.js | 22 | ||||
-rw-r--r-- | src/stores/UserStore.js | 6 |
7 files changed, 92 insertions, 67 deletions
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js index 941e60bfd..200777ae6 100644 --- a/src/components/layout/AppLayout.js +++ b/src/components/layout/AppLayout.js | |||
@@ -125,7 +125,15 @@ class AppLayout extends Component { | |||
125 | sticky={item.sticky} | 125 | sticky={item.sticky} |
126 | onHide={() => removeNewsItem({ newsId: item.id })} | 126 | onHide={() => removeNewsItem({ newsId: item.id })} |
127 | > | 127 | > |
128 | <span dangerouslySetInnerHTML={createMarkup(item.message)} /> | 128 | <span |
129 | dangerouslySetInnerHTML={createMarkup(item.message)} | ||
130 | onClick={(event) => { | ||
131 | const { target } = event; | ||
132 | if (target && target.hasAttribute('data-is-news-cta')) { | ||
133 | removeNewsItem({ newsId: item.id }); | ||
134 | } | ||
135 | }} | ||
136 | /> | ||
129 | </InfoBar> | 137 | </InfoBar> |
130 | ))} | 138 | ))} |
131 | {hasActivatedTrial && ( | 139 | {hasActivatedTrial && ( |
diff --git a/src/features/announcements/store.js b/src/features/announcements/store.js index de7ed2596..d58afbc8e 100644 --- a/src/features/announcements/store.js +++ b/src/features/announcements/store.js | |||
@@ -63,6 +63,11 @@ export class AnnouncementsStore extends FeatureStore { | |||
63 | return this.stores.settings.stats.appStarts <= 1; | 63 | return this.stores.settings.stats.appStarts <= 1; |
64 | } | 64 | } |
65 | 65 | ||
66 | @computed get isAnnouncementShown() { | ||
67 | const { router } = this.stores; | ||
68 | return router.location.pathname.includes('/announcements'); | ||
69 | } | ||
70 | |||
66 | async start(stores, actions) { | 71 | async start(stores, actions) { |
67 | debug('AnnouncementsStore::start'); | 72 | debug('AnnouncementsStore::start'); |
68 | this.stores = stores; | 73 | this.stores = stores; |
diff --git a/src/features/delayApp/index.js b/src/features/delayApp/index.js index 627537de7..c0029873a 100644 --- a/src/features/delayApp/index.js +++ b/src/features/delayApp/index.js | |||
@@ -44,14 +44,16 @@ export default function init(stores) { | |||
44 | config.delayDuration = globalConfig.wait !== undefined ? globalConfig.wait : DEFAULT_FEATURES_CONFIG.needToWaitToProceedConfig.wait; | 44 | config.delayDuration = globalConfig.wait !== undefined ? globalConfig.wait : DEFAULT_FEATURES_CONFIG.needToWaitToProceedConfig.wait; |
45 | 45 | ||
46 | autorun(() => { | 46 | autorun(() => { |
47 | if (stores.services.allDisplayed.length === 0) { | 47 | const { isAnnouncementShown } = stores.announcements; |
48 | debug('seas', stores.services.all.length); | 48 | if (stores.services.allDisplayed.length === 0 || isAnnouncementShown) { |
49 | shownAfterLaunch = true; | 49 | shownAfterLaunch = true; |
50 | setVisibility(false); | ||
50 | return; | 51 | return; |
51 | } | 52 | } |
52 | 53 | ||
53 | const diff = moment().diff(timeLastDelay); | 54 | const diff = moment().diff(timeLastDelay); |
54 | if ((stores.app.isFocused && diff >= config.delayOffset) || !shownAfterLaunch) { | 55 | const itsTimeToWait = diff >= config.delayOffset; |
56 | if (!isAnnouncementShown && ((stores.app.isFocused && itsTimeToWait) || !shownAfterLaunch)) { | ||
55 | debug(`App will be delayed for ${config.delayDuration / 1000}s`); | 57 | debug(`App will be delayed for ${config.delayDuration / 1000}s`); |
56 | 58 | ||
57 | setVisibility(true); | 59 | setVisibility(true); |
@@ -66,6 +68,8 @@ export default function init(stores) { | |||
66 | 68 | ||
67 | setVisibility(false); | 69 | setVisibility(false); |
68 | }, config.delayDuration + 1000); // timer needs to be able to hit 0 | 70 | }, config.delayDuration + 1000); // timer needs to be able to hit 0 |
71 | } else { | ||
72 | setVisibility(false); | ||
69 | } | 73 | } |
70 | }); | 74 | }); |
71 | } else { | 75 | } else { |
diff --git a/src/features/todos/components/TodosWebview.js b/src/features/todos/components/TodosWebview.js index 44c94a3ff..c06183e37 100644 --- a/src/features/todos/components/TodosWebview.js +++ b/src/features/todos/components/TodosWebview.js | |||
@@ -41,10 +41,6 @@ const styles = theme => ({ | |||
41 | '&:hover $closeTodosButton': { | 41 | '&:hover $closeTodosButton': { |
42 | opacity: 1, | 42 | opacity: 1, |
43 | }, | 43 | }, |
44 | }, | ||
45 | webview: { | ||
46 | height: '100%', | ||
47 | |||
48 | '& webview': { | 44 | '& webview': { |
49 | height: '100%', | 45 | height: '100%', |
50 | }, | 46 | }, |
@@ -245,60 +241,58 @@ class TodosWebview extends Component { | |||
245 | const { intl } = this.context; | 241 | const { intl } = this.context; |
246 | 242 | ||
247 | return ( | 243 | return ( |
248 | <> | 244 | <div |
249 | <div | 245 | className={classes.root} |
250 | className={classes.root} | 246 | style={{ width: isVisible ? width : 0 }} |
251 | style={{ width: isVisible ? width : 0 }} | 247 | onMouseUp={() => this.stopResize()} |
252 | onMouseUp={() => this.stopResize()} | 248 | ref={(node) => { this.node = node; }} |
253 | ref={(node) => { this.node = node; }} | 249 | > |
250 | <button | ||
251 | onClick={() => togglePanel()} | ||
252 | className={isVisible ? classes.closeTodosButton : classes.openTodosButton} | ||
253 | type="button" | ||
254 | > | 254 | > |
255 | <button | 255 | <Icon icon={isVisible ? mdiChevronRight : mdiCheckAll} size={2} /> |
256 | onClick={() => togglePanel()} | 256 | </button> |
257 | className={isVisible ? classes.closeTodosButton : classes.openTodosButton} | 257 | <div |
258 | type="button" | 258 | className={classes.resizeHandler} |
259 | > | 259 | style={Object.assign({ left: delta }, isDragging ? { width: 600, marginLeft: -200 } : {})} // This hack is required as resizing with webviews beneath behaves quite bad |
260 | <Icon icon={isVisible ? mdiChevronRight : mdiCheckAll} size={2} /> | 260 | onMouseDown={e => this.startResize(e)} |
261 | </button> | 261 | /> |
262 | {isDragging && ( | ||
262 | <div | 263 | <div |
263 | className={classes.resizeHandler} | 264 | className={classes.dragIndicator} |
264 | style={Object.assign({ left: delta }, isDragging ? { width: 600, marginLeft: -200 } : {})} // This hack is required as resizing with webviews beneath behaves quite bad | 265 | style={{ left: delta }} // This hack is required as resizing with webviews beneath behaves quite bad |
265 | onMouseDown={e => this.startResize(e)} | 266 | /> |
267 | )} | ||
268 | {isTodosIncludedInCurrentPlan ? ( | ||
269 | <Webview | ||
270 | className={classes.webview} | ||
271 | onDidAttach={() => { | ||
272 | const { setTodosWebview } = this.props; | ||
273 | setTodosWebview(this.webview); | ||
274 | this.startListeningToIpcMessages(); | ||
275 | }} | ||
276 | partition="persist:todos" | ||
277 | preload="./features/todos/preload.js" | ||
278 | ref={(webview) => { this.webview = webview ? webview.view : null; }} | ||
279 | src={environment.TODOS_FRONTEND} | ||
266 | /> | 280 | /> |
267 | {isDragging && ( | 281 | ) : ( |
268 | <div | 282 | <Appear> |
269 | className={classes.dragIndicator} | 283 | <div className={classes.premiumContainer}> |
270 | style={{ left: delta }} // This hack is required as resizing with webviews beneath behaves quite bad | 284 | <Icon icon={mdiCheckAll} className={classes.premiumIcon} size={4} /> |
271 | /> | 285 | <p>{intl.formatMessage(messages.premiumInfo)}</p> |
272 | )} | 286 | <p>{intl.formatMessage(messages.rolloutInfo)}</p> |
273 | {isTodosIncludedInCurrentPlan ? ( | 287 | <UpgradeButton |
274 | <Webview | 288 | className={classes.premiumCTA} |
275 | className={classes.webview} | 289 | gaEventInfo={{ category: 'Todos', event: 'upgrade' }} |
276 | onDidAttach={() => { | 290 | short |
277 | const { setTodosWebview } = this.props; | 291 | /> |
278 | setTodosWebview(this.webview); | 292 | </div> |
279 | this.startListeningToIpcMessages(); | 293 | </Appear> |
280 | }} | 294 | )} |
281 | partition="persist:todos" | 295 | </div> |
282 | preload="./features/todos/preload.js" | ||
283 | ref={(webview) => { this.webview = webview ? webview.view : null; }} | ||
284 | src={environment.TODOS_FRONTEND} | ||
285 | /> | ||
286 | ) : ( | ||
287 | <Appear> | ||
288 | <div className={classes.premiumContainer}> | ||
289 | <Icon icon={mdiCheckAll} className={classes.premiumIcon} size={4} /> | ||
290 | <p>{intl.formatMessage(messages.premiumInfo)}</p> | ||
291 | <p>{intl.formatMessage(messages.rolloutInfo)}</p> | ||
292 | <UpgradeButton | ||
293 | className={classes.premiumCTA} | ||
294 | gaEventInfo={{ category: 'Todos', event: 'upgrade' }} | ||
295 | short | ||
296 | /> | ||
297 | </div> | ||
298 | </Appear> | ||
299 | )} | ||
300 | </div> | ||
301 | </> | ||
302 | ); | 296 | ); |
303 | } | 297 | } |
304 | } | 298 | } |
diff --git a/src/features/todos/containers/TodosScreen.js b/src/features/todos/containers/TodosScreen.js index 65afc985b..a5da0b014 100644 --- a/src/features/todos/containers/TodosScreen.js +++ b/src/features/todos/containers/TodosScreen.js | |||
@@ -11,7 +11,7 @@ import { todoActions } from '../actions'; | |||
11 | @inject('stores', 'actions') @observer | 11 | @inject('stores', 'actions') @observer |
12 | class TodosScreen extends Component { | 12 | class TodosScreen extends Component { |
13 | render() { | 13 | render() { |
14 | if (!todosStore || !todosStore.isFeatureActive) { | 14 | if (!todosStore || !todosStore.isFeatureActive || todosStore.isTodosPanelForceHidden) { |
15 | return null; | 15 | return null; |
16 | } | 16 | } |
17 | 17 | ||
diff --git a/src/features/todos/store.js b/src/features/todos/store.js index 5c6abff4c..ea05077ab 100644 --- a/src/features/todos/store.js +++ b/src/features/todos/store.js | |||
@@ -29,10 +29,13 @@ export default class TodoStore extends FeatureStore { | |||
29 | return width < TODOS_MIN_WIDTH ? TODOS_MIN_WIDTH : width; | 29 | return width < TODOS_MIN_WIDTH ? TODOS_MIN_WIDTH : width; |
30 | } | 30 | } |
31 | 31 | ||
32 | @computed get isTodosPanelForceHidden() { | ||
33 | const { isAnnouncementShown } = this.stores.announcements; | ||
34 | return delayAppState.isDelayAppScreenVisible || isAnnouncementShown; | ||
35 | } | ||
36 | |||
32 | @computed get isTodosPanelVisible() { | 37 | @computed get isTodosPanelVisible() { |
33 | if (delayAppState.isDelayAppScreenVisible) return false; | ||
34 | if (this.settings.isTodosPanelVisible === undefined) return DEFAULT_TODOS_VISIBLE; | 38 | if (this.settings.isTodosPanelVisible === undefined) return DEFAULT_TODOS_VISIBLE; |
35 | |||
36 | return this.settings.isTodosPanelVisible; | 39 | return this.settings.isTodosPanelVisible; |
37 | } | 40 | } |
38 | 41 | ||
@@ -61,6 +64,7 @@ export default class TodoStore extends FeatureStore { | |||
61 | 64 | ||
62 | this._allReactions = createReactions([ | 65 | this._allReactions = createReactions([ |
63 | this._setFeatureEnabledReaction, | 66 | this._setFeatureEnabledReaction, |
67 | this._updateTodosConfig, | ||
64 | this._firstLaunchReaction, | 68 | this._firstLaunchReaction, |
65 | ]); | 69 | ]); |
66 | 70 | ||
@@ -124,11 +128,16 @@ export default class TodoStore extends FeatureStore { | |||
124 | // Todos client message handlers | 128 | // Todos client message handlers |
125 | 129 | ||
126 | _onTodosClientInitialized = () => { | 130 | _onTodosClientInitialized = () => { |
131 | const { authToken } = this.stores.user; | ||
132 | const { isDarkThemeActive } = this.stores.ui; | ||
133 | const { locale } = this.stores.app; | ||
134 | if (!this.webview) return; | ||
127 | this.webview.send(IPC.TODOS_HOST_CHANNEL, { | 135 | this.webview.send(IPC.TODOS_HOST_CHANNEL, { |
128 | action: 'todos:configure', | 136 | action: 'todos:configure', |
129 | data: { | 137 | data: { |
130 | authToken: this.stores.user.authToken, | 138 | authToken, |
131 | theme: this.stores.ui.isDarkThemeActive ? ThemeType.dark : ThemeType.default, | 139 | locale, |
140 | theme: isDarkThemeActive ? ThemeType.dark : ThemeType.default, | ||
132 | }, | 141 | }, |
133 | }); | 142 | }); |
134 | }; | 143 | }; |
@@ -148,6 +157,11 @@ export default class TodoStore extends FeatureStore { | |||
148 | this.isFeatureEnabled = isTodosEnabled; | 157 | this.isFeatureEnabled = isTodosEnabled; |
149 | }; | 158 | }; |
150 | 159 | ||
160 | _updateTodosConfig = () => { | ||
161 | // Resend the config if any part changes in Franz: | ||
162 | this._onTodosClientInitialized(); | ||
163 | }; | ||
164 | |||
151 | _firstLaunchReaction = () => { | 165 | _firstLaunchReaction = () => { |
152 | const { stats } = this.stores.settings.all; | 166 | const { stats } = this.stores.settings.all; |
153 | 167 | ||
diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js index 6e6f745c7..b652098f9 100644 --- a/src/stores/UserStore.js +++ b/src/stores/UserStore.js | |||
@@ -161,11 +161,11 @@ export default class UserStore extends Store { | |||
161 | } | 161 | } |
162 | 162 | ||
163 | @computed get isPremiumOverride() { | 163 | @computed get isPremiumOverride() { |
164 | return (!this.team.plan && this.isPremium) || (this.team.state === 'expired' && this.isPremium); | 164 | return ((!this.team || !this.team.plan) && this.isPremium) || (this.team.state === 'expired' && this.isPremium); |
165 | } | 165 | } |
166 | 166 | ||
167 | @computed get isPersonal() { | 167 | @computed get isPersonal() { |
168 | if (!this.team.plan) return false; | 168 | if (!this.team || !this.team.plan) return false; |
169 | const plan = getPlan(this.team.plan); | 169 | const plan = getPlan(this.team.plan); |
170 | 170 | ||
171 | return plan === PLANS.PERSONAL; | 171 | return plan === PLANS.PERSONAL; |
@@ -174,7 +174,7 @@ export default class UserStore extends Store { | |||
174 | @computed get isPro() { | 174 | @computed get isPro() { |
175 | if (this.isPremiumOverride) return true; | 175 | if (this.isPremiumOverride) return true; |
176 | 176 | ||
177 | if ((!this.team.plan || this.team.state === 'expired')) return false; | 177 | if (!this.team || (!this.team.plan || this.team.state === 'expired')) return false; |
178 | const plan = getPlan(this.team.plan); | 178 | const plan = getPlan(this.team.plan); |
179 | 179 | ||
180 | return plan === PLANS.PRO || plan === PLANS.LEGACY; | 180 | return plan === PLANS.PRO || plan === PLANS.LEGACY; |