diff options
author | Stefan Malzner <stefan@adlk.io> | 2018-01-08 10:34:00 +0100 |
---|---|---|
committer | Stefan Malzner <stefan@adlk.io> | 2018-01-08 10:34:00 +0100 |
commit | 91540e15eb2484a097587a38440b05897bb87638 (patch) | |
tree | dba412401da044ba203fcacb56018bfd7ed193eb /src/components | |
parent | Sort languages by name (diff) | |
parent | Add color to service icons (diff) | |
download | ferdium-app-91540e15eb2484a097587a38440b05897bb87638.tar.gz ferdium-app-91540e15eb2484a097587a38440b05897bb87638.tar.zst ferdium-app-91540e15eb2484a097587a38440b05897bb87638.zip |
Merge branch 'develop' into i18n
# Conflicts:
# src/i18n/languages.js
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/services/content/ServiceWebview.js | 2 | ||||
-rw-r--r-- | src/components/services/tabs/TabItem.js | 2 | ||||
-rw-r--r-- | src/components/settings/recipes/RecipesDashboard.js | 20 | ||||
-rw-r--r-- | src/components/settings/services/EditServiceForm.js | 100 | ||||
-rw-r--r-- | src/components/settings/services/ServicesDashboard.js | 42 | ||||
-rw-r--r-- | src/components/settings/settings/EditSettingsForm.js | 43 | ||||
-rw-r--r-- | src/components/ui/ImageUpload.js | 108 | ||||
-rw-r--r-- | src/components/ui/SearchInput.js | 48 | ||||
-rw-r--r-- | src/components/ui/SubscriptionPopup.js | 1 |
9 files changed, 297 insertions, 69 deletions
diff --git a/src/components/services/content/ServiceWebview.js b/src/components/services/content/ServiceWebview.js index faa356d3d..c146abf4e 100644 --- a/src/components/services/content/ServiceWebview.js +++ b/src/components/services/content/ServiceWebview.js | |||
@@ -65,6 +65,7 @@ export default class ServiceWebview extends Component { | |||
65 | 65 | ||
66 | const webviewClasses = classnames({ | 66 | const webviewClasses = classnames({ |
67 | services__webview: true, | 67 | services__webview: true, |
68 | 'services__webview-wrapper': true, | ||
68 | 'is-active': service.isActive, | 69 | 'is-active': service.isActive, |
69 | 'services__webview--force-repaint': this.state.forceRepaint, | 70 | 'services__webview--force-repaint': this.state.forceRepaint, |
70 | }); | 71 | }); |
@@ -105,7 +106,6 @@ export default class ServiceWebview extends Component { | |||
105 | onUpdateTargetUrl={this.updateTargetUrl} | 106 | onUpdateTargetUrl={this.updateTargetUrl} |
106 | useragent={service.userAgent} | 107 | useragent={service.userAgent} |
107 | muted={isAppMuted || service.isMuted} | 108 | muted={isAppMuted || service.isMuted} |
108 | disablewebsecurity | ||
109 | allowpopups | 109 | allowpopups |
110 | /> | 110 | /> |
111 | )} | 111 | )} |
diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js index 8403d9462..7aed8fda7 100644 --- a/src/components/services/tabs/TabItem.js +++ b/src/components/services/tabs/TabItem.js | |||
@@ -126,7 +126,7 @@ class TabItem extends Component { | |||
126 | const menu = Menu.buildFromTemplate(menuTemplate); | 126 | const menu = Menu.buildFromTemplate(menuTemplate); |
127 | 127 | ||
128 | let notificationBadge = null; | 128 | let notificationBadge = null; |
129 | if ((showMessageBadgeWhenMutedSetting || service.isNotificationEnabled) && showMessageBadgesEvenWhenMuted) { | 129 | if ((showMessageBadgeWhenMutedSetting || service.isNotificationEnabled) && showMessageBadgesEvenWhenMuted && service.isBadgeEnabled) { |
130 | notificationBadge = ( | 130 | notificationBadge = ( |
131 | <span> | 131 | <span> |
132 | {service.unreadDirectMessageCount > 0 && ( | 132 | {service.unreadDirectMessageCount > 0 && ( |
diff --git a/src/components/settings/recipes/RecipesDashboard.js b/src/components/settings/recipes/RecipesDashboard.js index b6ade5da4..4610c69a5 100644 --- a/src/components/settings/recipes/RecipesDashboard.js +++ b/src/components/settings/recipes/RecipesDashboard.js | |||
@@ -16,6 +16,10 @@ const messages = defineMessages({ | |||
16 | id: 'settings.recipes.headline', | 16 | id: 'settings.recipes.headline', |
17 | defaultMessage: '!!!Available Services', | 17 | defaultMessage: '!!!Available Services', |
18 | }, | 18 | }, |
19 | searchService: { | ||
20 | id: 'settings.searchService', | ||
21 | defaultMessage: '!!!Search service', | ||
22 | }, | ||
19 | mostPopularRecipes: { | 23 | mostPopularRecipes: { |
20 | id: 'settings.recipes.mostPopular', | 24 | id: 'settings.recipes.mostPopular', |
21 | defaultMessage: '!!!Most popular', | 25 | defaultMessage: '!!!Most popular', |
@@ -81,13 +85,7 @@ export default class RecipesDashboard extends Component { | |||
81 | return ( | 85 | return ( |
82 | <div className="settings__main"> | 86 | <div className="settings__main"> |
83 | <div className="settings__header"> | 87 | <div className="settings__header"> |
84 | <SearchInput | 88 | <h1>{intl.formatMessage(messages.headline)}</h1> |
85 | className="settings__search-header" | ||
86 | defaultValue={intl.formatMessage(messages.headline)} | ||
87 | onChange={e => searchRecipes(e)} | ||
88 | onReset={() => resetSearch()} | ||
89 | throttle | ||
90 | /> | ||
91 | </div> | 89 | </div> |
92 | <div className="settings__body recipes"> | 90 | <div className="settings__body recipes"> |
93 | {serviceStatus.length > 0 && serviceStatus.includes('created') && ( | 91 | {serviceStatus.length > 0 && serviceStatus.includes('created') && ( |
@@ -101,7 +99,13 @@ export default class RecipesDashboard extends Component { | |||
101 | </Infobox> | 99 | </Infobox> |
102 | </Appear> | 100 | </Appear> |
103 | )} | 101 | )} |
104 | {/* {!searchNeedle && ( */} | 102 | <SearchInput |
103 | placeholder={intl.formatMessage(messages.searchService)} | ||
104 | onChange={e => searchRecipes(e)} | ||
105 | onReset={() => resetSearch()} | ||
106 | autoFocus | ||
107 | throttle | ||
108 | /> | ||
105 | <div className="recipes__navigation"> | 109 | <div className="recipes__navigation"> |
106 | <Link | 110 | <Link |
107 | to="/settings/recipes" | 111 | to="/settings/recipes" |
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js index 36cefe87c..f6f2df2f3 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.js | |||
@@ -13,6 +13,7 @@ import Tabs, { TabItem } from '../../ui/Tabs'; | |||
13 | import Input from '../../ui/Input'; | 13 | import Input from '../../ui/Input'; |
14 | import Toggle from '../../ui/Toggle'; | 14 | import Toggle from '../../ui/Toggle'; |
15 | import Button from '../../ui/Button'; | 15 | import Button from '../../ui/Button'; |
16 | import ImageUpload from '../../ui/ImageUpload'; | ||
16 | 17 | ||
17 | const messages = defineMessages({ | 18 | const messages = defineMessages({ |
18 | saveService: { | 19 | saveService: { |
@@ -47,6 +48,10 @@ const messages = defineMessages({ | |||
47 | id: 'settings.service.form.tabOnPremise', | 48 | id: 'settings.service.form.tabOnPremise', |
48 | defaultMessage: '!!!Self hosted ⭐️', | 49 | defaultMessage: '!!!Self hosted ⭐️', |
49 | }, | 50 | }, |
51 | useHostedService: { | ||
52 | id: 'settings.service.form.useHostedService', | ||
53 | defaultMessage: '!!!Use the hosted {name} service.', | ||
54 | }, | ||
50 | customUrlValidationError: { | 55 | customUrlValidationError: { |
51 | id: 'settings.service.form.customUrlValidationError', | 56 | id: 'settings.service.form.customUrlValidationError', |
52 | defaultMessage: '!!!Could not validate custom {name} server.', | 57 | defaultMessage: '!!!Could not validate custom {name} server.', |
@@ -67,6 +72,26 @@ const messages = defineMessages({ | |||
67 | id: 'settings.service.form.isMutedInfo', | 72 | id: 'settings.service.form.isMutedInfo', |
68 | defaultMessage: '!!!When disabled, all notification sounds and audio playback are muted', | 73 | defaultMessage: '!!!When disabled, all notification sounds and audio playback are muted', |
69 | }, | 74 | }, |
75 | headlineNotifications: { | ||
76 | id: 'settings.service.form.headlineNotifications', | ||
77 | defaultMessage: '!!!Notifications', | ||
78 | }, | ||
79 | headlineBadges: { | ||
80 | id: 'settings.service.form.headlineBadges', | ||
81 | defaultMessage: '!!!Unread message badges', | ||
82 | }, | ||
83 | headlineGeneral: { | ||
84 | id: 'settings.service.form.headlineGeneral', | ||
85 | defaultMessage: '!!!General', | ||
86 | }, | ||
87 | iconDelete: { | ||
88 | id: 'settings.service.form.iconDelete', | ||
89 | defaultMessage: '!!!Delete', | ||
90 | }, | ||
91 | iconUpload: { | ||
92 | id: 'settings.service.form.iconUpload', | ||
93 | defaultMessage: '!!!Drop your image, or click here', | ||
94 | }, | ||
70 | }); | 95 | }); |
71 | 96 | ||
72 | @observer | 97 | @observer |
@@ -108,9 +133,13 @@ export default class EditServiceForm extends Component { | |||
108 | this.props.form.submit({ | 133 | this.props.form.submit({ |
109 | onSuccess: async (form) => { | 134 | onSuccess: async (form) => { |
110 | const values = form.values(); | 135 | const values = form.values(); |
111 | |||
112 | let isValid = true; | 136 | let isValid = true; |
113 | 137 | ||
138 | const files = form.$('customIcon').files; | ||
139 | if (files) { | ||
140 | values.iconFile = files[0]; | ||
141 | } | ||
142 | |||
114 | if (recipe.validateUrl && values.customUrl) { | 143 | if (recipe.validateUrl && values.customUrl) { |
115 | this.setState({ isValidatingCustomUrl: true }); | 144 | this.setState({ isValidatingCustomUrl: true }); |
116 | try { | 145 | try { |
@@ -166,6 +195,13 @@ export default class EditServiceForm extends Component { | |||
166 | /> | 195 | /> |
167 | ); | 196 | ); |
168 | 197 | ||
198 | let activeTabIndex = 0; | ||
199 | if (recipe.hasHostedOption && service.team) { | ||
200 | activeTabIndex = 1; | ||
201 | } else if (recipe.hasHostedOption && service.customUrl) { | ||
202 | activeTabIndex = 2; | ||
203 | } | ||
204 | |||
169 | return ( | 205 | return ( |
170 | <div className="settings__main"> | 206 | <div className="settings__main"> |
171 | <div className="settings__header"> | 207 | <div className="settings__header"> |
@@ -195,14 +231,25 @@ export default class EditServiceForm extends Component { | |||
195 | </div> | 231 | </div> |
196 | <div className="settings__body"> | 232 | <div className="settings__body"> |
197 | <form onSubmit={e => this.submit(e)} id="form"> | 233 | <form onSubmit={e => this.submit(e)} id="form"> |
198 | <Input field={form.$('name')} focus /> | 234 | <div className="service-name"> |
235 | <Input field={form.$('name')} focus /> | ||
236 | </div> | ||
199 | {(recipe.hasTeamId || recipe.hasCustomUrl) && ( | 237 | {(recipe.hasTeamId || recipe.hasCustomUrl) && ( |
200 | <Tabs | 238 | <Tabs |
201 | active={service.customUrl ? 1 : 0} | 239 | active={activeTabIndex} |
202 | > | 240 | > |
241 | {recipe.hasHostedOption && ( | ||
242 | <TabItem title={recipe.name}> | ||
243 | {intl.formatMessage(messages.useHostedService, { name: recipe.name })} | ||
244 | </TabItem> | ||
245 | )} | ||
203 | {recipe.hasTeamId && ( | 246 | {recipe.hasTeamId && ( |
204 | <TabItem title={intl.formatMessage(messages.tabHosted)}> | 247 | <TabItem title={intl.formatMessage(messages.tabHosted)}> |
205 | <Input field={form.$('team')} suffix={recipe.urlInputSuffix} /> | 248 | <Input |
249 | field={form.$('team')} | ||
250 | prefix={recipe.urlInputPrefix} | ||
251 | suffix={recipe.urlInputSuffix} | ||
252 | /> | ||
206 | </TabItem> | 253 | </TabItem> |
207 | )} | 254 | )} |
208 | {recipe.hasCustomUrl && ( | 255 | {recipe.hasCustomUrl && ( |
@@ -230,21 +277,42 @@ export default class EditServiceForm extends Component { | |||
230 | )} | 277 | )} |
231 | </Tabs> | 278 | </Tabs> |
232 | )} | 279 | )} |
233 | <div className="settings__options"> | 280 | <div className="service-flex-grid"> |
234 | <Toggle field={form.$('isNotificationEnabled')} /> | 281 | <div className="settings__options"> |
235 | {recipe.hasIndirectMessages && ( | 282 | <div className="settings__settings-group"> |
236 | <div> | 283 | <h3>{intl.formatMessage(messages.headlineNotifications)}</h3> |
237 | <Toggle field={form.$('isIndirectMessageBadgeEnabled')} /> | 284 | <Toggle field={form.$('isNotificationEnabled')} /> |
285 | <Toggle field={form.$('isMuted')} /> | ||
238 | <p className="settings__help"> | 286 | <p className="settings__help"> |
239 | {intl.formatMessage(messages.indirectMessageInfo)} | 287 | {intl.formatMessage(messages.isMutedInfo)} |
240 | </p> | 288 | </p> |
241 | </div> | 289 | </div> |
242 | )} | 290 | |
243 | <Toggle field={form.$('isMuted')} /> | 291 | <div className="settings__settings-group"> |
244 | <p className="settings__help"> | 292 | <h3>{intl.formatMessage(messages.headlineBadges)}</h3> |
245 | {intl.formatMessage(messages.isMutedInfo)} | 293 | <Toggle field={form.$('isBadgeEnabled')} /> |
246 | </p> | 294 | {recipe.hasIndirectMessages && form.$('isBadgeEnabled').value && ( |
247 | <Toggle field={form.$('isEnabled')} /> | 295 | <div> |
296 | <Toggle field={form.$('isIndirectMessageBadgeEnabled')} /> | ||
297 | <p className="settings__help"> | ||
298 | {intl.formatMessage(messages.indirectMessageInfo)} | ||
299 | </p> | ||
300 | </div> | ||
301 | )} | ||
302 | </div> | ||
303 | |||
304 | <div className="settings__settings-group"> | ||
305 | <h3>{intl.formatMessage(messages.headlineGeneral)}</h3> | ||
306 | <Toggle field={form.$('isEnabled')} /> | ||
307 | </div> | ||
308 | </div> | ||
309 | <div className="service-icon"> | ||
310 | <ImageUpload | ||
311 | field={form.$('customIcon')} | ||
312 | textDelete={intl.formatMessage(messages.iconDelete)} | ||
313 | textUpload={intl.formatMessage(messages.iconUpload)} | ||
314 | /> | ||
315 | </div> | ||
248 | </div> | 316 | </div> |
249 | {recipe.message && ( | 317 | {recipe.message && ( |
250 | <p className="settings__message"> | 318 | <p className="settings__message"> |
diff --git a/src/components/settings/services/ServicesDashboard.js b/src/components/settings/services/ServicesDashboard.js index 5f146b5f3..20e451f01 100644 --- a/src/components/settings/services/ServicesDashboard.js +++ b/src/components/settings/services/ServicesDashboard.js | |||
@@ -15,10 +15,18 @@ const messages = defineMessages({ | |||
15 | id: 'settings.services.headline', | 15 | id: 'settings.services.headline', |
16 | defaultMessage: '!!!Your services', | 16 | defaultMessage: '!!!Your services', |
17 | }, | 17 | }, |
18 | searchService: { | ||
19 | id: 'settings.searchService', | ||
20 | defaultMessage: '!!!Search service', | ||
21 | }, | ||
18 | noServicesAdded: { | 22 | noServicesAdded: { |
19 | id: 'settings.services.noServicesAdded', | 23 | id: 'settings.services.noServicesAdded', |
20 | defaultMessage: '!!!You haven\'t added any services yet.', | 24 | defaultMessage: '!!!You haven\'t added any services yet.', |
21 | }, | 25 | }, |
26 | noServiceFound: { | ||
27 | id: 'settings.recipes.nothingFound', | ||
28 | defaultMessage: '!!!Sorry, but no service matched your search term.', | ||
29 | }, | ||
22 | discoverServices: { | 30 | discoverServices: { |
23 | id: 'settings.services.discoverServices', | 31 | id: 'settings.services.discoverServices', |
24 | defaultMessage: '!!!Discover services', | 32 | defaultMessage: '!!!Discover services', |
@@ -53,7 +61,13 @@ export default class ServicesDashboard extends Component { | |||
53 | servicesRequestFailed: PropTypes.bool.isRequired, | 61 | servicesRequestFailed: PropTypes.bool.isRequired, |
54 | retryServicesRequest: PropTypes.func.isRequired, | 62 | retryServicesRequest: PropTypes.func.isRequired, |
55 | status: MobxPropTypes.arrayOrObservableArray.isRequired, | 63 | status: MobxPropTypes.arrayOrObservableArray.isRequired, |
64 | searchNeedle: PropTypes.string, | ||
56 | }; | 65 | }; |
66 | |||
67 | static defaultProps = { | ||
68 | searchNeedle: '', | ||
69 | } | ||
70 | |||
57 | static contextTypes = { | 71 | static contextTypes = { |
58 | intl: intlShape, | 72 | intl: intlShape, |
59 | }; | 73 | }; |
@@ -69,20 +83,24 @@ export default class ServicesDashboard extends Component { | |||
69 | servicesRequestFailed, | 83 | servicesRequestFailed, |
70 | retryServicesRequest, | 84 | retryServicesRequest, |
71 | status, | 85 | status, |
86 | searchNeedle, | ||
72 | } = this.props; | 87 | } = this.props; |
73 | const { intl } = this.context; | 88 | const { intl } = this.context; |
74 | 89 | ||
75 | return ( | 90 | return ( |
76 | <div className="settings__main"> | 91 | <div className="settings__main"> |
77 | <div className="settings__header"> | 92 | <div className="settings__header"> |
78 | <SearchInput | 93 | <h1>{intl.formatMessage(messages.headline)}</h1> |
79 | className="settings__search-header" | ||
80 | defaultValue={intl.formatMessage(messages.headline)} | ||
81 | onChange={needle => filterServices({ needle })} | ||
82 | onReset={() => resetFilter()} | ||
83 | /> | ||
84 | </div> | 94 | </div> |
85 | <div className="settings__body"> | 95 | <div className="settings__body"> |
96 | {!isLoading && ( | ||
97 | <SearchInput | ||
98 | placeholder={intl.formatMessage(messages.searchService)} | ||
99 | onChange={needle => filterServices({ needle })} | ||
100 | onReset={() => resetFilter()} | ||
101 | autoFocus | ||
102 | /> | ||
103 | )} | ||
86 | {!isLoading && servicesRequestFailed && ( | 104 | {!isLoading && servicesRequestFailed && ( |
87 | <div> | 105 | <div> |
88 | <Infobox | 106 | <Infobox |
@@ -121,7 +139,7 @@ export default class ServicesDashboard extends Component { | |||
121 | </Appear> | 139 | </Appear> |
122 | )} | 140 | )} |
123 | 141 | ||
124 | {!isLoading && services.length === 0 && ( | 142 | {!isLoading && services.length === 0 && !searchNeedle && ( |
125 | <div className="align-middle settings__empty-state"> | 143 | <div className="align-middle settings__empty-state"> |
126 | <p className="settings__empty-text"> | 144 | <p className="settings__empty-text"> |
127 | <span className="emoji"> | 145 | <span className="emoji"> |
@@ -132,6 +150,16 @@ export default class ServicesDashboard extends Component { | |||
132 | <Link to="/settings/recipes" className="button">{intl.formatMessage(messages.discoverServices)}</Link> | 150 | <Link to="/settings/recipes" className="button">{intl.formatMessage(messages.discoverServices)}</Link> |
133 | </div> | 151 | </div> |
134 | )} | 152 | )} |
153 | {!isLoading && services.length === 0 && searchNeedle && ( | ||
154 | <div className="align-middle settings__empty-state"> | ||
155 | <p className="settings__empty-text"> | ||
156 | <span className="emoji"> | ||
157 | <img src="./assets/images/emoji/dontknow.png" alt="" /> | ||
158 | </span> | ||
159 | {intl.formatMessage(messages.noServiceFound)} | ||
160 | </p> | ||
161 | </div> | ||
162 | )} | ||
135 | {isLoading ? ( | 163 | {isLoading ? ( |
136 | <Loader /> | 164 | <Loader /> |
137 | ) : ( | 165 | ) : ( |
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index 878e46d6d..72aa5a8af 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js | |||
@@ -40,6 +40,18 @@ const messages = defineMessages({ | |||
40 | id: 'settings.app.translationHelp', | 40 | id: 'settings.app.translationHelp', |
41 | defaultMessage: '!!!Help us to translate Franz into your language.', | 41 | defaultMessage: '!!!Help us to translate Franz into your language.', |
42 | }, | 42 | }, |
43 | subheadlineCache: { | ||
44 | id: 'settings.app.subheadlineCache', | ||
45 | defaultMessage: '!!!Cache', | ||
46 | }, | ||
47 | cacheInfo: { | ||
48 | id: 'settings.app.cacheInfo', | ||
49 | defaultMessage: '!!!Franz cache is currently using {size} of disk space.', | ||
50 | }, | ||
51 | buttonClearAllCache: { | ||
52 | id: 'settings.app.buttonClearAllCache', | ||
53 | defaultMessage: '!!!Clear cache', | ||
54 | }, | ||
43 | buttonSearchForUpdate: { | 55 | buttonSearchForUpdate: { |
44 | id: 'settings.app.buttonSearchForUpdate', | 56 | id: 'settings.app.buttonSearchForUpdate', |
45 | defaultMessage: '!!!Check for updates', | 57 | defaultMessage: '!!!Check for updates', |
@@ -64,10 +76,6 @@ const messages = defineMessages({ | |||
64 | id: 'settings.app.currentVersion', | 76 | id: 'settings.app.currentVersion', |
65 | defaultMessage: '!!!Current version:', | 77 | defaultMessage: '!!!Current version:', |
66 | }, | 78 | }, |
67 | restartRequired: { | ||
68 | id: 'settings.app.restartRequired', | ||
69 | defaultMessage: '!!!Changes require restart', | ||
70 | }, | ||
71 | }); | 79 | }); |
72 | 80 | ||
73 | @observer | 81 | @observer |
@@ -81,6 +89,9 @@ export default class EditSettingsForm extends Component { | |||
81 | isUpdateAvailable: PropTypes.bool.isRequired, | 89 | isUpdateAvailable: PropTypes.bool.isRequired, |
82 | noUpdateAvailable: PropTypes.bool.isRequired, | 90 | noUpdateAvailable: PropTypes.bool.isRequired, |
83 | updateIsReadyToInstall: PropTypes.bool.isRequired, | 91 | updateIsReadyToInstall: PropTypes.bool.isRequired, |
92 | isClearingAllCache: PropTypes.bool.isRequired, | ||
93 | onClearAllCache: PropTypes.func.isRequired, | ||
94 | cacheSize: PropTypes.string.isRequired, | ||
84 | }; | 95 | }; |
85 | 96 | ||
86 | static contextTypes = { | 97 | static contextTypes = { |
@@ -107,6 +118,9 @@ export default class EditSettingsForm extends Component { | |||
107 | isUpdateAvailable, | 118 | isUpdateAvailable, |
108 | noUpdateAvailable, | 119 | noUpdateAvailable, |
109 | updateIsReadyToInstall, | 120 | updateIsReadyToInstall, |
121 | isClearingAllCache, | ||
122 | onClearAllCache, | ||
123 | cacheSize, | ||
110 | } = this.props; | 124 | } = this.props; |
111 | const { intl } = this.context; | 125 | const { intl } = this.context; |
112 | 126 | ||
@@ -158,8 +172,26 @@ export default class EditSettingsForm extends Component { | |||
158 | {/* Advanced */} | 172 | {/* Advanced */} |
159 | <h2 id="advanced">{intl.formatMessage(messages.headlineAdvanced)}</h2> | 173 | <h2 id="advanced">{intl.formatMessage(messages.headlineAdvanced)}</h2> |
160 | <Toggle field={form.$('enableSpellchecking')} /> | 174 | <Toggle field={form.$('enableSpellchecking')} /> |
161 | <p className="settings__help">{intl.formatMessage(messages.restartRequired)}</p> | ||
162 | {/* <Select field={form.$('spellcheckingLanguage')} /> */} | 175 | {/* <Select field={form.$('spellcheckingLanguage')} /> */} |
176 | <div className="settings__settings-group"> | ||
177 | <h3> | ||
178 | {intl.formatMessage(messages.subheadlineCache)} | ||
179 | </h3> | ||
180 | <p> | ||
181 | {intl.formatMessage(messages.cacheInfo, { | ||
182 | size: cacheSize, | ||
183 | })} | ||
184 | </p> | ||
185 | <p> | ||
186 | <Button | ||
187 | buttonType="secondary" | ||
188 | label={intl.formatMessage(messages.buttonClearAllCache)} | ||
189 | onClick={onClearAllCache} | ||
190 | disabled={isClearingAllCache} | ||
191 | loaded={!isClearingAllCache} | ||
192 | /> | ||
193 | </p> | ||
194 | </div> | ||
163 | 195 | ||
164 | {/* Updates */} | 196 | {/* Updates */} |
165 | <h2 id="updates">{intl.formatMessage(messages.headlineUpdates)}</h2> | 197 | <h2 id="updates">{intl.formatMessage(messages.headlineUpdates)}</h2> |
@@ -170,6 +202,7 @@ export default class EditSettingsForm extends Component { | |||
170 | /> | 202 | /> |
171 | ) : ( | 203 | ) : ( |
172 | <Button | 204 | <Button |
205 | buttonType="secondary" | ||
173 | label={intl.formatMessage(updateButtonLabelMessage)} | 206 | label={intl.formatMessage(updateButtonLabelMessage)} |
174 | onClick={checkForUpdates} | 207 | onClick={checkForUpdates} |
175 | disabled={isCheckingForUpdates || isUpdateAvailable} | 208 | disabled={isCheckingForUpdates || isUpdateAvailable} |
diff --git a/src/components/ui/ImageUpload.js b/src/components/ui/ImageUpload.js new file mode 100644 index 000000000..81c3b8da6 --- /dev/null +++ b/src/components/ui/ImageUpload.js | |||
@@ -0,0 +1,108 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import { Field } from 'mobx-react-form'; | ||
5 | // import Loader from 'react-loader'; | ||
6 | import classnames from 'classnames'; | ||
7 | import Dropzone from 'react-dropzone'; | ||
8 | |||
9 | @observer | ||
10 | export default class ImageUpload extends Component { | ||
11 | static propTypes = { | ||
12 | field: PropTypes.instanceOf(Field).isRequired, | ||
13 | className: PropTypes.string, | ||
14 | multiple: PropTypes.bool, | ||
15 | textDelete: PropTypes.string.isRequired, | ||
16 | textUpload: PropTypes.string.isRequired, | ||
17 | }; | ||
18 | |||
19 | static defaultProps = { | ||
20 | className: null, | ||
21 | multiple: false, | ||
22 | }; | ||
23 | |||
24 | state = { | ||
25 | path: null, | ||
26 | } | ||
27 | |||
28 | onDrop(acceptedFiles) { | ||
29 | const { field } = this.props; | ||
30 | |||
31 | acceptedFiles.forEach((file) => { | ||
32 | this.setState({ | ||
33 | path: file.path, | ||
34 | }); | ||
35 | this.props.field.onDrop(file); | ||
36 | }); | ||
37 | |||
38 | field.set(''); | ||
39 | } | ||
40 | |||
41 | dropzoneRef = null; | ||
42 | |||
43 | render() { | ||
44 | const { | ||
45 | field, | ||
46 | className, | ||
47 | multiple, | ||
48 | textDelete, | ||
49 | textUpload, | ||
50 | } = this.props; | ||
51 | |||
52 | const cssClasses = classnames({ | ||
53 | 'image-upload__dropzone': true, | ||
54 | [`${className}`]: className, | ||
55 | }); | ||
56 | |||
57 | return ( | ||
58 | <div className="image-upload-wrapper"> | ||
59 | <label className="franz-form__label" htmlFor="iconUpload">{field.label}</label> | ||
60 | <div className="image-upload"> | ||
61 | {(field.value && field.value !== 'delete') || this.state.path ? ( | ||
62 | <div> | ||
63 | <div | ||
64 | className="image-upload__preview" | ||
65 | style={({ | ||
66 | backgroundImage: `url("${this.state.path || field.value}")`, | ||
67 | })} | ||
68 | /> | ||
69 | <div className="image-upload__action"> | ||
70 | <button | ||
71 | type="button" | ||
72 | onClick={() => { | ||
73 | if (field.value) { | ||
74 | field.set('delete'); | ||
75 | } else { | ||
76 | this.setState({ | ||
77 | path: null, | ||
78 | }); | ||
79 | } | ||
80 | }} | ||
81 | > | ||
82 | <i className="mdi mdi-delete" /> | ||
83 | <p> | ||
84 | {textDelete} | ||
85 | </p> | ||
86 | </button> | ||
87 | <div className="image-upload__action-background" /> | ||
88 | </div> | ||
89 | </div> | ||
90 | ) : ( | ||
91 | <Dropzone | ||
92 | ref={(node) => { this.dropzoneRef = node; }} | ||
93 | onDrop={this.onDrop.bind(this)} | ||
94 | className={cssClasses} | ||
95 | multiple={multiple} | ||
96 | accept="image/jpeg, image/png" | ||
97 | > | ||
98 | <i className="mdi mdi-file-image" /> | ||
99 | <p> | ||
100 | {textUpload} | ||
101 | </p> | ||
102 | </Dropzone> | ||
103 | )} | ||
104 | </div> | ||
105 | </div> | ||
106 | ); | ||
107 | } | ||
108 | } | ||
diff --git a/src/components/ui/SearchInput.js b/src/components/ui/SearchInput.js index bca412cef..a94cde201 100644 --- a/src/components/ui/SearchInput.js +++ b/src/components/ui/SearchInput.js | |||
@@ -9,36 +9,46 @@ import { debounce } from 'lodash'; | |||
9 | export default class SearchInput extends Component { | 9 | export default class SearchInput extends Component { |
10 | static propTypes = { | 10 | static propTypes = { |
11 | value: PropTypes.string, | 11 | value: PropTypes.string, |
12 | defaultValue: PropTypes.string, | 12 | placeholder: PropTypes.string, |
13 | className: PropTypes.string, | 13 | className: PropTypes.string, |
14 | onChange: PropTypes.func, | 14 | onChange: PropTypes.func, |
15 | onReset: PropTypes.func, | 15 | onReset: PropTypes.func, |
16 | name: PropTypes.string, | 16 | name: PropTypes.string, |
17 | throttle: PropTypes.bool, | 17 | throttle: PropTypes.bool, |
18 | throttleDelay: PropTypes.number, | 18 | throttleDelay: PropTypes.number, |
19 | autoFocus: PropTypes.bool, | ||
19 | }; | 20 | }; |
20 | 21 | ||
21 | static defaultProps = { | 22 | static defaultProps = { |
22 | value: '', | 23 | value: '', |
23 | defaultValue: '', | 24 | placeholder: '', |
24 | className: '', | 25 | className: '', |
25 | name: uuidv1(), | 26 | name: uuidv1(), |
26 | throttle: false, | 27 | throttle: false, |
27 | throttleDelay: 250, | 28 | throttleDelay: 250, |
28 | onChange: () => null, | 29 | onChange: () => null, |
29 | onReset: () => null, | 30 | onReset: () => null, |
31 | autoFocus: false, | ||
30 | } | 32 | } |
31 | 33 | ||
32 | constructor(props) { | 34 | constructor(props) { |
33 | super(props); | 35 | super(props); |
34 | 36 | ||
35 | this.state = { | 37 | this.state = { |
36 | value: props.value || props.defaultValue, | 38 | value: props.value, |
37 | }; | 39 | }; |
38 | 40 | ||
39 | this.throttledOnChange = debounce(this.throttledOnChange, this.props.throttleDelay); | 41 | this.throttledOnChange = debounce(this.throttledOnChange, this.props.throttleDelay); |
40 | } | 42 | } |
41 | 43 | ||
44 | componentDidMount() { | ||
45 | const { autoFocus } = this.props; | ||
46 | |||
47 | if (autoFocus) { | ||
48 | this.input.focus(); | ||
49 | } | ||
50 | } | ||
51 | |||
42 | onChange(e) { | 52 | onChange(e) { |
43 | const { throttle, onChange } = this.props; | 53 | const { throttle, onChange } = this.props; |
44 | const { value } = e.target; | 54 | const { value } = e.target; |
@@ -52,26 +62,6 @@ export default class SearchInput extends Component { | |||
52 | } | 62 | } |
53 | } | 63 | } |
54 | 64 | ||
55 | onClick() { | ||
56 | const { defaultValue } = this.props; | ||
57 | const { value } = this.state; | ||
58 | |||
59 | if (value === defaultValue) { | ||
60 | this.setState({ value: '' }); | ||
61 | } | ||
62 | |||
63 | this.input.focus(); | ||
64 | } | ||
65 | |||
66 | onBlur() { | ||
67 | const { defaultValue } = this.props; | ||
68 | const { value } = this.state; | ||
69 | |||
70 | if (value === '') { | ||
71 | this.setState({ value: defaultValue }); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | throttledOnChange(e) { | 65 | throttledOnChange(e) { |
76 | const { onChange } = this.props; | 66 | const { onChange } = this.props; |
77 | 67 | ||
@@ -79,8 +69,8 @@ export default class SearchInput extends Component { | |||
79 | } | 69 | } |
80 | 70 | ||
81 | reset() { | 71 | reset() { |
82 | const { defaultValue, onReset } = this.props; | 72 | const { onReset } = this.props; |
83 | this.setState({ value: defaultValue }); | 73 | this.setState({ value: '' }); |
84 | 74 | ||
85 | onReset(); | 75 | onReset(); |
86 | } | 76 | } |
@@ -88,7 +78,7 @@ export default class SearchInput extends Component { | |||
88 | input = null; | 78 | input = null; |
89 | 79 | ||
90 | render() { | 80 | render() { |
91 | const { className, name, defaultValue } = this.props; | 81 | const { className, name, placeholder } = this.props; |
92 | const { value } = this.state; | 82 | const { value } = this.state; |
93 | 83 | ||
94 | return ( | 84 | return ( |
@@ -101,18 +91,16 @@ export default class SearchInput extends Component { | |||
101 | <label | 91 | <label |
102 | htmlFor={name} | 92 | htmlFor={name} |
103 | className="mdi mdi-magnify" | 93 | className="mdi mdi-magnify" |
104 | onClick={() => this.onClick()} | ||
105 | /> | 94 | /> |
106 | <input | 95 | <input |
107 | name={name} | 96 | name={name} |
108 | type="text" | 97 | type="text" |
98 | placeholder={placeholder} | ||
109 | value={value} | 99 | value={value} |
110 | onChange={e => this.onChange(e)} | 100 | onChange={e => this.onChange(e)} |
111 | onClick={() => this.onClick()} | ||
112 | onBlur={() => this.onBlur()} | ||
113 | ref={(ref) => { this.input = ref; }} | 101 | ref={(ref) => { this.input = ref; }} |
114 | /> | 102 | /> |
115 | {value !== defaultValue && value.length > 0 && ( | 103 | {value.length > 0 && ( |
116 | <span | 104 | <span |
117 | className="mdi mdi-close-circle-outline" | 105 | className="mdi mdi-close-circle-outline" |
118 | onClick={() => this.reset()} | 106 | onClick={() => this.reset()} |
diff --git a/src/components/ui/SubscriptionPopup.js b/src/components/ui/SubscriptionPopup.js index 5aae2c47a..528d02907 100644 --- a/src/components/ui/SubscriptionPopup.js +++ b/src/components/ui/SubscriptionPopup.js | |||
@@ -58,7 +58,6 @@ export default class SubscriptionPopup extends Component { | |||
58 | 58 | ||
59 | autosize | 59 | autosize |
60 | src={encodeURI(url)} | 60 | src={encodeURI(url)} |
61 | disablewebsecurity | ||
62 | onDidNavigate={completeCheck} | 61 | onDidNavigate={completeCheck} |
63 | // onNewWindow={(event, url, frameName, options) => | 62 | // onNewWindow={(event, url, frameName, options) => |
64 | // openWindow({ event, url, frameName, options })} | 63 | // openWindow({ event, url, frameName, options })} |