summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/components/settings/services/EditServiceForm.js75
-rw-r--r--src/components/ui/ImageUpload.js93
-rw-r--r--src/containers/settings/EditServiceScreen.js4
-rw-r--r--src/i18n/locales/en-US.json4
-rw-r--r--src/models/Service.js3
-rw-r--r--src/stores/ServicesStore.js10
-rw-r--r--src/styles/image-upload.scss47
-rw-r--r--src/styles/settings.scss11
8 files changed, 163 insertions, 84 deletions
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js
index 4f2f98a01..c0a993736 100644
--- a/src/components/settings/services/EditServiceForm.js
+++ b/src/components/settings/services/EditServiceForm.js
@@ -84,6 +84,14 @@ const messages = defineMessages({
84 id: 'settings.service.form.headlineGeneral', 84 id: 'settings.service.form.headlineGeneral',
85 defaultMessage: '!!!General', 85 defaultMessage: '!!!General',
86 }, 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 },
87}); 95});
88 96
89@observer 97@observer
@@ -223,14 +231,8 @@ export default class EditServiceForm extends Component {
223 </div> 231 </div>
224 <div className="settings__body"> 232 <div className="settings__body">
225 <form onSubmit={e => this.submit(e)} id="form"> 233 <form onSubmit={e => this.submit(e)} id="form">
226 <div className="service-flex-grid"> 234 <div className="service-name">
227 <div className="service-name"> 235 <Input field={form.$('name')} focus />
228 <Input field={form.$('name')} focus />
229 </div>
230 <div className="service-icon">
231 {/* <Input field={form.$('name')} focus /> */}
232 <ImageUpload field={form.$('customIcon')} />
233 </div>
234 </div> 236 </div>
235 {(recipe.hasTeamId || recipe.hasCustomUrl) && ( 237 {(recipe.hasTeamId || recipe.hasCustomUrl) && (
236 <Tabs 238 <Tabs
@@ -275,32 +277,41 @@ export default class EditServiceForm extends Component {
275 )} 277 )}
276 </Tabs> 278 </Tabs>
277 )} 279 )}
278 <div className="settings__options"> 280 <div className="service-flex-grid">
279 <div className="settings__settings-group"> 281 <div className="settings__options">
280 <h3>{intl.formatMessage(messages.headlineNotifications)}</h3> 282 <div className="settings__settings-group">
281 <Toggle field={form.$('isNotificationEnabled')} /> 283 <h3>{intl.formatMessage(messages.headlineNotifications)}</h3>
282 <Toggle field={form.$('isMuted')} /> 284 <Toggle field={form.$('isNotificationEnabled')} />
283 <p className="settings__help"> 285 <Toggle field={form.$('isMuted')} />
284 {intl.formatMessage(messages.isMutedInfo)} 286 <p className="settings__help">
285 </p> 287 {intl.formatMessage(messages.isMutedInfo)}
286 </div> 288 </p>
289 </div>
287 290
288 <div className="settings__settings-group"> 291 <div className="settings__settings-group">
289 <h3>{intl.formatMessage(messages.headlineBadges)}</h3> 292 <h3>{intl.formatMessage(messages.headlineBadges)}</h3>
290 <Toggle field={form.$('isBadgeEnabled')} /> 293 <Toggle field={form.$('isBadgeEnabled')} />
291 {recipe.hasIndirectMessages && form.$('isBadgeEnabled').value && ( 294 {recipe.hasIndirectMessages && form.$('isBadgeEnabled').value && (
292 <div> 295 <div>
293 <Toggle field={form.$('isIndirectMessageBadgeEnabled')} /> 296 <Toggle field={form.$('isIndirectMessageBadgeEnabled')} />
294 <p className="settings__help"> 297 <p className="settings__help">
295 {intl.formatMessage(messages.indirectMessageInfo)} 298 {intl.formatMessage(messages.indirectMessageInfo)}
296 </p> 299 </p>
297 </div> 300 </div>
298 )} 301 )}
299 </div> 302 </div>
300 303
301 <div className="settings__settings-group"> 304 <div className="settings__settings-group">
302 <h3>{intl.formatMessage(messages.headlineGeneral)}</h3> 305 <h3>{intl.formatMessage(messages.headlineGeneral)}</h3>
303 <Toggle field={form.$('isEnabled')} /> 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 />
304 </div> 315 </div>
305 </div> 316 </div>
306 {recipe.message && ( 317 {recipe.message && (
diff --git a/src/components/ui/ImageUpload.js b/src/components/ui/ImageUpload.js
index f25d966f4..08809f007 100644
--- a/src/components/ui/ImageUpload.js
+++ b/src/components/ui/ImageUpload.js
@@ -12,6 +12,8 @@ export default class ImageUpload extends Component {
12 field: PropTypes.instanceOf(Field).isRequired, 12 field: PropTypes.instanceOf(Field).isRequired,
13 className: PropTypes.string, 13 className: PropTypes.string,
14 multiple: PropTypes.bool, 14 multiple: PropTypes.bool,
15 textDelete: PropTypes.string.isRequired,
16 textUpload: PropTypes.string.isRequired,
15 }; 17 };
16 18
17 static defaultProps = { 19 static defaultProps = {
@@ -39,56 +41,63 @@ export default class ImageUpload extends Component {
39 field, 41 field,
40 className, 42 className,
41 multiple, 43 multiple,
44 textDelete,
45 textUpload,
42 } = this.props; 46 } = this.props;
43 47
44 const cssClasses = classnames({ 48 const cssClasses = classnames({
45 'franz-form__button': true, 49 'image-upload__dropzone': true,
46 [`${className}`]: className, 50 [`${className}`]: className,
47 }); 51 });
48 52
49 return ( 53 return (
50 <div> 54 <div className="image-upload-wrapper">
51 {field.label} 55 <label className="franz-form__label" htmlFor="iconUpload">{field.label}</label>
52 {(field.value && field.value !== 'delete') || this.state.path ? ( 56 <div className="image-upload">
53 <div 57 {(field.value && field.value !== 'delete') || this.state.path ? (
54 className="image-upload" 58 <div>
55 > 59 <div
56 <div 60 className="image-upload__preview"
57 className="image-upload__preview" 61 style={({
58 style={({ 62 backgroundImage: `url("${field.value || this.state.path}")`,
59 backgroundImage: `url(${field.value || this.state.path})`, 63 })}
60 })} 64 />
61 /> 65 <div className="image-upload__action">
62 <div className="image-upload__action"> 66 <button
63 <button 67 type="button"
64 type="button" 68 onClick={() => {
65 onClick={() => { 69 if (field.value) {
66 if (field.value) { 70 field.set('delete');
67 field.value = 'delete'; 71 } else {
68 } else { 72 this.setState({
69 this.setState({ 73 path: null,
70 path: null, 74 });
71 }); 75 }
72 } 76 }}
73 }} 77 >
74 > 78 <i className="mdi mdi-delete" />
75 remove icon 79 <p>
76 80 {textDelete}
77 </button> 81 </p>
78 <div className="image-upload__action-background" /> 82 </button>
83 <div className="image-upload__action-background" />
84 </div>
79 </div> 85 </div>
80 </div> 86 ) : (
81 ) : ( 87 <Dropzone
82 <Dropzone 88 ref={(node) => { this.dropzoneRef = node; }}
83 ref={(node) => { this.dropzoneRef = node; }} 89 onDrop={this.onDrop.bind(this)}
84 onDrop={this.onDrop.bind(this)} 90 className={cssClasses}
85 className={cssClasses} 91 multiple={multiple}
86 multiple={multiple} 92 accept="image/jpeg, image/png"
87 accept="image/jpeg, image/png" 93 >
88 > 94 <i className="mdi mdi-file-image" />
89 <p>Try dropping some files here, or click to select files to upload.</p> 95 <p>
90 </Dropzone> 96 {textUpload}
91 )} 97 </p>
98 </Dropzone>
99 )}
100 </div>
92 </div> 101 </div>
93 ); 102 );
94 } 103 }
diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js
index 8d3a268ad..c26195a1e 100644
--- a/src/containers/settings/EditServiceScreen.js
+++ b/src/containers/settings/EditServiceScreen.js
@@ -48,7 +48,7 @@ const messages = defineMessages({
48 }, 48 },
49 icon: { 49 icon: {
50 id: 'settings.service.form.icon', 50 id: 'settings.service.form.icon',
51 defaultMessage: '!!!Icon', 51 defaultMessage: '!!!Custom icon',
52 }, 52 },
53}); 53});
54 54
@@ -108,7 +108,7 @@ export default class EditServiceScreen extends Component {
108 }, 108 },
109 customIcon: { 109 customIcon: {
110 label: intl.formatMessage(messages.icon), 110 label: intl.formatMessage(messages.icon),
111 value: service.hasCustomIcon ? service.icon : false, 111 value: service.hasCustomUploadedIcon ? service.icon : false,
112 default: null, 112 default: null,
113 type: 'file', 113 type: 'file',
114 }, 114 },
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index bfb95da04..f3db20aea 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -127,7 +127,9 @@
127 "settings.service.form.headlineNotifications": "Notifications", 127 "settings.service.form.headlineNotifications": "Notifications",
128 "settings.service.form.headlineBadges": "Unread message badges", 128 "settings.service.form.headlineBadges": "Unread message badges",
129 "settings.service.form.headlineGeneral": "General", 129 "settings.service.form.headlineGeneral": "General",
130 "settings.service.form.icon": "Icon", 130 "settings.service.form.icon": "Custom icon",
131 "settings.service.form.iconDelete": "Delete",
132 "settings.service.form.iconUpload": "Drop your image, or click here",
131 "settings.service.error.headline": "Error", 133 "settings.service.error.headline": "Error",
132 "settings.service.error.goBack": "Back to services", 134 "settings.service.error.goBack": "Back to services",
133 "settings.service.error.message": "Could not load service recipe.", 135 "settings.service.error.message": "Could not load service recipe.",
diff --git a/src/models/Service.js b/src/models/Service.js
index 652d2594c..423510c7d 100644
--- a/src/models/Service.js
+++ b/src/models/Service.js
@@ -25,6 +25,7 @@ export default class Service {
25 @observable isBadgeEnabled = true; 25 @observable isBadgeEnabled = true;
26 @observable isIndirectMessageBadgeEnabled = true; 26 @observable isIndirectMessageBadgeEnabled = true;
27 @observable iconUrl = ''; 27 @observable iconUrl = '';
28 @observable hasCustomUploadedIcon = false;
28 @observable hasCrashed = false; 29 @observable hasCrashed = false;
29 30
30 constructor(data, recipe) { 31 constructor(data, recipe) {
@@ -62,6 +63,8 @@ export default class Service {
62 63
63 this.isMuted = data.isMuted !== undefined ? data.isMuted : this.isMuted; 64 this.isMuted = data.isMuted !== undefined ? data.isMuted : this.isMuted;
64 65
66 this.hasCustomUploadedIcon = data.hasCustomIcon !== undefined ? data.hasCustomIcon : this.hasCustomUploadedIcon;
67
65 this.recipe = recipe; 68 this.recipe = recipe;
66 69
67 autorun(() => { 70 autorun(() => {
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index 4fb5c9767..f2a8683ba 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -177,13 +177,21 @@ export default class ServicesStore extends Store {
177 await request._promise; 177 await request._promise;
178 178
179 newData.iconUrl = request.result.data.iconUrl; 179 newData.iconUrl = request.result.data.iconUrl;
180 newData.hasCustomUploadedIcon = true;
180 } 181 }
181 182
182 this.allServicesRequest.patch((result) => { 183 this.allServicesRequest.patch((result) => {
183 if (!result) return; 184 if (!result) return;
184 185
186 // patch custom icon deletion
185 if (data.customIcon === 'delete') { 187 if (data.customIcon === 'delete') {
186 data.iconUrl = ''; 188 data.iconUrl = '';
189 data.hasCustomUploadedIcon = false;
190 }
191
192 // patch custom icon url
193 if (data.customIconUrl) {
194 data.iconUrl = data.customIconUrl;
187 } 195 }
188 196
189 Object.assign(result.find(c => c.id === serviceId), newData); 197 Object.assign(result.find(c => c.id === serviceId), newData);
@@ -328,7 +336,7 @@ export default class ServicesStore extends Store {
328 } 336 }
329 } else if (channel === 'avatar') { 337 } else if (channel === 'avatar') {
330 const url = args[0]; 338 const url = args[0];
331 if (service.customIconUrl !== url) { 339 if (service.iconUrl !== url && !service.hasCustomUploadedIcon) {
332 service.customIconUrl = url; 340 service.customIconUrl = url;
333 341
334 this.actions.service.updateService({ 342 this.actions.service.updateService({
diff --git a/src/styles/image-upload.scss b/src/styles/image-upload.scss
index 764bb3158..06176a7af 100644
--- a/src/styles/image-upload.scss
+++ b/src/styles/image-upload.scss
@@ -1,9 +1,12 @@
1.image-upload { 1.image-upload {
2 position: absolute; 2 position: absolute;
3 width: 100px; 3 width: 140px;
4 height: 100px; 4 height: 140px;
5 border-radius: $theme-border-radius; 5 border: 1px solid $theme-gray-lighter;
6 border-radius: $theme-border-radius-small;
7 background: $theme-gray-lightest;
6 overflow: hidden; 8 overflow: hidden;
9 margin-top: 5px;
7 10
8 &__preview, 11 &__preview,
9 &__action { 12 &__action {
@@ -19,7 +22,7 @@
19 background-size: 100%; 22 background-size: 100%;
20 background-repeat: no-repeat; 23 background-repeat: no-repeat;
21 background-position: center center; 24 background-position: center center;
22 margin: 5px; 25 border-radius: 3px;
23 } 26 }
24 27
25 &__action { 28 &__action {
@@ -27,6 +30,8 @@
27 z-index: 10; 30 z-index: 10;
28 opacity: 0; 31 opacity: 0;
29 transition: opacity 0.5s; 32 transition: opacity 0.5s;
33 display: flex;
34 justify-content: center;
30 35
31 &-background { 36 &-background {
32 position: absolute; 37 position: absolute;
@@ -41,6 +46,33 @@
41 button { 46 button {
42 position: relative; 47 position: relative;
43 z-index: 100; 48 z-index: 100;
49 color: #FFF;
50
51 .mdi {
52 color: #FFF;
53 }
54 }
55 }
56
57 &__dropzone {
58 text-align: center;
59 border-radius: 5px;
60 padding: 10px;
61 display: flex;
62 align-items: center;
63 justify-content: center;
64 flex-direction: column;
65 }
66
67 &__dropzone,
68 button {
69 .mdi {
70 margin-bottom: 5px;
71 }
72
73 p {
74 font-size: 10px;
75 line-height: 10px;
44 } 76 }
45 } 77 }
46 78
@@ -49,4 +81,11 @@
49 opacity: 1; 81 opacity: 1;
50 } 82 }
51 } 83 }
84}
85
86.image-upload-wrapper {
87 .mdi {
88 font-size: 40px;
89 color: $theme-gray-light;
90 }
52} \ No newline at end of file 91} \ No newline at end of file
diff --git a/src/styles/settings.scss b/src/styles/settings.scss
index 283913ab7..cb39717ea 100644
--- a/src/styles/settings.scss
+++ b/src/styles/settings.scss
@@ -121,9 +121,15 @@
121 } 121 }
122 122
123 .service-icon { 123 .service-icon {
124 width: 30%; 124 width: 140px;
125 min-width: 100px; 125 float: right;
126 margin-top: 30px;
126 margin-left: 40px; 127 margin-left: 40px;
128
129 label {
130 font-weight: bold;
131 letter-spacing: -0.1px;
132 }
127 } 133 }
128 } 134 }
129 135
@@ -167,6 +173,7 @@
167 173
168 &__options { 174 &__options {
169 margin-top: 20px; 175 margin-top: 20px;
176 flex: 1;
170 } 177 }
171 178
172 &__settings-group { 179 &__settings-group {