diff options
author | Amine El Mouafik <412895+kytwb@users.noreply.github.com> | 2021-02-08 10:34:45 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-08 10:34:45 +0100 |
commit | 035002ceedf78d5ec73eabc0df7f06139939b967 (patch) | |
tree | 1c0d1e9531bae05fb65d70b9ea25baf404b74fe1 /src/components | |
parent | docs: add k0staa as a contributor (#1193) (diff) | |
download | ferdium-app-035002ceedf78d5ec73eabc0df7f06139939b967.tar.gz ferdium-app-035002ceedf78d5ec73eabc0df7f06139939b967.tar.zst ferdium-app-035002ceedf78d5ec73eabc0df7f06139939b967.zip |
Synchronize with Franz 5.6.0 (#1033)
Co-authored-by: FranzBot <i18n@meetfranz.com>
Co-authored-by: vantezzen <hello@vantezzen.io>
Co-authored-by: Makazzz <makazzzpro@live.ca>
Co-authored-by: Stefan Malzner <stefan@adlk.io>
Co-authored-by: Amine Mouafik <amine@mouafik.fr>
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/auth/Pricing.js | 29 | ||||
-rw-r--r-- | src/components/auth/SetupAssistant.js | 319 | ||||
-rw-r--r-- | src/components/layout/Sidebar.js | 3 | ||||
-rw-r--r-- | src/components/services/content/ServiceView.js | 73 | ||||
-rw-r--r-- | src/components/services/content/ServiceWebview.js | 5 | ||||
-rw-r--r-- | src/components/services/content/Services.js | 6 | ||||
-rw-r--r-- | src/components/services/tabs/TabItem.js | 87 | ||||
-rw-r--r-- | src/components/settings/services/EditServiceForm.js | 13 | ||||
-rw-r--r-- | src/components/settings/settings/EditSettingsForm.js | 14 | ||||
-rw-r--r-- | src/components/ui/FeatureList.js | 2 | ||||
-rw-r--r-- | src/components/ui/Modal/index.js | 4 |
11 files changed, 461 insertions, 94 deletions
diff --git a/src/components/auth/Pricing.js b/src/components/auth/Pricing.js index 593cb9c4b..4f5a76c8a 100644 --- a/src/components/auth/Pricing.js +++ b/src/components/auth/Pricing.js | |||
@@ -67,6 +67,15 @@ const messages = defineMessages({ | |||
67 | }); | 67 | }); |
68 | 68 | ||
69 | const styles = theme => ({ | 69 | const styles = theme => ({ |
70 | root: { | ||
71 | width: '500px !important', | ||
72 | textAlign: 'center', | ||
73 | padding: 20, | ||
74 | zIndex: 100, | ||
75 | |||
76 | '& h1': { | ||
77 | }, | ||
78 | }, | ||
70 | container: { | 79 | container: { |
71 | position: 'relative', | 80 | position: 'relative', |
72 | marginLeft: -150, | 81 | marginLeft: -150, |
@@ -86,8 +95,8 @@ const styles = theme => ({ | |||
86 | featureContainer: { | 95 | featureContainer: { |
87 | width: 300, | 96 | width: 300, |
88 | position: 'absolute', | 97 | position: 'absolute', |
89 | left: 'calc(100% / 2 + 225px)', | 98 | left: 'calc(100% / 2 + 250px)', |
90 | top: 155, | 99 | marginTop: 20, |
91 | background: theme.signup.pricing.feature.background, | 100 | background: theme.signup.pricing.feature.background, |
92 | height: 'auto', | 101 | height: 'auto', |
93 | padding: 20, | 102 | padding: 20, |
@@ -174,8 +183,8 @@ export default @injectSheet(styles) @observer class Signup extends Component { | |||
174 | const [intPart, fractionPart] = (price).toString().split('.'); | 183 | const [intPart, fractionPart] = (price).toString().split('.'); |
175 | 184 | ||
176 | return ( | 185 | return ( |
177 | <div className={classnames('auth__scroll-container', classes.container)}> | 186 | <> |
178 | <div className={classnames('auth__container', 'auth__container--signup', classes.content)}> | 187 | <div className={classnames('auth__container', classes.root, classes.container)}> |
179 | <form className="franz-form auth__form"> | 188 | <form className="franz-form auth__form"> |
180 | {isLoadingRequiredData ? <Loader /> : ( | 189 | {isLoadingRequiredData ? <Loader /> : ( |
181 | <img | 190 | <img |
@@ -212,7 +221,7 @@ export default @injectSheet(styles) @observer class Signup extends Component { | |||
212 | <p className={classnames(classes.price, classes.trialPrice)}> | 221 | <p className={classnames(classes.price, classes.trialPrice)}> |
213 | <span className={classes.figure}> | 222 | <span className={classes.figure}> |
214 | {currency} | 223 | {currency} |
215 | 0 | 224 | 0 |
216 | </span> | 225 | </span> |
217 | <sup>00</sup> | 226 | <sup>00</sup> |
218 | </p> | 227 | </p> |
@@ -234,7 +243,7 @@ export default @injectSheet(styles) @observer class Signup extends Component { | |||
234 | </ul> | 243 | </ul> |
235 | </div> | 244 | </div> |
236 | {trialActivationError && ( | 245 | {trialActivationError && ( |
237 | <p className={classes.error}>{intl.formatMessage(messages.activationError)}</p> | 246 | <p className={classes.error}>{intl.formatMessage(messages.activationError)}</p> |
238 | )} | 247 | )} |
239 | <Button | 248 | <Button |
240 | label={intl.formatMessage(!canSkipTrial ? messages.ctaStart : messages.ctaAccept)} | 249 | label={intl.formatMessage(!canSkipTrial ? messages.ctaStart : messages.ctaAccept)} |
@@ -244,9 +253,9 @@ export default @injectSheet(styles) @observer class Signup extends Component { | |||
244 | disabled={isLoadingRequiredData || isActivatingTrial} | 253 | disabled={isLoadingRequiredData || isActivatingTrial} |
245 | /> | 254 | /> |
246 | {canSkipTrial && ( | 255 | {canSkipTrial && ( |
247 | <p className={classes.skipLink}> | 256 | <p className={classes.skipLink}> |
248 | <a href="#/">{intl.formatMessage(messages.ctaSkip)}</a> | 257 | <a href="#/">{intl.formatMessage(messages.ctaSkip)}</a> |
249 | </p> | 258 | </p> |
250 | )} | 259 | )} |
251 | </form> | 260 | </form> |
252 | </div> | 261 | </div> |
@@ -256,7 +265,7 @@ export default @injectSheet(styles) @observer class Signup extends Component { | |||
256 | </H2> | 265 | </H2> |
257 | <FeatureList /> | 266 | <FeatureList /> |
258 | </div> | 267 | </div> |
259 | </div> | 268 | </> |
260 | ); | 269 | ); |
261 | } | 270 | } |
262 | } | 271 | } |
diff --git a/src/components/auth/SetupAssistant.js b/src/components/auth/SetupAssistant.js new file mode 100644 index 000000000..e03cf9101 --- /dev/null +++ b/src/components/auth/SetupAssistant.js | |||
@@ -0,0 +1,319 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | import injectSheet from 'react-jss'; | ||
6 | import classnames from 'classnames'; | ||
7 | |||
8 | import { Input, Button } from '@meetfranz/forms'; | ||
9 | import { Badge } from '@meetfranz/ui'; | ||
10 | import Modal from '../ui/Modal'; | ||
11 | import Infobox from '../ui/Infobox'; | ||
12 | import Appear from '../ui/effects/Appear'; | ||
13 | |||
14 | import { CDN_URL } from '../../config'; | ||
15 | |||
16 | const SLACK_ID = 'slack'; | ||
17 | |||
18 | const messages = defineMessages({ | ||
19 | headline: { | ||
20 | id: 'setupAssistant.headline', | ||
21 | defaultMessage: '!!!Let\'s get started', | ||
22 | }, | ||
23 | subHeadline: { | ||
24 | id: 'setupAssistant.subheadline', | ||
25 | defaultMessage: '!!!Choose from our most used services and get back on top of your messaging now.', | ||
26 | }, | ||
27 | submitButtonLabel: { | ||
28 | id: 'setupAssistant.submit.label', | ||
29 | defaultMessage: '!!!Let\'s go', | ||
30 | }, | ||
31 | inviteSuccessInfo: { | ||
32 | id: 'invite.successInfo', | ||
33 | defaultMessage: '!!!Invitations sent successfully', | ||
34 | }, | ||
35 | }); | ||
36 | |||
37 | const styles = theme => ({ | ||
38 | root: { | ||
39 | width: '500px !important', | ||
40 | textAlign: 'center', | ||
41 | padding: 20, | ||
42 | |||
43 | '& h1': { | ||
44 | }, | ||
45 | }, | ||
46 | servicesGrid: { | ||
47 | display: 'flex', | ||
48 | flexWrap: 'wrap', | ||
49 | justifyContent: 'space-between', | ||
50 | }, | ||
51 | serviceContainer: { | ||
52 | background: theme.colorBackground, | ||
53 | position: 'relative', | ||
54 | width: '32%', | ||
55 | display: 'flex', | ||
56 | alignItems: 'center', | ||
57 | flexDirection: 'column', | ||
58 | justifyContent: 'center', | ||
59 | padding: 20, | ||
60 | borderRadius: theme.borderRadius, | ||
61 | marginBottom: 10, | ||
62 | opacity: 0.5, | ||
63 | transition: 'all 0.25s', | ||
64 | border: [3, 'solid', 'transparent'], | ||
65 | |||
66 | '& h2': { | ||
67 | margin: [10, 0, 0], | ||
68 | color: theme.colorText, | ||
69 | }, | ||
70 | |||
71 | '&:hover': { | ||
72 | border: [3, 'solid', theme.brandPrimary], | ||
73 | '& $serviceIcon': { | ||
74 | }, | ||
75 | }, | ||
76 | |||
77 | }, | ||
78 | selected: { | ||
79 | border: [3, 'solid', theme.brandPrimary], | ||
80 | background: `${theme.brandPrimary}47`, | ||
81 | opacity: 1, | ||
82 | }, | ||
83 | serviceIcon: { | ||
84 | width: 50, | ||
85 | transition: 'all 0.25s', | ||
86 | }, | ||
87 | |||
88 | slackModalContent: { | ||
89 | textAlign: 'center', | ||
90 | |||
91 | '& img': { | ||
92 | width: 50, | ||
93 | marginBottom: 20, | ||
94 | }, | ||
95 | }, | ||
96 | modalActionContainer: { | ||
97 | display: 'flex', | ||
98 | flexDirection: 'column', | ||
99 | justifyContent: 'center', | ||
100 | alignItems: 'center', | ||
101 | }, | ||
102 | ctaCancel: { | ||
103 | background: 'none !important', | ||
104 | }, | ||
105 | slackBadge: { | ||
106 | position: 'absolute', | ||
107 | bottom: 4, | ||
108 | height: 'auto', | ||
109 | padding: '0px 4px', | ||
110 | borderRadius: theme.borderRadiusSmall, | ||
111 | margin: 0, | ||
112 | display: 'flex', | ||
113 | overflow: 'hidden', | ||
114 | }, | ||
115 | clearSlackWorkspace: { | ||
116 | background: theme.inputPrefixColor, | ||
117 | marginLeft: 5, | ||
118 | height: '100%', | ||
119 | color: theme.colorText, | ||
120 | display: 'inline-flex', | ||
121 | justifyContent: 'center', | ||
122 | alignItems: 'center', | ||
123 | marginRight: -4, | ||
124 | padding: [0, 5], | ||
125 | }, | ||
126 | }); | ||
127 | |||
128 | @injectSheet(styles) @observer | ||
129 | class SetupAssistant extends Component { | ||
130 | static propTypes = { | ||
131 | classes: PropTypes.object.isRequired, | ||
132 | onSubmit: PropTypes.func.isRequired, | ||
133 | isInviteSuccessful: PropTypes.bool, | ||
134 | services: PropTypes.object.isRequired, | ||
135 | isSettingUpServices: PropTypes.bool.isRequired, | ||
136 | }; | ||
137 | |||
138 | static defaultProps = { | ||
139 | isInviteSuccessful: false, | ||
140 | }; | ||
141 | |||
142 | static contextTypes = { | ||
143 | intl: intlShape, | ||
144 | }; | ||
145 | |||
146 | state = { | ||
147 | services: [{ | ||
148 | id: 'whatsapp', | ||
149 | }, { | ||
150 | id: 'messenger', | ||
151 | }, { | ||
152 | id: 'gmail', | ||
153 | }], | ||
154 | isSlackModalOpen: false, | ||
155 | slackWorkspace: '', | ||
156 | }; | ||
157 | |||
158 | slackWorkspaceHandler() { | ||
159 | const { slackWorkspace = '', services } = this.state; | ||
160 | |||
161 | const sanitizedWorkspace = slackWorkspace.trim().replace(/^https?:\/\//, ''); | ||
162 | |||
163 | if (sanitizedWorkspace) { | ||
164 | const index = services.findIndex(s => s.id === SLACK_ID); | ||
165 | |||
166 | if (index === -1) { | ||
167 | const newServices = services; | ||
168 | newServices.push({ id: SLACK_ID, team: sanitizedWorkspace }); | ||
169 | this.setState({ services: newServices }); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | this.setState({ | ||
174 | isSlackModalOpen: false, | ||
175 | slackWorkspace: sanitizedWorkspace, | ||
176 | }); | ||
177 | } | ||
178 | |||
179 | render() { | ||
180 | const { intl } = this.context; | ||
181 | const { | ||
182 | classes, isInviteSuccessful, onSubmit, services, isSettingUpServices, | ||
183 | } = this.props; | ||
184 | const { isSlackModalOpen, slackWorkspace, services: addedServices } = this.state; | ||
185 | |||
186 | return ( | ||
187 | <div className={`auth__container ${classes.root}`}> | ||
188 | {this.state.showSuccessInfo && isInviteSuccessful && ( | ||
189 | <Appear> | ||
190 | <Infobox | ||
191 | type="success" | ||
192 | icon="checkbox-marked-circle-outline" | ||
193 | dismissable | ||
194 | > | ||
195 | {intl.formatMessage(messages.inviteSuccessInfo)} | ||
196 | </Infobox> | ||
197 | </Appear> | ||
198 | )} | ||
199 | |||
200 | <img | ||
201 | src="./assets/images/logo.svg" | ||
202 | className="auth__logo" | ||
203 | alt="" | ||
204 | /> | ||
205 | <h1> | ||
206 | {intl.formatMessage(messages.headline)} | ||
207 | </h1> | ||
208 | <h2> | ||
209 | {intl.formatMessage(messages.subHeadline)} | ||
210 | </h2> | ||
211 | <div className={classnames('grid', classes.servicesGrid)}> | ||
212 | {Object.keys(services).map((id) => { | ||
213 | const service = services[id]; | ||
214 | return ( | ||
215 | <button | ||
216 | className={classnames({ | ||
217 | [classes.serviceContainer]: true, | ||
218 | [classes.selected]: this.state.services.findIndex(s => s.id === id) !== -1, | ||
219 | })} | ||
220 | key={id} | ||
221 | onClick={() => { | ||
222 | const index = this.state.services.findIndex(s => s.id === id); | ||
223 | if (index === -1) { | ||
224 | if (id === SLACK_ID) { | ||
225 | this.setState({ isSlackModalOpen: true }); | ||
226 | } else { | ||
227 | addedServices.push({ id }); | ||
228 | } | ||
229 | } else { | ||
230 | addedServices.splice(index, 1); | ||
231 | if (id === SLACK_ID) { | ||
232 | this.setState({ | ||
233 | slackWorkspace: '', | ||
234 | }); | ||
235 | } | ||
236 | } | ||
237 | |||
238 | this.setState({ services: addedServices }); | ||
239 | }} | ||
240 | type="button" | ||
241 | > | ||
242 | <img | ||
243 | src={`${CDN_URL}/recipes/dist/${id}/src/icon.svg`} | ||
244 | className={classes.serviceIcon} | ||
245 | alt="" | ||
246 | /> | ||
247 | <h2> | ||
248 | {service.name} | ||
249 | </h2> | ||
250 | {id === SLACK_ID && slackWorkspace && ( | ||
251 | <Badge type="secondary" className={classes.slackBadge}> | ||
252 | {slackWorkspace} | ||
253 | <button | ||
254 | type="button" | ||
255 | className={classes.clearSlackWorkspace} | ||
256 | onClick={() => { | ||
257 | this.setState({ | ||
258 | slackWorkspace: '', | ||
259 | }); | ||
260 | }} | ||
261 | > | ||
262 | x | ||
263 | </button> | ||
264 | </Badge> | ||
265 | )} | ||
266 | </button> | ||
267 | ); | ||
268 | })} | ||
269 | </div> | ||
270 | <Modal | ||
271 | isOpen={isSlackModalOpen} | ||
272 | // isBlocking={false} | ||
273 | close={() => this.setState({ isSlackModalOpen: false })} | ||
274 | > | ||
275 | <div className={classes.slackModalContent}> | ||
276 | <img src={`${CDN_URL}/recipes/dist/slack/src/icon.svg`} alt="" /> | ||
277 | <h1>Create your first Slack workspace</h1> | ||
278 | <form onSubmit={(e) => { | ||
279 | e.preventDefault(); | ||
280 | this.slackWorkspaceHandler(); | ||
281 | }} | ||
282 | > | ||
283 | <Input | ||
284 | suffix=".slack.com" | ||
285 | placeholder="workspace-url" | ||
286 | onChange={e => this.setState({ slackWorkspace: e.target.value })} | ||
287 | value={slackWorkspace} | ||
288 | /> | ||
289 | <div className={classes.modalActionContainer}> | ||
290 | <Button | ||
291 | type="submit" | ||
292 | label="Save" | ||
293 | /> | ||
294 | <Button | ||
295 | type="link" | ||
296 | buttonType="secondary" | ||
297 | label="Cancel" | ||
298 | className={classes.ctaCancel} | ||
299 | onClick={() => this.setState({ slackWorkspace: '' })} | ||
300 | /> | ||
301 | </div> | ||
302 | </form> | ||
303 | </div> | ||
304 | </Modal> | ||
305 | <Button | ||
306 | type="button" | ||
307 | className="auth__button" | ||
308 | // disabled={!atLeastOneEmailAddress} | ||
309 | label={intl.formatMessage(messages.submitButtonLabel)} | ||
310 | onClick={() => onSubmit(this.state.services)} | ||
311 | busy={isSettingUpServices} | ||
312 | disabled={isSettingUpServices || addedServices.length === 0} | ||
313 | /> | ||
314 | </div> | ||
315 | ); | ||
316 | } | ||
317 | } | ||
318 | |||
319 | export default SetupAssistant; | ||
diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js index 90bbe86e9..a47e74db0 100644 --- a/src/components/layout/Sidebar.js +++ b/src/components/layout/Sidebar.js | |||
@@ -60,6 +60,7 @@ export default @inject('stores', 'actions') @observer class Sidebar extends Comp | |||
60 | isAppMuted: PropTypes.bool.isRequired, | 60 | isAppMuted: PropTypes.bool.isRequired, |
61 | isWorkspaceDrawerOpen: PropTypes.bool.isRequired, | 61 | isWorkspaceDrawerOpen: PropTypes.bool.isRequired, |
62 | toggleWorkspaceDrawer: PropTypes.func.isRequired, | 62 | toggleWorkspaceDrawer: PropTypes.func.isRequired, |
63 | isTodosServiceActive: PropTypes.bool.isRequired, | ||
63 | }; | 64 | }; |
64 | 65 | ||
65 | static contextTypes = { | 66 | static contextTypes = { |
@@ -96,6 +97,7 @@ export default @inject('stores', 'actions') @observer class Sidebar extends Comp | |||
96 | toggleWorkspaceDrawer, | 97 | toggleWorkspaceDrawer, |
97 | stores, | 98 | stores, |
98 | actions, | 99 | actions, |
100 | isTodosServiceActive, | ||
99 | } = this.props; | 101 | } = this.props; |
100 | const { intl } = this.context; | 102 | const { intl } = this.context; |
101 | const todosToggleMessage = ( | 103 | const todosToggleMessage = ( |
@@ -140,6 +142,7 @@ export default @inject('stores', 'actions') @observer class Sidebar extends Comp | |||
140 | todoActions.toggleTodosPanel(); | 142 | todoActions.toggleTodosPanel(); |
141 | this.updateToolTip(); | 143 | this.updateToolTip(); |
142 | }} | 144 | }} |
145 | disblaed={isTodosServiceActive} | ||
143 | className={`sidebar__button sidebar__button--todos ${todosStore.isTodosPanelVisible ? 'is-active' : ''}`} | 146 | className={`sidebar__button sidebar__button--todos ${todosStore.isTodosPanelVisible ? 'is-active' : ''}`} |
144 | data-tip={`${intl.formatMessage(todosToggleMessage)} (${ctrlKey}+T)`} | 147 | data-tip={`${intl.formatMessage(todosToggleMessage)} (${ctrlKey}+T)`} |
145 | > | 148 | > |
diff --git a/src/components/services/content/ServiceView.js b/src/components/services/content/ServiceView.js index d91016c71..444d5fea4 100644 --- a/src/components/services/content/ServiceView.js +++ b/src/components/services/content/ServiceView.js | |||
@@ -1,6 +1,6 @@ | |||
1 | import React, { Component, Fragment } from 'react'; | 1 | import React, { Component, Fragment } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { autorun, reaction } from 'mobx'; | 3 | import { autorun } from 'mobx'; |
4 | import { observer, inject } from 'mobx-react'; | 4 | import { observer, inject } from 'mobx-react'; |
5 | import classnames from 'classnames'; | 5 | import classnames from 'classnames'; |
6 | 6 | ||
@@ -27,11 +27,7 @@ export default @inject('stores', 'actions') @observer class ServiceView extends | |||
27 | stores: PropTypes.shape({ | 27 | stores: PropTypes.shape({ |
28 | settings: PropTypes.instanceOf(SettingsStore).isRequired, | 28 | settings: PropTypes.instanceOf(SettingsStore).isRequired, |
29 | }).isRequired, | 29 | }).isRequired, |
30 | actions: PropTypes.shape({ | 30 | isSpellcheckerEnabled: PropTypes.bool.isRequired, |
31 | service: PropTypes.shape({ | ||
32 | setHibernation: PropTypes.func.isRequired, | ||
33 | }).isRequired, | ||
34 | }).isRequired, | ||
35 | }; | 31 | }; |
36 | 32 | ||
37 | static defaultProps = { | 33 | static defaultProps = { |
@@ -50,12 +46,6 @@ export default @inject('stores', 'actions') @observer class ServiceView extends | |||
50 | 46 | ||
51 | forceRepaintTimeout = null; | 47 | forceRepaintTimeout = null; |
52 | 48 | ||
53 | constructor(props) { | ||
54 | super(props); | ||
55 | |||
56 | this.startHibernationTimer = this.startHibernationTimer.bind(this); | ||
57 | } | ||
58 | |||
59 | componentDidMount() { | 49 | componentDidMount() { |
60 | this.autorunDisposer = autorun(() => { | 50 | this.autorunDisposer = autorun(() => { |
61 | if (this.props.service.isActive) { | 51 | if (this.props.service.isActive) { |
@@ -65,32 +55,6 @@ export default @inject('stores', 'actions') @observer class ServiceView extends | |||
65 | }, 100); | 55 | }, 100); |
66 | } | 56 | } |
67 | }); | 57 | }); |
68 | |||
69 | reaction( | ||
70 | () => this.props.service.isActive, | ||
71 | () => { | ||
72 | if (!this.props.service.isActive && this.props.stores.settings.all.app.hibernate) { | ||
73 | // Service is inactive - start hibernation countdown | ||
74 | this.startHibernationTimer(); | ||
75 | } else { | ||
76 | if (this.hibernationTimer) { | ||
77 | // Service is active but we have an active hibernation timer: Clear timeout | ||
78 | clearTimeout(this.hibernationTimer); | ||
79 | } | ||
80 | |||
81 | // Service is active, wake up service from hibernation | ||
82 | this.props.actions.service.setHibernation({ | ||
83 | serviceId: this.props.service.id, | ||
84 | hibernating: false, | ||
85 | }); | ||
86 | } | ||
87 | }, | ||
88 | ); | ||
89 | |||
90 | // Start hibernation counter if we are in background | ||
91 | if (!this.props.service.isActive && this.props.stores.settings.all.app.hibernate) { | ||
92 | this.startHibernationTimer(); | ||
93 | } | ||
94 | } | 58 | } |
95 | 59 | ||
96 | componentWillUnmount() { | 60 | componentWillUnmount() { |
@@ -110,19 +74,6 @@ export default @inject('stores', 'actions') @observer class ServiceView extends | |||
110 | }); | 74 | }); |
111 | }; | 75 | }; |
112 | 76 | ||
113 | startHibernationTimer() { | ||
114 | const timerDuration = (Number(this.props.stores.settings.all.app.hibernationStrategy) || 300) * 1000; | ||
115 | |||
116 | const hibernationTimer = setTimeout(() => { | ||
117 | this.props.actions.service.setHibernation({ | ||
118 | serviceId: this.props.service.id, | ||
119 | hibernating: true, | ||
120 | }); | ||
121 | }, timerDuration); | ||
122 | |||
123 | this.hibernationTimer = hibernationTimer; | ||
124 | } | ||
125 | |||
126 | render() { | 77 | render() { |
127 | const { | 78 | const { |
128 | detachService, | 79 | detachService, |
@@ -132,6 +83,7 @@ export default @inject('stores', 'actions') @observer class ServiceView extends | |||
132 | edit, | 83 | edit, |
133 | enable, | 84 | enable, |
134 | stores, | 85 | stores, |
86 | isSpellcheckerEnabled, | ||
135 | } = this.props; | 87 | } = this.props; |
136 | 88 | ||
137 | const { | 89 | const { |
@@ -193,22 +145,19 @@ export default @inject('stores', 'actions') @observer class ServiceView extends | |||
193 | </Fragment> | 145 | </Fragment> |
194 | ) : ( | 146 | ) : ( |
195 | <> | 147 | <> |
196 | {(!service.isHibernating || service.disableHibernation) ? ( | 148 | {(!service.isHibernating || service.isHibernationEnabled) ? ( |
197 | <> | 149 | <> |
198 | {showNavBar && ( | 150 | {showNavBar && ( |
199 | <WebControlsScreen service={service} /> | 151 | <WebControlsScreen service={service} /> |
200 | )} | 152 | )} |
201 | <ServiceWebview | 153 | {!service.isHibernating && ( |
202 | service={service} | 154 | <ServiceWebview |
203 | setWebviewReference={setWebviewReference} | 155 | service={service} |
204 | detachService={detachService} | 156 | setWebviewReference={setWebviewReference} |
205 | /> | 157 | detachService={detachService} |
206 | {/* {service.lostRecipeConnection && ( | 158 | isSpellcheckerEnabled={isSpellcheckerEnabled} |
207 | <ConnectionLostBanner | ||
208 | name={service.name} | ||
209 | reload={reload} | ||
210 | /> | 159 | /> |
211 | )} */} | 160 | )} |
212 | </> | 161 | </> |
213 | ) : ( | 162 | ) : ( |
214 | <div> | 163 | <div> |
diff --git a/src/components/services/content/ServiceWebview.js b/src/components/services/content/ServiceWebview.js index 2e3354279..4edbde5e2 100644 --- a/src/components/services/content/ServiceWebview.js +++ b/src/components/services/content/ServiceWebview.js | |||
@@ -15,6 +15,7 @@ class ServiceWebview extends Component { | |||
15 | service: PropTypes.instanceOf(ServiceModel).isRequired, | 15 | service: PropTypes.instanceOf(ServiceModel).isRequired, |
16 | setWebviewReference: PropTypes.func.isRequired, | 16 | setWebviewReference: PropTypes.func.isRequired, |
17 | detachService: PropTypes.func.isRequired, | 17 | detachService: PropTypes.func.isRequired, |
18 | isSpellcheckerEnabled: PropTypes.bool.isRequired, | ||
18 | }; | 19 | }; |
19 | 20 | ||
20 | @observable webview = null; | 21 | @observable webview = null; |
@@ -55,6 +56,7 @@ class ServiceWebview extends Component { | |||
55 | const { | 56 | const { |
56 | service, | 57 | service, |
57 | setWebviewReference, | 58 | setWebviewReference, |
59 | isSpellcheckerEnabled, | ||
58 | } = this.props; | 60 | } = this.props; |
59 | 61 | ||
60 | const preloadScript = path.join(__dirname, '../../../', 'webview', 'recipe.js'); | 62 | const preloadScript = path.join(__dirname, '../../../', 'webview', 'recipe.js'); |
@@ -70,7 +72,7 @@ class ServiceWebview extends Component { | |||
70 | autosize | 72 | autosize |
71 | src={service.url} | 73 | src={service.url} |
72 | preload={preloadScript} | 74 | preload={preloadScript} |
73 | partition={`persist:service-${service.id}`} | 75 | partition={service.partition} |
74 | onDidAttach={() => { | 76 | onDidAttach={() => { |
75 | setWebviewReference({ | 77 | setWebviewReference({ |
76 | serviceId: service.id, | 78 | serviceId: service.id, |
@@ -81,6 +83,7 @@ class ServiceWebview extends Component { | |||
81 | useragent={service.userAgent} | 83 | useragent={service.userAgent} |
82 | disablewebsecurity={service.recipe.disablewebsecurity ? true : undefined} | 84 | disablewebsecurity={service.recipe.disablewebsecurity ? true : undefined} |
83 | allowpopups | 85 | allowpopups |
86 | webpreferences={`spellcheck=${isSpellcheckerEnabled ? 1 : 0}`} | ||
84 | /> | 87 | /> |
85 | ); | 88 | ); |
86 | } | 89 | } |
diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js index da2ee0b9e..f679eeed0 100644 --- a/src/components/services/content/Services.js +++ b/src/components/services/content/Services.js | |||
@@ -10,6 +10,7 @@ import injectSheet from 'react-jss'; | |||
10 | import ServiceView from './ServiceView'; | 10 | import ServiceView from './ServiceView'; |
11 | import Appear from '../../ui/effects/Appear'; | 11 | import Appear from '../../ui/effects/Appear'; |
12 | import serverlessLogin from '../../../helpers/serverless-helpers'; | 12 | import serverlessLogin from '../../../helpers/serverless-helpers'; |
13 | import { TODOS_RECIPE_ID } from '../../../features/todos'; | ||
13 | 14 | ||
14 | const messages = defineMessages({ | 15 | const messages = defineMessages({ |
15 | welcome: { | 16 | welcome: { |
@@ -58,6 +59,7 @@ export default @injectSheet(styles) @inject('actions') @observer class Services | |||
58 | hasActivatedTrial: PropTypes.bool.isRequired, | 59 | hasActivatedTrial: PropTypes.bool.isRequired, |
59 | classes: PropTypes.object.isRequired, | 60 | classes: PropTypes.object.isRequired, |
60 | actions: PropTypes.object.isRequired, | 61 | actions: PropTypes.object.isRequired, |
62 | isSpellcheckerEnabled: PropTypes.bool.isRequired, | ||
61 | }; | 63 | }; |
62 | 64 | ||
63 | static defaultProps = { | 65 | static defaultProps = { |
@@ -111,6 +113,7 @@ export default @injectSheet(styles) @inject('actions') @observer class Services | |||
111 | userHasCompletedSignup, | 113 | userHasCompletedSignup, |
112 | hasActivatedTrial, | 114 | hasActivatedTrial, |
113 | classes, | 115 | classes, |
116 | isSpellcheckerEnabled, | ||
114 | } = this.props; | 117 | } = this.props; |
115 | 118 | ||
116 | const { | 119 | const { |
@@ -168,7 +171,7 @@ export default @injectSheet(styles) @inject('actions') @observer class Services | |||
168 | </div> | 171 | </div> |
169 | </Appear> | 172 | </Appear> |
170 | )} | 173 | )} |
171 | {services.map(service => ( | 174 | {services.filter(service => service.recipe.id !== TODOS_RECIPE_ID).map(service => ( |
172 | <ServiceView | 175 | <ServiceView |
173 | key={service.id} | 176 | key={service.id} |
174 | service={service} | 177 | service={service} |
@@ -186,6 +189,7 @@ export default @injectSheet(styles) @inject('actions') @observer class Services | |||
186 | redirect: false, | 189 | redirect: false, |
187 | })} | 190 | })} |
188 | upgrade={() => openSettings({ path: 'user' })} | 191 | upgrade={() => openSettings({ path: 'user' })} |
192 | isSpellcheckerEnabled={isSpellcheckerEnabled} | ||
189 | /> | 193 | /> |
190 | ))} | 194 | ))} |
191 | </div> | 195 | </div> |
diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js index efa5fa60c..479f151a6 100644 --- a/src/components/services/tabs/TabItem.js +++ b/src/components/services/tabs/TabItem.js | |||
@@ -5,9 +5,14 @@ import PropTypes from 'prop-types'; | |||
5 | import { observer } from 'mobx-react'; | 5 | import { observer } from 'mobx-react'; |
6 | import classnames from 'classnames'; | 6 | import classnames from 'classnames'; |
7 | import { SortableElement } from 'react-sortable-hoc'; | 7 | import { SortableElement } from 'react-sortable-hoc'; |
8 | import injectSheet from 'react-jss'; | ||
9 | import ms from 'ms'; | ||
8 | 10 | ||
11 | import { observable, autorun } from 'mobx'; | ||
9 | import ServiceModel from '../../../models/Service'; | 12 | import ServiceModel from '../../../models/Service'; |
10 | import { ctrlKey } from '../../../environment'; | 13 | import { ctrlKey, cmdKey } from '../../../environment'; |
14 | |||
15 | const IS_SERVICE_DEBUGGING_ENABLED = (localStorage.getItem('debug') || '').includes('Franz:Service'); | ||
11 | 16 | ||
12 | const { Menu } = remote; | 17 | const { Menu } = remote; |
13 | 18 | ||
@@ -50,9 +55,38 @@ const messages = defineMessages({ | |||
50 | }, | 55 | }, |
51 | }); | 56 | }); |
52 | 57 | ||
53 | @observer | 58 | const styles = { |
54 | class TabItem extends Component { | 59 | pollIndicator: { |
60 | position: 'absolute', | ||
61 | bottom: 2, | ||
62 | width: 10, | ||
63 | height: 10, | ||
64 | borderRadius: 5, | ||
65 | background: 'gray', | ||
66 | transition: 'background 0.5s', | ||
67 | }, | ||
68 | pollIndicatorPoll: { | ||
69 | left: 2, | ||
70 | }, | ||
71 | pollIndicatorAnswer: { | ||
72 | left: 14, | ||
73 | }, | ||
74 | polled: { | ||
75 | background: 'yellow !important', | ||
76 | transition: 'background 0.1s', | ||
77 | }, | ||
78 | pollAnswered: { | ||
79 | background: 'green !important', | ||
80 | transition: 'background 0.1s', | ||
81 | }, | ||
82 | stale: { | ||
83 | background: 'red !important', | ||
84 | }, | ||
85 | }; | ||
86 | |||
87 | @injectSheet(styles) @observer class TabItem extends Component { | ||
55 | static propTypes = { | 88 | static propTypes = { |
89 | classes: PropTypes.object.isRequired, | ||
56 | service: PropTypes.instanceOf(ServiceModel).isRequired, | 90 | service: PropTypes.instanceOf(ServiceModel).isRequired, |
57 | clickHandler: PropTypes.func.isRequired, | 91 | clickHandler: PropTypes.func.isRequired, |
58 | shortcutIndex: PropTypes.number.isRequired, | 92 | shortcutIndex: PropTypes.number.isRequired, |
@@ -71,8 +105,33 @@ class TabItem extends Component { | |||
71 | intl: intlShape, | 105 | intl: intlShape, |
72 | }; | 106 | }; |
73 | 107 | ||
108 | @observable isPolled = false; | ||
109 | |||
110 | @observable isPollAnswered = false; | ||
111 | |||
112 | componentDidMount() { | ||
113 | const { service } = this.props; | ||
114 | |||
115 | if (IS_SERVICE_DEBUGGING_ENABLED) { | ||
116 | autorun(() => { | ||
117 | if (Date.now() - service.lastPoll < ms('0.2s')) { | ||
118 | this.isPolled = true; | ||
119 | |||
120 | setTimeout(() => { this.isPolled = false; }, ms('1s')); | ||
121 | } | ||
122 | |||
123 | if (Date.now() - service.lastPollAnswer < ms('0.2s')) { | ||
124 | this.isPollAnswered = true; | ||
125 | |||
126 | setTimeout(() => { this.isPollAnswered = false; }, ms('1s')); | ||
127 | } | ||
128 | }); | ||
129 | } | ||
130 | } | ||
131 | |||
74 | render() { | 132 | render() { |
75 | const { | 133 | const { |
134 | classes, | ||
76 | service, | 135 | service, |
77 | clickHandler, | 136 | clickHandler, |
78 | shortcutIndex, | 137 | shortcutIndex, |
@@ -97,6 +156,7 @@ class TabItem extends Component { | |||
97 | }, { | 156 | }, { |
98 | label: intl.formatMessage(messages.reload), | 157 | label: intl.formatMessage(messages.reload), |
99 | click: reload, | 158 | click: reload, |
159 | accelerator: `${cmdKey}+R`, | ||
100 | }, { | 160 | }, { |
101 | label: intl.formatMessage(messages.edit), | 161 | label: intl.formatMessage(messages.edit), |
102 | click: () => openSettings({ | 162 | click: () => openSettings({ |
@@ -141,7 +201,7 @@ class TabItem extends Component { | |||
141 | • | 201 | • |
142 | </span> | 202 | </span> |
143 | )} | 203 | )} |
144 | {service.isHibernating && !service.disableHibernation && ( | 204 | {service.isHibernating && !service.isHibernationEnabled && ( |
145 | <span className="tab-item__message-count hibernating"> | 205 | <span className="tab-item__message-count hibernating"> |
146 | • | 206 | • |
147 | </span> | 207 | </span> |
@@ -153,6 +213,7 @@ class TabItem extends Component { | |||
153 | return ( | 213 | return ( |
154 | <li | 214 | <li |
155 | className={classnames({ | 215 | className={classnames({ |
216 | [classes.stale]: IS_SERVICE_DEBUGGING_ENABLED && service.lostRecipeConnection, | ||
156 | 'tab-item': true, | 217 | 'tab-item': true, |
157 | 'is-active': service.isActive, | 218 | 'is-active': service.isActive, |
158 | 'has-custom-icon': service.hasCustomIcon, | 219 | 'has-custom-icon': service.hasCustomIcon, |
@@ -168,6 +229,24 @@ class TabItem extends Component { | |||
168 | alt="" | 229 | alt="" |
169 | /> | 230 | /> |
170 | {notificationBadge} | 231 | {notificationBadge} |
232 | {IS_SERVICE_DEBUGGING_ENABLED && ( | ||
233 | <> | ||
234 | <div | ||
235 | className={classnames({ | ||
236 | [classes.pollIndicator]: true, | ||
237 | [classes.pollIndicatorPoll]: true, | ||
238 | [classes.polled]: this.isPolled, | ||
239 | })} | ||
240 | /> | ||
241 | <div | ||
242 | className={classnames({ | ||
243 | [classes.pollIndicator]: true, | ||
244 | [classes.pollIndicatorAnswer]: true, | ||
245 | [classes.pollAnswered]: this.isPollAnswered, | ||
246 | })} | ||
247 | /> | ||
248 | </> | ||
249 | )} | ||
171 | </li> | 250 | </li> |
172 | ); | 251 | ); |
173 | } | 252 | } |
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js index f1e70ce59..80f60b3e1 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.js | |||
@@ -20,7 +20,6 @@ import Select from '../../ui/Select'; | |||
20 | import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer'; | 20 | import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer'; |
21 | import LimitReachedInfobox from '../../../features/serviceLimit/components/LimitReachedInfobox'; | 21 | import LimitReachedInfobox from '../../../features/serviceLimit/components/LimitReachedInfobox'; |
22 | import { serviceLimitStore } from '../../../features/serviceLimit'; | 22 | import { serviceLimitStore } from '../../../features/serviceLimit'; |
23 | |||
24 | import { isMac } from '../../../environment'; | 23 | import { isMac } from '../../../environment'; |
25 | 24 | ||
26 | const messages = defineMessages({ | 25 | const messages = defineMessages({ |
@@ -96,9 +95,9 @@ const messages = defineMessages({ | |||
96 | id: 'settings.service.form.isMutedInfo', | 95 | id: 'settings.service.form.isMutedInfo', |
97 | defaultMessage: '!!!When disabled, all notification sounds and audio playback are muted', | 96 | defaultMessage: '!!!When disabled, all notification sounds and audio playback are muted', |
98 | }, | 97 | }, |
99 | disableHibernationInfo: { | 98 | isHibernationEnabledInfo: { |
100 | id: 'settings.service.form.disableHibernationInfo', | 99 | id: 'settings.service.form.isHibernatedEnabledInfo', |
101 | defaultMessage: '!!!You currently have hibernation enabled but you can disable hibernation for individual services using this option.', | 100 | defaultMessage: '!!!When enabled, a service will be shut down after a period of time to save system resources.', |
102 | }, | 101 | }, |
103 | headlineNotifications: { | 102 | headlineNotifications: { |
104 | id: 'settings.service.form.headlineNotifications', | 103 | id: 'settings.service.form.headlineNotifications', |
@@ -375,9 +374,9 @@ export default @observer class EditServiceForm extends Component { | |||
375 | <Toggle field={form.$('isEnabled')} /> | 374 | <Toggle field={form.$('isEnabled')} /> |
376 | {isHibernationFeatureActive && ( | 375 | {isHibernationFeatureActive && ( |
377 | <> | 376 | <> |
378 | <Toggle field={form.$('disableHibernation')} /> | 377 | <Toggle field={form.$('isHibernationEnabled')} /> |
379 | <p className="settings__help"> | 378 | <p className="settings__help"> |
380 | {intl.formatMessage(messages.disableHibernationInfo)} | 379 | {intl.formatMessage(messages.isHibernationEnabledInfo)} |
381 | </p> | 380 | </p> |
382 | </> | 381 | </> |
383 | )} | 382 | )} |
@@ -409,7 +408,7 @@ export default @observer class EditServiceForm extends Component { | |||
409 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }} | 408 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }} |
410 | > | 409 | > |
411 | <div className="settings__settings-group"> | 410 | <div className="settings__settings-group"> |
412 | <Select field={form.$('spellcheckerLanguage')} multiple /> | 411 | <Select field={form.$('spellcheckerLanguage')} /> |
413 | </div> | 412 | </div> |
414 | </PremiumFeatureContainer> | 413 | </PremiumFeatureContainer> |
415 | )} | 414 | )} |
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index cd772214f..f20c38bc9 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js | |||
@@ -173,6 +173,8 @@ export default @observer class EditSettingsForm extends Component { | |||
173 | isAdaptableDarkModeEnabled: PropTypes.bool.isRequired, | 173 | isAdaptableDarkModeEnabled: PropTypes.bool.isRequired, |
174 | isNightlyEnabled: PropTypes.bool.isRequired, | 174 | isNightlyEnabled: PropTypes.bool.isRequired, |
175 | openProcessManager: PropTypes.func.isRequired, | 175 | openProcessManager: PropTypes.func.isRequired, |
176 | hasAddedTodosAsService: PropTypes.bool.isRequired, | ||
177 | isOnline: PropTypes.bool.isRequired, | ||
176 | }; | 178 | }; |
177 | 179 | ||
178 | static contextTypes = { | 180 | static contextTypes = { |
@@ -227,6 +229,8 @@ export default @observer class EditSettingsForm extends Component { | |||
227 | openProcessManager, | 229 | openProcessManager, |
228 | isTodosActivated, | 230 | isTodosActivated, |
229 | isNightlyEnabled, | 231 | isNightlyEnabled, |
232 | hasAddedTodosAsService, | ||
233 | isOnline, | ||
230 | } = this.props; | 234 | } = this.props; |
231 | const { intl } = this.context; | 235 | const { intl } = this.context; |
232 | 236 | ||
@@ -346,7 +350,7 @@ export default @observer class EditSettingsForm extends Component { | |||
346 | 350 | ||
347 | <Hr /> | 351 | <Hr /> |
348 | 352 | ||
349 | {isTodosEnabled && ( | 353 | {isTodosEnabled && !hasAddedTodosAsService && ( |
350 | <> | 354 | <> |
351 | <Toggle field={form.$('enableTodos')} /> | 355 | <Toggle field={form.$('enableTodos')} /> |
352 | {isTodosActivated && ( | 356 | {isTodosActivated && ( |
@@ -535,10 +539,10 @@ export default @observer class EditSettingsForm extends Component { | |||
535 | <Toggle | 539 | <Toggle |
536 | field={form.$('enableSpellchecking')} | 540 | field={form.$('enableSpellchecking')} |
537 | /> | 541 | /> |
538 | {form.$('enableSpellchecking').value && !isMac && ( | 542 | {!isMac && form.$('enableSpellchecking').value && ( |
539 | <Select field={form.$('spellcheckerLanguage')} multiple /> | 543 | <Select field={form.$('spellcheckerLanguage')} /> |
540 | )} | 544 | )} |
541 | {form.$('enableSpellchecking').value && isMac && ( | 545 | {isMac && form.$('enableSpellchecking').value && ( |
542 | <p>{intl.formatMessage(messages.spellCheckerLanguageInfo)}</p> | 546 | <p>{intl.formatMessage(messages.spellCheckerLanguageInfo)}</p> |
543 | )} | 547 | )} |
544 | </Fragment> | 548 | </Fragment> |
@@ -625,7 +629,7 @@ export default @observer class EditSettingsForm extends Component { | |||
625 | buttonType="secondary" | 629 | buttonType="secondary" |
626 | label={intl.formatMessage(updateButtonLabelMessage)} | 630 | label={intl.formatMessage(updateButtonLabelMessage)} |
627 | onClick={checkForUpdates} | 631 | onClick={checkForUpdates} |
628 | disabled={!automaticUpdates || isCheckingForUpdates || isUpdateAvailable} | 632 | disabled={!automaticUpdates || isCheckingForUpdates || isUpdateAvailable || !isOnline} |
629 | loaded={!isCheckingForUpdates || !isUpdateAvailable} | 633 | loaded={!isCheckingForUpdates || !isUpdateAvailable} |
630 | /> | 634 | /> |
631 | )} | 635 | )} |
diff --git a/src/components/ui/FeatureList.js b/src/components/ui/FeatureList.js index dbc2a9078..72c799819 100644 --- a/src/components/ui/FeatureList.js +++ b/src/components/ui/FeatureList.js | |||
@@ -76,7 +76,7 @@ export class FeatureList extends Component { | |||
76 | static propTypes = { | 76 | static propTypes = { |
77 | className: PropTypes.string, | 77 | className: PropTypes.string, |
78 | featureClassName: PropTypes.string, | 78 | featureClassName: PropTypes.string, |
79 | plan: PropTypes.oneOf(Object.values(PLANS)), | 79 | plan: PropTypes.oneOf(Object.keys(PLANS)), |
80 | }; | 80 | }; |
81 | 81 | ||
82 | static defaultProps = { | 82 | static defaultProps = { |
diff --git a/src/components/ui/Modal/index.js b/src/components/ui/Modal/index.js index 0af521452..a9fa0cd1b 100644 --- a/src/components/ui/Modal/index.js +++ b/src/components/ui/Modal/index.js | |||
@@ -41,8 +41,6 @@ export default @injectCSS(styles) class Modal extends Component { | |||
41 | showClose, | 41 | showClose, |
42 | } = this.props; | 42 | } = this.props; |
43 | 43 | ||
44 | const appRoot = document.getElementById('root'); | ||
45 | |||
46 | return ( | 44 | return ( |
47 | <ReactModal | 45 | <ReactModal |
48 | isOpen={isOpen} | 46 | isOpen={isOpen} |
@@ -55,7 +53,7 @@ export default @injectCSS(styles) class Modal extends Component { | |||
55 | portal={portal} | 53 | portal={portal} |
56 | onRequestClose={close} | 54 | onRequestClose={close} |
57 | shouldCloseOnOverlayClick={shouldCloseOnOverlayClick} | 55 | shouldCloseOnOverlayClick={shouldCloseOnOverlayClick} |
58 | appElement={appRoot} | 56 | appElement={document.getElementById('root')} |
59 | > | 57 | > |
60 | {showClose && close && ( | 58 | {showClose && close && ( |
61 | <button | 59 | <button |