@@ -23,6 +23,9 @@ import Notifier from "../../../../../Notifier";
23
23
import SettingsStore from '../../../../../settings/SettingsStore' ;
24
24
import { SettingLevel } from "../../../../../settings/SettingLevel" ;
25
25
import { replaceableComponent } from "../../../../../utils/replaceableComponent" ;
26
+ import Field from "../../../elements/Field" ;
27
+ import Modal from '../../../../../Modal' ;
28
+ import * as sdk from '../../../../../index' ;
26
29
27
30
@replaceableComponent ( "views.settings.tabs.room.NotificationsSettingsTab" )
28
31
export default class NotificationsSettingsTab extends React . Component {
@@ -37,38 +40,94 @@ export default class NotificationsSettingsTab extends React.Component {
37
40
38
41
this . state = {
39
42
currentSound : "default" ,
40
- uploadedFile : null ,
43
+ currentSoundReplaced : false ,
44
+ selected : null ,
45
+ soundLibrary : { } ,
41
46
} ;
42
47
}
43
48
44
49
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
45
50
UNSAFE_componentWillMount ( ) { // eslint-disable-line camelcase
46
- const soundData = Notifier . getSoundForRoom ( this . props . roomId ) ;
47
- if ( ! soundData ) {
48
- return ;
49
- }
50
- this . setState ( { currentSound : soundData . name || soundData . url } ) ;
51
+ const soundData = SettingsStore . getValue ( "notificationSound" , this . props . roomId ) ;
52
+ const soundLibrary = SettingsStore . getValue ( "soundLibrary" , null ) ;
53
+ console . log ( soundLibrary ) ;
54
+ const selected = ( soundData === null ) ? "default" : soundData . name ;
55
+ this . setState ( {
56
+ currentSound : soundData . name || soundData . url ,
57
+ selected : selected ,
58
+ soundLibrary : soundLibrary ,
59
+ } ) ;
51
60
}
52
61
53
- async _triggerUploader ( e ) {
54
- e . stopPropagation ( ) ;
55
- e . preventDefault ( ) ;
56
-
62
+ async _triggerUploader ( ) {
57
63
this . _soundUpload . current . click ( ) ;
58
64
}
59
65
60
66
async _onSoundUploadChanged ( e ) {
61
67
if ( ! e . target . files || ! e . target . files . length ) {
62
- this . setState ( {
63
- uploadedFile : null ,
64
- } ) ;
65
68
return ;
66
69
}
67
70
68
71
const file = e . target . files [ 0 ] ;
72
+ let soundLibrary = this . state . soundLibrary ;
73
+
74
+ if ( file . name in soundLibrary ) {
75
+ const QuestionDialog = sdk . getComponent ( 'dialogs.QuestionDialog' ) ;
76
+ Modal . createDialog ( QuestionDialog , {
77
+ title : _t ( "Replace File" ) ,
78
+ description : _t ( "There already exists a file with this name. " +
79
+ "Are you sure, you want to replace it?" ) ,
80
+ button : _t ( "Replace" ) ,
81
+ onFinished : ( ) => {
82
+ this . setState ( { currentSoundReplaced : true } ) ;
83
+ this . _uploadSound ( file ) ;
84
+ } ,
85
+ } ) ;
86
+
87
+ return ;
88
+ }
89
+ this . _uploadSound ( file ) ;
90
+
91
+ }
92
+
93
+ async _uploadSound ( file ) {
94
+
95
+ let type = file . type ;
96
+ if ( type === "video/ogg" ) {
97
+ // XXX: I've observed browsers allowing users to pick a audio/ogg files,
98
+ // and then calling it a video/ogg. This is a lame hack, but man browsers
99
+ // suck at detecting mimetypes.
100
+ type = "audio/ogg" ;
101
+ }
102
+
103
+ const url = await MatrixClientPeg . get ( ) . uploadContent (
104
+ file , {
105
+ type,
106
+ } ,
107
+ ) ;
108
+
109
+ const soundJSON = {
110
+ name : file . name ,
111
+ type : type ,
112
+ size : file . size ,
113
+ url,
114
+ } ;
115
+
116
+ let soundLibrary = this . state . soundLibrary ;
117
+ soundLibrary [ soundJSON . name ] = soundJSON ;
118
+
119
+ await SettingsStore . setValue (
120
+ "soundLibrary" ,
121
+ null ,
122
+ SettingLevel . ACCOUNT ,
123
+ soundLibrary ,
124
+ ) ;
125
+
69
126
this . setState ( {
70
- uploadedFile : file ,
127
+ soundLibrary : soundLibrary ,
128
+ selected : soundJSON . name ,
71
129
} ) ;
130
+
72
131
}
73
132
74
133
async _onClickSaveSound ( e ) {
@@ -86,45 +145,30 @@ export default class NotificationsSettingsTab extends React.Component {
86
145
}
87
146
88
147
async _saveSound ( ) {
89
- if ( ! this . state . uploadedFile ) {
148
+ if ( ! this . state . selected ) {
90
149
return ;
91
150
}
92
151
93
- let type = this . state . uploadedFile . type ;
94
- if ( type === "video/ogg" ) {
95
- // XXX: I've observed browsers allowing users to pick a audio/ogg files,
96
- // and then calling it a video/ogg. This is a lame hack, but man browsers
97
- // suck at detecting mimetypes.
98
- type = "audio/ogg" ;
152
+ if ( this . state . selected == "default" ) {
153
+ this . _clearSound ( ) ;
154
+ return ;
99
155
}
100
156
101
- const url = await MatrixClientPeg . get ( ) . uploadContent (
102
- this . state . uploadedFile , {
103
- type,
104
- } ,
105
- ) ;
157
+ const soundJSON = this . state . soundLibrary [ this . state . selected ] ;
106
158
107
159
await SettingsStore . setValue (
108
160
"notificationSound" ,
109
161
this . props . roomId ,
110
162
SettingLevel . ROOM_ACCOUNT ,
111
- {
112
- name : this . state . uploadedFile . name ,
113
- type : type ,
114
- size : this . state . uploadedFile . size ,
115
- url,
116
- } ,
163
+ soundJSON ,
117
164
) ;
118
165
119
166
this . setState ( {
120
- uploadedFile : null ,
121
- currentSound : this . state . uploadedFile . name ,
167
+ currentSound : soundJSON . name ,
122
168
} ) ;
123
169
}
124
170
125
- _clearSound ( e ) {
126
- e . stopPropagation ( ) ;
127
- e . preventDefault ( ) ;
171
+ _clearSound ( ) {
128
172
SettingsStore . setValue (
129
173
"notificationSound" ,
130
174
this . props . roomId ,
@@ -137,40 +181,60 @@ export default class NotificationsSettingsTab extends React.Component {
137
181
} ) ;
138
182
}
139
183
140
- render ( ) {
141
- let currentUploadedFile = null ;
142
- if ( this . state . uploadedFile ) {
143
- currentUploadedFile = (
144
- < div >
145
- < span > { _t ( "Uploaded sound" ) } : < code > { this . state . uploadedFile . name } </ code > </ span >
146
- </ div >
147
- ) ;
184
+ _onReset ( ) {
185
+ this . setState ( {
186
+ selected : this . state . currentSound ,
187
+ } ) ;
188
+ }
189
+
190
+ _onChangeSelection ( e ) {
191
+ e . stopPropagation ( ) ;
192
+ e . preventDefault ( ) ;
193
+
194
+ if ( e . target . value === "upload" ) {
195
+ this . _triggerUploader ( ) ;
196
+ return ;
148
197
}
149
198
199
+ this . setState ( {
200
+ selected : e . target . value ,
201
+ } ) ;
202
+ }
203
+
204
+
205
+ render ( ) {
206
+
207
+ const notChanged = this . state . currentSound == this . state . selected && ! this . state . currentSoundReplaced ;
208
+
150
209
return (
151
210
< div className = "mx_SettingsTab" >
152
211
< div className = "mx_SettingsTab_heading" > { _t ( "Notifications" ) } </ div >
153
212
< div className = 'mx_SettingsTab_section mx_SettingsTab_subsectionText' >
154
213
< span className = 'mx_SettingsTab_subheading' > { _t ( "Sounds" ) } </ span >
155
214
< div >
156
215
< span > { _t ( "Notification sound" ) } : < code > { this . state . currentSound } </ code > </ span > < br />
157
- < AccessibleButton className = "mx_NotificationSound_resetSound" disabled = { this . state . currentSound == "default" } onClick = { this . _clearSound . bind ( this ) } kind = "primary" >
158
- { _t ( "Reset" ) }
159
- </ AccessibleButton >
160
216
</ div >
161
217
< div >
162
- < h3 > { _t ( "Set a new custom sound" ) } </ h3 >
218
+ < h3 > { _t ( "Select a custom sound" ) } </ h3 >
219
+ < Field
220
+ id = "soundLibrary"
221
+ element = "select"
222
+ label = "custom sound"
223
+ value = { this . state . selected }
224
+ onChange = { this . _onChangeSelection . bind ( this ) }
225
+ >
226
+ < option key = "default" value = "default" > { _t ( "Default" ) } </ option >
227
+ { Object . keys ( this . state . soundLibrary ) . map ( ( sound , i ) => < option key = { i } value = { sound } > { sound } </ option > ) }
228
+ < option key = "uplod" value = "upload" > { _t ( "upload" ) } </ option >
229
+ </ Field >
163
230
< form autoComplete = "off" noValidate = { true } >
164
231
< input ref = { this . _soundUpload } className = "mx_NotificationSound_soundUpload" type = "file" onChange = { this . _onSoundUploadChanged . bind ( this ) } accept = "audio/*" />
165
232
</ form >
166
233
167
- { currentUploadedFile }
168
-
169
- < AccessibleButton className = "mx_NotificationSound_browse" onClick = { this . _triggerUploader . bind ( this ) } kind = "primary" >
170
- { _t ( "Browse" ) }
234
+ < AccessibleButton className = "mx_NotificationSound_resetSound" disabled = { notChanged } onClick = { this . _onReset . bind ( this ) } kind = "primary" >
235
+ { _t ( "Reset" ) }
171
236
</ AccessibleButton >
172
-
173
- < AccessibleButton className = "mx_NotificationSound_save" disabled = { this . state . uploadedFile == null } onClick = { this . _onClickSaveSound . bind ( this ) } kind = "primary" >
237
+ < AccessibleButton className = "mx_NotificationSound_save" disabled = { notChanged } onClick = { this . _onClickSaveSound . bind ( this ) } kind = "primary" >
174
238
{ _t ( "Save" ) }
175
239
</ AccessibleButton >
176
240
< br />
0 commit comments