浏览代码

ref(notifications): convert some dialogs to error or warning notifica… (#1991)

* ref(notifications): convert some dialogs to error or warning notifications

- Expand the configurability of the Notification component so warnings
  and errors can be displayed.
- Allow Notification to take in arbitrary text for the body.
- Rename defaultTitleKey to titleKey for consistency with descriptionKey.

* ref(notifications): remove openReportDialog method

openReportDialog is a wrapper around showError that adds
a logger statement. It is being called in one place only
so remove the method and have that one place call logger.

* ref(notifications): UI.showTrackNotWorkingDialog takes a boolean

Change UI.showTrackNotWorkingDialog so it takes a boolean
arguments instead of the entire track. A small refactor so
the method needs to know less.

* [squash] Fixes eslint errors

* WiP: Fixes desktop sharing error strings and adds support button

* [squash] Fix icons appearances

* [squash] Fix translate titles and messages

* [squash] fix(translation): Fixes incorrect password string

* [squash] fix(recording): Fixes recording message

* [squash] fix(warning): Turns some warnings to errors and makes support link optional.

* [squash] fix(translation): Addressing language comments

* [squash] Fixes jsdoc and formatting

* [squash] fix(noopener): Fixes window.open noopener

* [squash] fix(constants): Extract constants and refactor NotificationWithToggle

* [squash] fix(lang): Fixes camera and mic error titles

* [squash] fix(supportLink): Renames addSupportLink to hideErrorSupportLink
master
virtuacoplenny 7 年前
父节点
当前提交
510334fa7f

+ 15
- 11
conference.js 查看文件

319
             APP.UI.notifyGracefulShutdown();
319
             APP.UI.notifyGracefulShutdown();
320
             break;
320
             break;
321
 
321
 
322
-        case JitsiConferenceErrors.JINGLE_FATAL_ERROR:
323
-            APP.UI.notifyInternalError();
322
+        case JitsiConferenceErrors.JINGLE_FATAL_ERROR: {
323
+            const [ error ] = params;
324
+
325
+            APP.UI.notifyInternalError(error);
324
             break;
326
             break;
327
+        }
325
 
328
 
326
         case JitsiConferenceErrors.CONFERENCE_DESTROYED: {
329
         case JitsiConferenceErrors.CONFERENCE_DESTROYED: {
327
             const [ reason ] = params;
330
             const [ reason ] = params;
1680
         // JitsiTrackErrors.CHROME_EXTENSION_INSTALLATION_ERROR
1683
         // JitsiTrackErrors.CHROME_EXTENSION_INSTALLATION_ERROR
1681
         // JitsiTrackErrors.GENERAL
1684
         // JitsiTrackErrors.GENERAL
1682
         // and any other
1685
         // and any other
1683
-        let dialogTxt;
1684
-        let dialogTitleKey;
1686
+        let descriptionKey;
1687
+        let titleKey;
1685
 
1688
 
1686
         if (error.name === JitsiTrackErrors.PERMISSION_DENIED) {
1689
         if (error.name === JitsiTrackErrors.PERMISSION_DENIED) {
1687
-            dialogTxt = APP.translation.generateTranslationHTML(
1688
-                'dialog.screenSharingPermissionDeniedError');
1689
-            dialogTitleKey = 'dialog.error';
1690
+            descriptionKey = 'dialog.screenSharingPermissionDeniedError';
1691
+            titleKey = 'dialog.screenSharingFailedToInstallTitle';
1690
         } else {
1692
         } else {
1691
-            dialogTxt = APP.translation.generateTranslationHTML(
1692
-                'dialog.failtoinstall');
1693
-            dialogTitleKey = 'dialog.permissionDenied';
1693
+            descriptionKey = 'dialog.screenSharingFailedToInstall';
1694
+            titleKey = 'dialog.screenSharingFailedToInstallTitle';
1694
         }
1695
         }
1695
 
1696
 
1696
-        APP.UI.messageHandler.openDialog(dialogTitleKey, dialogTxt, false);
1697
+        APP.UI.messageHandler.showError({
1698
+            descriptionKey,
1699
+            titleKey
1700
+        });
1697
     },
1701
     },
1698
 
1702
 
1699
     /**
1703
     /**

+ 1
- 1
interface_config.js 查看文件

117
      * If indicated some of the error dialogs may point to the support URL for
117
      * If indicated some of the error dialogs may point to the support URL for
118
      * help.
118
      * help.
119
      */
119
      */
120
-    // SUPPORT_URL: "",
120
+    SUPPORT_URL: 'https://github.com/jitsi/jitsi-meet/issues/new',
121
 
121
 
122
     /**
122
     /**
123
      * Whether the connection indicator icon should hide itself based on
123
      * Whether the connection indicator icon should hide itself based on

+ 41
- 25
lang/main.json 查看文件

226
         "add": "Add",
226
         "add": "Add",
227
         "allow": "Allow",
227
         "allow": "Allow",
228
         "kickMessage": "Ouch! You have been kicked out of the meet!",
228
         "kickMessage": "Ouch! You have been kicked out of the meet!",
229
-        "popupError": "Your browser is blocking popup windows from this site. Please enable popups in your browser's security settings and try again.",
229
+        "popupErrorTitle": "Pop-up blocked",
230
+        "popupError": "Your browser is blocking pop-up windows from this site. Please enable pop-ups in your browser's security settings and try again.",
230
         "passwordErrorTitle": "Password Error",
231
         "passwordErrorTitle": "Password Error",
231
         "passwordError": "This conversation is currently protected by a password. Only the owner of the conference can set a password.",
232
         "passwordError": "This conversation is currently protected by a password. Only the owner of the conference can set a password.",
232
         "passwordError2": "This conversation isn't currently protected by a password. Only the owner of the conference can set a password.",
233
         "passwordError2": "This conversation isn't currently protected by a password. Only the owner of the conference can set a password.",
233
         "connectError": "Oops! Something went wrong and we couldn't connect to the conference.",
234
         "connectError": "Oops! Something went wrong and we couldn't connect to the conference.",
234
         "connectErrorWithMsg": "Oops! Something went wrong and we couldn't connect to the conference: __msg__",
235
         "connectErrorWithMsg": "Oops! Something went wrong and we couldn't connect to the conference: __msg__",
235
-        "incorrectPassword": "Password is incorrect",
236
+        "incorrectPassword": "Incorrect username or password",
236
         "connecting": "Connecting",
237
         "connecting": "Connecting",
237
         "copy": "Copy",
238
         "copy": "Copy",
239
+        "contactSupport": "Contact support",
238
         "error": "Error",
240
         "error": "Error",
239
         "createPassword": "Create password",
241
         "createPassword": "Create password",
240
         "detectext": "Error when trying to detect desktopsharing extension.",
242
         "detectext": "Error when trying to detect desktopsharing extension.",
241
-        "failtoinstall": "Failed to install desktop sharing extension",
242
         "failedpermissions": "Failed to obtain permissions to use the local microphone and/or camera.",
243
         "failedpermissions": "Failed to obtain permissions to use the local microphone and/or camera.",
243
         "conferenceReloadTitle": "Unfortunately, something went wrong.",
244
         "conferenceReloadTitle": "Unfortunately, something went wrong.",
244
         "conferenceReloadMsg": "We're trying to fix this. Reconnecting in __seconds__ sec...",
245
         "conferenceReloadMsg": "We're trying to fix this. Reconnecting in __seconds__ sec...",
245
         "conferenceDisconnectTitle": "You have been disconnected.",
246
         "conferenceDisconnectTitle": "You have been disconnected.",
246
         "conferenceDisconnectMsg": "You may want to check your network connection. Reconnecting in __seconds__ sec...",
247
         "conferenceDisconnectMsg": "You may want to check your network connection. Reconnecting in __seconds__ sec...",
248
+        "dismiss": "Dismiss",
247
         "rejoinNow": "Rejoin now",
249
         "rejoinNow": "Rejoin now",
248
-        "maxUsersLimitReached": "The limit for maximum number of members in the conference has been reached. The conference is full. Please try again later!",
250
+        "maxUsersLimitReachedTitle": "Maximum members limit reached",
251
+        "maxUsersLimitReached": "The limit for maximum number of members has been reached. The conference is full. Please contact the meeting owner or try again later!",
249
         "lockTitle": "Lock failed",
252
         "lockTitle": "Lock failed",
250
         "lockMessage": "Failed to lock the conference.",
253
         "lockMessage": "Failed to lock the conference.",
251
         "warning": "Warning",
254
         "warning": "Warning",
252
-        "passwordNotSupported": "Room passwords are currently not supported.",
255
+        "passwordNotSupportedTitle": "Password not supported",
256
+        "passwordNotSupported": "Setting a meeting password is not supported.",
253
         "internalErrorTitle": "Internal error",
257
         "internalErrorTitle": "Internal error",
254
-        "internalError": "Oups! Something went wrong. The following error occurred: [setRemoteDescription]",
258
+        "internalError": "Oops! Something went wrong. The following error occurred: __error__",
255
         "unableToSwitch": "Unable to switch video stream.",
259
         "unableToSwitch": "Unable to switch video stream.",
256
         "SLDFailure": "Oops! Something went wrong and we failed to mute! (SLD Failure)",
260
         "SLDFailure": "Oops! Something went wrong and we failed to mute! (SLD Failure)",
257
         "SRDFailure": "Oops! Something went wrong and we failed to stop video! (SRD Failure)",
261
         "SRDFailure": "Oops! Something went wrong and we failed to stop video! (SRD Failure)",
268
         "shareVideoLinkError": "Please provide a correct youtube link.",
272
         "shareVideoLinkError": "Please provide a correct youtube link.",
269
         "removeSharedVideoTitle": "Remove shared video",
273
         "removeSharedVideoTitle": "Remove shared video",
270
         "removeSharedVideoMsg": "Are you sure you would like to remove your shared video?",
274
         "removeSharedVideoMsg": "Are you sure you would like to remove your shared video?",
271
-        "alreadySharedVideoMsg": "Another member is already sharing video. This conference allows only one shared video at a time.",
275
+        "alreadySharedVideoMsg": "Another member is already sharing a video. This conference allows only one shared video at a time.",
276
+        "alreadySharedVideoTitle": "Only one shared video is allowed at a time",
272
         "WaitingForHost": "Waiting for the host ...",
277
         "WaitingForHost": "Waiting for the host ...",
273
         "WaitForHostMsg": "The conference <b>__room__ </b> has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.",
278
         "WaitForHostMsg": "The conference <b>__room__ </b> has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.",
274
         "IamHost": "I am the host",
279
         "IamHost": "I am the host",
277
         "retry": "Retry",
282
         "retry": "Retry",
278
         "logoutTitle" : "Logout",
283
         "logoutTitle" : "Logout",
279
         "logoutQuestion" : "Are you sure you want to logout and stop the conference?",
284
         "logoutQuestion" : "Are you sure you want to logout and stop the conference?",
280
-        "sessTerminated": "Session Terminated",
285
+        "sessTerminated": "Call terminated",
281
         "hungUp": "You hung up",
286
         "hungUp": "You hung up",
282
         "joinAgain": "Join again",
287
         "joinAgain": "Join again",
283
         "Share": "Share",
288
         "Share": "Share",
297
         "password": "Enter password",
302
         "password": "Enter password",
298
         "userPassword": "user password",
303
         "userPassword": "user password",
299
         "token": "token",
304
         "token": "token",
300
-        "tokenAuthFailedTitle": "Authentication problem",
305
+        "tokenAuthFailedTitle": "Authentication failed",
301
         "tokenAuthFailed": "Sorry, you're not allowed to join this call.",
306
         "tokenAuthFailed": "Sorry, you're not allowed to join this call.",
302
         "displayNameRequired": "Display name is required",
307
         "displayNameRequired": "Display name is required",
303
         "enterDisplayName": "Please enter your display name",
308
         "enterDisplayName": "Please enter your display name",
317
         "doNotShowWarningAgain": "Don't show this warning again",
322
         "doNotShowWarningAgain": "Don't show this warning again",
318
         "doNotShowMessageAgain": "Don't show this message again",
323
         "doNotShowMessageAgain": "Don't show this message again",
319
         "permissionDenied": "Permission Denied",
324
         "permissionDenied": "Permission Denied",
320
-        "screenSharingPermissionDeniedError": "You have not granted permission to share your screen.",
325
+        "screenSharingFailedToInstall": "Oops! Your screen sharing extension failed to install.",
326
+        "screenSharingFailedToInstallTitle": "Screen sharing extension failed to install",
327
+        "screenSharingPermissionDeniedError": "Oops! Something went wrong with your screen sharing extension permissions. Please reload and try again.",
321
         "micErrorPresent": "There was an error connecting to your microphone.",
328
         "micErrorPresent": "There was an error connecting to your microphone.",
322
         "cameraErrorPresent": "There was an error connecting to your camera.",
329
         "cameraErrorPresent": "There was an error connecting to your camera.",
323
         "cameraUnsupportedResolutionError": "Your camera does not support required video resolution.",
330
         "cameraUnsupportedResolutionError": "Your camera does not support required video resolution.",
329
         "micPermissionDeniedError": "You have not granted permission to use your microphone. You can still join the conference but others won't hear you. Use the camera button in the address bar to fix this.",
336
         "micPermissionDeniedError": "You have not granted permission to use your microphone. You can still join the conference but others won't hear you. Use the camera button in the address bar to fix this.",
330
         "micNotFoundError": "Microphone was not found.",
337
         "micNotFoundError": "Microphone was not found.",
331
         "micConstraintFailedError": "Your microphone does not satisfy some of the required constraints.",
338
         "micConstraintFailedError": "Your microphone does not satisfy some of the required constraints.",
332
-        "micNotSendingData": "We are unable to access your microphone. Please select another device from the settings menu or try to restart the application.",
333
-        "cameraNotSendingData": "We are unable to access your camera. Please check if another application is using this device, select another device from the settings menu or try to restart the application.",
339
+        "micNotSendingDataTitle": "Unable to access microphone",
340
+        "micNotSendingData": "We are unable to access your microphone. Please select another device from the settings menu or try to reload the application.",
341
+        "cameraNotSendingDataTitle": "Unable to access camera",
342
+        "cameraNotSendingData": "We are unable to access your camera. Please check if another application is using this device, select another device from the settings menu or try to reload the application.",
334
         "goToStore": "Go to the webstore",
343
         "goToStore": "Go to the webstore",
335
         "externalInstallationTitle": "Extension required",
344
         "externalInstallationTitle": "Extension required",
336
         "externalInstallationMsg": "You need to install our desktop sharing extension.",
345
         "externalInstallationMsg": "You need to install our desktop sharing extension.",
401
     },
410
     },
402
     "recording":
411
     "recording":
403
     {
412
     {
404
-        "pending": "Recording waiting for a member to join...",
405
-        "on": "Recording",
406
-        "off": "Recording stopped",
407
-        "failedToStart": "Recording failed to start",
413
+        "busy": "We're working on freeing recording resources. Please try again in a few minutes.",
414
+        "busyTitle": "All recorders are currently busy",
408
         "buttonTooltip": "Start / Stop recording",
415
         "buttonTooltip": "Start / Stop recording",
409
         "error": "Recording failed. Please try again.",
416
         "error": "Recording failed. Please try again.",
410
-        "unavailable": "The recording service is currently unavailable. Please try again later."
417
+        "failedToStart": "Recording failed to start",
418
+        "off": "Recording stopped",
419
+        "on": "Recording",
420
+        "pending": "Recording waiting for a member to join...",
421
+        "unavailable": "Oops! The recording service is currently unavailable. We're working on resolving the issue. Please try again later.",
422
+        "unavailableTitle": "Recording unavailable"
411
     },
423
     },
412
     "liveStreaming":
424
     "liveStreaming":
413
     {
425
     {
414
-        "pending": "Starting Live Stream...",
426
+        "busy": "We're working on freeing streaming resources. Please try again in a few minutes.",
427
+        "busyTitle": "All streamers are currently busy",
428
+        "buttonTooltip": "Start / Stop Live Stream",
429
+        "error": "Live Streaming failed. Please try again.",
430
+        "failedToStart": "Live Streaming failed to start",
431
+        "off": "Live Streaming stopped",
415
         "on": "Live Streaming",
432
         "on": "Live Streaming",
416
-        "off": "Live Streaming Stopped",
417
-        "unavailable": "The live streaming service is currently unavailable. Please try again later.",
418
-        "failedToStart": "Live streaming failed to start",
419
-        "buttonTooltip": "Start / Stop live stream",
420
-        "streamIdRequired": "Please fill in the stream id in order to launch the live streaming.",
433
+        "pending": "Starting Live Stream...",
434
+        "streamIdRequired": "Please fill in the stream id in order to launch the Live Streaming.",
421
         "streamIdHelp": "Where do I find this?",
435
         "streamIdHelp": "Where do I find this?",
422
-        "error": "Live streaming failed. Please try again.",
423
-        "busy": "All recorders are currently busy. Please try again later."
436
+        "unavailable": "Oops! The Live Streaming service is currently unavailable. We're working on resolving the issue. Please try again later.",
437
+        "unavailableTitle": "Live Streaming unavailable"
424
     },
438
     },
425
     "speakerStats":
439
     "speakerStats":
426
     {
440
     {
487
         "supportMsg": "If this keeps happening, reach out to"
501
         "supportMsg": "If this keeps happening, reach out to"
488
     },
502
     },
489
     "deviceError": {
503
     "deviceError": {
504
+        "cameraError": "Failed to access your camera",
505
+        "microphoneError": "Failed to access your microphone",
490
         "cameraPermission": "Error obtaining camera permission",
506
         "cameraPermission": "Error obtaining camera permission",
491
         "microphonePermission": "Error obtaining microphone permission"
507
         "microphonePermission": "Error obtaining microphone permission"
492
     },
508
     },

+ 61
- 41
modules/UI/UI.js 查看文件

144
  * Notify user that server has shut down.
144
  * Notify user that server has shut down.
145
  */
145
  */
146
 UI.notifyGracefulShutdown = function() {
146
 UI.notifyGracefulShutdown = function() {
147
-    messageHandler.openMessageDialog(
148
-        'dialog.serviceUnavailable',
149
-        'dialog.gracefulShutdown'
150
-    );
147
+    messageHandler.showError({
148
+        descriptionKey: 'dialog.gracefulShutdown',
149
+        titleKey: 'dialog.serviceUnavailable'
150
+    });
151
 };
151
 };
152
 
152
 
153
 /**
153
 /**
154
  * Notify user that reservation error happened.
154
  * Notify user that reservation error happened.
155
  */
155
  */
156
 UI.notifyReservationError = function(code, msg) {
156
 UI.notifyReservationError = function(code, msg) {
157
-    const message
158
-        = APP.translation.generateTranslationHTML(
159
-            'dialog.reservationErrorMsg',
160
-            {
161
-                code,
162
-                msg
163
-            });
164
-
165
-    messageHandler.openDialog(
166
-        'dialog.reservationError', message, true, {}, () => false);
157
+    messageHandler.showError({
158
+        descriptionArguments: {
159
+            code,
160
+            msg
161
+        },
162
+        descriptionKey: 'dialog.reservationErrorMsg',
163
+        titleKey: 'dialog.reservationError'
164
+    });
167
 };
165
 };
168
 
166
 
169
 /**
167
 /**
170
  * Notify user that he has been kicked from the server.
168
  * Notify user that he has been kicked from the server.
171
  */
169
  */
172
 UI.notifyKicked = function() {
170
 UI.notifyKicked = function() {
173
-    messageHandler.openMessageDialog(
174
-        'dialog.sessTerminated',
175
-        'dialog.kickMessage');
171
+    messageHandler.showError({
172
+        hideErrorSupportLink: true,
173
+        descriptionKey: 'dialog.kickMessage',
174
+        titleKey: 'dialog.sessTerminated'
175
+    });
176
 };
176
 };
177
 
177
 
178
 /**
178
 /**
182
 UI.notifyConferenceDestroyed = function(reason) {
182
 UI.notifyConferenceDestroyed = function(reason) {
183
     // FIXME: use Session Terminated from translation, but
183
     // FIXME: use Session Terminated from translation, but
184
     // 'reason' text comes from XMPP packet and is not translated
184
     // 'reason' text comes from XMPP packet and is not translated
185
-    messageHandler.openDialog(
186
-        'dialog.sessTerminated', reason, true, {}, () => false);
185
+    messageHandler.showError({
186
+        description: reason,
187
+        titleKey: 'dialog.sessTerminated'
188
+    });
187
 };
189
 };
188
 
190
 
189
 /**
191
 /**
833
  * @param {string} stropheErrorMsg raw Strophe error message
835
  * @param {string} stropheErrorMsg raw Strophe error message
834
  */
836
  */
835
 UI.notifyConnectionFailed = function(stropheErrorMsg) {
837
 UI.notifyConnectionFailed = function(stropheErrorMsg) {
836
-    let message;
838
+    let descriptionKey;
839
+    let descriptionArguments;
837
 
840
 
838
     if (stropheErrorMsg) {
841
     if (stropheErrorMsg) {
839
-        message = APP.translation.generateTranslationHTML(
840
-            'dialog.connectErrorWithMsg', { msg: stropheErrorMsg });
842
+        descriptionKey = 'dialog.connectErrorWithMsg';
843
+        descriptionArguments = { msg: stropheErrorMsg };
841
     } else {
844
     } else {
842
-        message = APP.translation.generateTranslationHTML(
843
-            'dialog.connectError');
845
+        descriptionKey = 'dialog.connectError';
844
     }
846
     }
845
 
847
 
846
-    messageHandler.openDialog('dialog.error', message, true, {}, () => false);
848
+    messageHandler.showError({
849
+        descriptionArguments,
850
+        descriptionKey,
851
+        titleKey: 'connection.CONNFAIL'
852
+    });
847
 };
853
 };
848
 
854
 
849
 
855
 
851
  * Notify user that maximum users limit has been reached.
857
  * Notify user that maximum users limit has been reached.
852
  */
858
  */
853
 UI.notifyMaxUsersLimitReached = function() {
859
 UI.notifyMaxUsersLimitReached = function() {
854
-    const message = APP.translation.generateTranslationHTML(
855
-            'dialog.maxUsersLimitReached');
856
-
857
-    messageHandler.openDialog('dialog.error', message, true, {}, () => false);
860
+    messageHandler.showError({
861
+        hideErrorSupportLink: true,
862
+        descriptionKey: 'dialog.maxUsersLimitReached',
863
+        titleKey: 'dialog.maxUsersLimitReachedTitle'
864
+    });
858
 };
865
 };
859
 
866
 
860
 /**
867
 /**
955
 };
962
 };
956
 
963
 
957
 UI.notifyTokenAuthFailed = function() {
964
 UI.notifyTokenAuthFailed = function() {
958
-    messageHandler.showError('dialog.tokenAuthFailedTitle',
959
-                                'dialog.tokenAuthFailed');
965
+    messageHandler.showError({
966
+        descriptionKey: 'dialog.tokenAuthFailed',
967
+        titleKey: 'dialog.tokenAuthFailedTitle'
968
+    });
960
 };
969
 };
961
 
970
 
962
-UI.notifyInternalError = function() {
963
-    messageHandler.showError('dialog.internalErrorTitle',
964
-                                'dialog.internalError');
971
+UI.notifyInternalError = function(error) {
972
+    messageHandler.showError({
973
+        descriptionArguments: { error },
974
+        descriptionKey: 'dialog.internalError',
975
+        titleKey: 'dialog.internalErrorTitle'
976
+    });
965
 };
977
 };
966
 
978
 
967
 UI.notifyFocusDisconnected = function(focus, retrySec) {
979
 UI.notifyFocusDisconnected = function(focus, retrySec) {
1158
             showToggle: Boolean(micJitsiTrackErrorMsg),
1170
             showToggle: Boolean(micJitsiTrackErrorMsg),
1159
             subtitleKey: 'dialog.micErrorPresent',
1171
             subtitleKey: 'dialog.micErrorPresent',
1160
             titleKey: name === JitsiTrackErrors.PERMISSION_DENIED
1172
             titleKey: name === JitsiTrackErrors.PERMISSION_DENIED
1161
-                ? 'deviceError.microphonePermission' : 'dialog.error',
1173
+                ? 'deviceError.microphonePermission'
1174
+                : 'deviceError.microphoneError',
1162
             toggleLabelKey: 'dialog.doNotShowWarningAgain'
1175
             toggleLabelKey: 'dialog.doNotShowWarningAgain'
1163
         }));
1176
         }));
1164
 };
1177
 };
1194
             showToggle: Boolean(cameraJitsiTrackErrorMsg),
1207
             showToggle: Boolean(cameraJitsiTrackErrorMsg),
1195
             subtitleKey: 'dialog.cameraErrorPresent',
1208
             subtitleKey: 'dialog.cameraErrorPresent',
1196
             titleKey: name === JitsiTrackErrors.PERMISSION_DENIED
1209
             titleKey: name === JitsiTrackErrors.PERMISSION_DENIED
1197
-                ? 'deviceError.cameraPermission' : 'dialog.error',
1210
+                ? 'deviceError.cameraPermission' : 'deviceError.cameraError',
1198
             toggleLabelKey: 'dialog.doNotShowWarningAgain'
1211
             toggleLabelKey: 'dialog.doNotShowWarningAgain'
1199
         }));
1212
         }));
1200
 };
1213
 };
1202
 /**
1215
 /**
1203
  * Shows error dialog that informs the user that no data is received from the
1216
  * Shows error dialog that informs the user that no data is received from the
1204
  * device.
1217
  * device.
1218
+ *
1219
+ * @param {boolean} isAudioTrack - Whether or not the dialog is for an audio
1220
+ * track error.
1221
+ * @returns {void}
1205
  */
1222
  */
1206
-UI.showTrackNotWorkingDialog = function(stream) {
1207
-    messageHandler.openMessageDialog(
1208
-        'dialog.error',
1209
-        stream.isAudioTrack() ? 'dialog.micNotSendingData'
1210
-            : 'dialog.cameraNotSendingData');
1223
+UI.showTrackNotWorkingDialog = function(isAudioTrack) {
1224
+    messageHandler.showError({
1225
+        descriptionKey: isAudioTrack
1226
+            ? 'dialog.micNotSendingData' : 'dialog.cameraNotSendingData',
1227
+        titleKey: isAudioTrack
1228
+            ? 'dialog.micNotSendingDataTitle'
1229
+            : 'dialog.cameraNotSendingDataTitle'
1230
+    });
1211
 };
1231
 };
1212
 
1232
 
1213
 UI.updateDevicesAvailability = function(id, devices) {
1233
 UI.updateDevicesAvailability = function(id, devices) {

+ 4
- 1
modules/UI/authentication/LoginDialog.js 查看文件

203
         );
203
         );
204
 
204
 
205
         if (!dialog) {
205
         if (!dialog) {
206
-            APP.UI.messageHandler.openMessageDialog(null, 'dialog.popupError');
206
+            APP.UI.messageHandler.showWarning({
207
+                descriptionKey: 'dialog.popupError',
208
+                titleKey: 'dialog.popupErrorTitle'
209
+            });
207
         }
210
         }
208
 
211
 
209
         return dialog;
212
         return dialog;

+ 15
- 19
modules/UI/recording/Recording.js 查看文件

42
  */
42
  */
43
 export const RECORDING_TRANSLATION_KEYS = {
43
 export const RECORDING_TRANSLATION_KEYS = {
44
     failedToStartKey: 'recording.failedToStart',
44
     failedToStartKey: 'recording.failedToStart',
45
-    recordingBusy: 'liveStreaming.busy',
45
+    recordingBusy: 'recording.busy',
46
+    recordingBusyTitle: 'recording.busyTitle',
46
     recordingButtonTooltip: 'recording.buttonTooltip',
47
     recordingButtonTooltip: 'recording.buttonTooltip',
47
     recordingErrorKey: 'recording.error',
48
     recordingErrorKey: 'recording.error',
48
     recordingOffKey: 'recording.off',
49
     recordingOffKey: 'recording.off',
49
     recordingOnKey: 'recording.on',
50
     recordingOnKey: 'recording.on',
50
     recordingPendingKey: 'recording.pending',
51
     recordingPendingKey: 'recording.pending',
51
     recordingTitle: 'dialog.recording',
52
     recordingTitle: 'dialog.recording',
52
-    recordingUnavailable: 'recording.unavailable'
53
+    recordingUnavailable: 'recording.unavailable',
54
+    recordingUnavailableTitle: 'recording.unavailableTitle'
53
 };
55
 };
54
 
56
 
55
 /**
57
 /**
62
 export const STREAMING_TRANSLATION_KEYS = {
64
 export const STREAMING_TRANSLATION_KEYS = {
63
     failedToStartKey: 'liveStreaming.failedToStart',
65
     failedToStartKey: 'liveStreaming.failedToStart',
64
     recordingBusy: 'liveStreaming.busy',
66
     recordingBusy: 'liveStreaming.busy',
67
+    recordingBusyTitle: 'liveStreaming.busyTitle',
65
     recordingButtonTooltip: 'liveStreaming.buttonTooltip',
68
     recordingButtonTooltip: 'liveStreaming.buttonTooltip',
66
     recordingErrorKey: 'liveStreaming.error',
69
     recordingErrorKey: 'liveStreaming.error',
67
     recordingOffKey: 'liveStreaming.off',
70
     recordingOffKey: 'liveStreaming.off',
68
     recordingOnKey: 'liveStreaming.on',
71
     recordingOnKey: 'liveStreaming.on',
69
     recordingPendingKey: 'liveStreaming.pending',
72
     recordingPendingKey: 'liveStreaming.pending',
70
     recordingTitle: 'dialog.liveStreaming',
73
     recordingTitle: 'dialog.liveStreaming',
71
-    recordingUnavailable: 'liveStreaming.unavailable'
74
+    recordingUnavailable: 'liveStreaming.unavailable',
75
+    recordingUnavailableTitle: 'liveStreaming.unavailableTitle'
72
 };
76
 };
73
 
77
 
74
 /**
78
 /**
513
             break;
517
             break;
514
         }
518
         }
515
         case JitsiRecordingStatus.BUSY: {
519
         case JitsiRecordingStatus.BUSY: {
516
-            dialog = APP.UI.messageHandler.openMessageDialog(
517
-                this.recordingTitle,
518
-                this.recordingBusy,
519
-                null,
520
-                () => {
521
-                    dialog = null;
522
-                }
523
-            );
520
+            APP.UI.messageHandler.showWarning({
521
+                descriptionKey: this.recordingBusy,
522
+                titleKey: this.recordingBusyTitle
523
+            });
524
             break;
524
             break;
525
         }
525
         }
526
         default: {
526
         default: {
527
-            dialog = APP.UI.messageHandler.openMessageDialog(
528
-                this.recordingTitle,
529
-                this.recordingUnavailable,
530
-                null,
531
-                () => {
532
-                    dialog = null;
533
-                }
534
-            );
527
+            APP.UI.messageHandler.showError({
528
+                descriptionKey: this.recordingUnavailable,
529
+                titleKey: this.recordingUnavailableTitle
530
+            });
535
         }
531
         }
536
         }
532
         }
537
     },
533
     },

+ 4
- 8
modules/UI/shared_video/SharedVideo.js 查看文件

111
                 },
111
                 },
112
                 () => {}); // eslint-disable-line no-empty-function
112
                 () => {}); // eslint-disable-line no-empty-function
113
         } else {
113
         } else {
114
-            dialog = APP.UI.messageHandler.openMessageDialog(
115
-                'dialog.shareVideoTitle',
116
-                'dialog.alreadySharedVideoMsg',
117
-                null,
118
-                () => {
119
-                    dialog = null;
120
-                }
121
-            );
114
+            APP.UI.messageHandler.showWarning({
115
+                descriptionKey: 'dialog.alreadySharedVideoMsg',
116
+                titleKey: 'dialog.alreadySharedVideoTitle'
117
+            });
122
             sendAnalyticsEvent('sharedvideo.alreadyshared');
118
             sendAnalyticsEvent('sharedvideo.alreadyshared');
123
         }
119
         }
124
     }
120
     }

+ 15
- 16
modules/UI/util/MessageHandler.js 查看文件

5
 
5
 
6
 import {
6
 import {
7
     Notification,
7
     Notification,
8
-    showNotification
8
+    showErrorNotification,
9
+    showNotification,
10
+    showWarningNotification
9
 } from '../../../react/features/notifications';
11
 } from '../../../react/features/notifications';
10
 
12
 
11
 /**
13
 /**
453
     },
455
     },
454
 
456
 
455
     /**
457
     /**
456
-     * Shows a dialog prompting the user to send an error report.
458
+     * Shows an error dialog to the user.
457
      *
459
      *
458
-     * @param titleKey the title of the message
459
-     * @param msgKey the text of the message
460
-     * @param error the error that is being reported
460
+     * @param {object} props - The properties to pass to the
461
+     * showErrorNotification action.
461
      */
462
      */
462
-    openReportDialog(titleKey, msgKey, error) {
463
-        this.openMessageDialog(titleKey, msgKey);
464
-        logger.log(error);
465
-
466
-        // FIXME send the error to the server
463
+    showError(props) {
464
+        APP.store.dispatch(showErrorNotification(props));
467
     },
465
     },
468
 
466
 
469
     /**
467
     /**
470
-     *  Shows an error dialog to the user.
471
-     * @param titleKey the title of the message.
472
-     * @param msgKey the text of the message.
468
+     * Shows a warning dialog to the user.
469
+     *
470
+     * @param {object} props - The properties to pass to the
471
+     * showWarningNotification action.
473
      */
472
      */
474
-    showError(titleKey = 'dialog.oops', msgKey = 'dialog.defaultError') {
475
-        messageHandler.openMessageDialog(titleKey, msgKey);
473
+    showWarning(props) {
474
+        APP.store.dispatch(showWarningNotification(props));
476
     },
475
     },
477
 
476
 
478
     /**
477
     /**
498
             showNotification(
497
             showNotification(
499
                 Notification,
498
                 Notification,
500
                 {
499
                 {
501
-                    defaultTitleKey: displayNameKey,
502
                     descriptionArguments: messageArguments,
500
                     descriptionArguments: messageArguments,
503
                     descriptionKey: messageKey,
501
                     descriptionKey: messageKey,
502
+                    titleKey: displayNameKey,
504
                     title: displayName
503
                     title: displayName
505
                 },
504
                 },
506
                 timeout));
505
                 timeout));

+ 2
- 1
react/features/base/tracks/functions.js 查看文件

75
                 tracks.forEach(track =>
75
                 tracks.forEach(track =>
76
                     track.on(
76
                     track.on(
77
                         JitsiTrackEvents.NO_DATA_FROM_SOURCE,
77
                         JitsiTrackEvents.NO_DATA_FROM_SOURCE,
78
-                        APP.UI.showTrackNotWorkingDialog.bind(null, track)));
78
+                        APP.UI.showTrackNotWorkingDialog.bind(
79
+                            null, track.isAudioTrack())));
79
             }
80
             }
80
 
81
 
81
             return tracks;
82
             return tracks;

+ 6
- 4
react/features/conference/route.js 查看文件

77
                     _initConference();
77
                     _initConference();
78
                 })
78
                 })
79
                 .catch(err => {
79
                 .catch(err => {
80
+                    logger.log(err);
81
+
80
                     // Show obtain config error.
82
                     // Show obtain config error.
81
-                    APP.UI.messageHandler.openReportDialog(
82
-                        null,
83
-                        'dialog.connectError',
84
-                        err);
83
+                    APP.UI.messageHandler.showError({
84
+                        titleKey: 'connection.CONNFAIL',
85
+                        descriptionKey: 'dialog.connectError'
86
+                    });
85
                 });
87
                 });
86
         } else {
88
         } else {
87
             chooseBOSHAddress(config, room);
89
             chooseBOSHAddress(config, room);

+ 32
- 1
react/features/notifications/actions.js 查看文件

5
     SET_NOTIFICATIONS_ENABLED,
5
     SET_NOTIFICATIONS_ENABLED,
6
     SHOW_NOTIFICATION
6
     SHOW_NOTIFICATION
7
 } from './actionTypes';
7
 } from './actionTypes';
8
-import { NotificationWithToggle } from './components';
8
+import {
9
+    Notification,
10
+    NotificationWithToggle
11
+} from './components';
12
+
13
+import { NOTIFICATION_TYPE } from './constants';
9
 
14
 
10
 /**
15
 /**
11
  * Removes the notification with the passed in id.
16
  * Removes the notification with the passed in id.
40
     };
45
     };
41
 }
46
 }
42
 
47
 
48
+/**
49
+ * Queues an error notification for display.
50
+ *
51
+ * @param {Object} props - The props needed to show the notification component.
52
+ * @returns {Object}
53
+ */
54
+export function showErrorNotification(props) {
55
+    return showNotification(Notification, {
56
+        ...props,
57
+        appearance: NOTIFICATION_TYPE.ERROR
58
+    });
59
+}
60
+
43
 /**
61
 /**
44
  * Queues a notification for display.
62
  * Queues a notification for display.
45
  *
63
  *
66
     };
84
     };
67
 }
85
 }
68
 
86
 
87
+/**
88
+ * Queues a warning notification for display.
89
+ *
90
+ * @param {Object} props - The props needed to show the notification component.
91
+ * @returns {Object}
92
+ */
93
+export function showWarningNotification(props) {
94
+    return showNotification(Notification, {
95
+        ...props,
96
+        appearance: NOTIFICATION_TYPE.WARNING
97
+    });
98
+}
99
+
69
 /**
100
 /**
70
  * Displays a notification unless the passed in persistenceKey value exists in
101
  * Displays a notification unless the passed in persistenceKey value exists in
71
  * local storage and has been set to "true".
102
  * local storage and has been set to "true".

+ 178
- 12
react/features/notifications/components/Notification.web.js 查看文件

1
 import Flag from '@atlaskit/flag';
1
 import Flag from '@atlaskit/flag';
2
 import EditorInfoIcon from '@atlaskit/icon/glyph/editor/info';
2
 import EditorInfoIcon from '@atlaskit/icon/glyph/editor/info';
3
 import PropTypes from 'prop-types';
3
 import PropTypes from 'prop-types';
4
+import ErrorIcon from '@atlaskit/icon/glyph/error';
5
+import WarningIcon from '@atlaskit/icon/glyph/warning';
6
+import { colors } from '@atlaskit/theme';
4
 import React, { Component } from 'react';
7
 import React, { Component } from 'react';
5
 
8
 
6
 import { translate } from '../../base/i18n';
9
 import { translate } from '../../base/i18n';
7
 
10
 
11
+import { NOTIFICATION_TYPE } from '../constants';
12
+
13
+declare var interfaceConfig: Object;
14
+
15
+/**
16
+ * Secondary colors for notification icons.
17
+ *
18
+ * @type {{error, info, normal, success, warning}}
19
+ */
20
+const ICON_COLOR = {
21
+    error: colors.R400,
22
+    info: colors.N500,
23
+    normal: colors.N0,
24
+    success: colors.G400,
25
+    warning: colors.Y200
26
+};
27
+
8
 /**
28
 /**
9
  * Implements a React {@link Component} to display a notification.
29
  * Implements a React {@link Component} to display a notification.
10
  *
30
  *
11
  * @extends Component
31
  * @extends Component
12
  */
32
  */
13
 class Notification extends Component {
33
 class Notification extends Component {
34
+    /**
35
+     * Default values for {@code Notification} component's properties.
36
+     *
37
+     * @static
38
+     */
39
+    static defaultProps = {
40
+        appearance: NOTIFICATION_TYPE.NORMAL
41
+    };
42
+
14
     /**
43
     /**
15
      * {@code Notification} component's property types.
44
      * {@code Notification} component's property types.
16
      *
45
      *
18
      */
47
      */
19
     static propTypes = {
48
     static propTypes = {
20
         /**
49
         /**
21
-         * The translation key to display as the title of the notification if
22
-         * no title is provided.
50
+         * Display appearance for the component, passed directly to
51
+         * {@code Flag}.
52
+         */
53
+        appearance: PropTypes.string,
54
+
55
+        /**
56
+         * The text to display in the body of the notification. If not passed
57
+         * in, the passed in descriptionKey will be used.
23
          */
58
          */
24
         defaultTitleKey: PropTypes.string,
59
         defaultTitleKey: PropTypes.string,
25
 
60
 
61
+        /**
62
+         * The description string.
63
+         */
64
+        description: PropTypes.string,
65
+
26
         /**
66
         /**
27
          * The translation arguments that may be necessary for the description.
67
          * The translation arguments that may be necessary for the description.
28
          */
68
          */
33
          */
73
          */
34
         descriptionKey: PropTypes.string,
74
         descriptionKey: PropTypes.string,
35
 
75
 
76
+        /**
77
+         * Whether the support link should be hidden in the case of an error
78
+         * message.
79
+         */
80
+        hideErrorSupportLink: PropTypes.bool,
81
+
36
         /**
82
         /**
37
          * Whether or not the dismiss button should be displayed. This is passed
83
          * Whether or not the dismiss button should be displayed. This is passed
38
          * in by {@code FlagGroup}.
84
          * in by {@code FlagGroup}.
52
 
98
 
53
         /**
99
         /**
54
          * The text to display at the top of the notification. If not passed in,
100
          * The text to display at the top of the notification. If not passed in,
55
-         * the passed in defaultTitleKey will be used.
101
+         * the passed in titleKey will be used.
56
          */
102
          */
57
         title: PropTypes.string,
103
         title: PropTypes.string,
58
 
104
 
105
+        /**
106
+         * The translation key to display as the title of the notification if
107
+         * no title is provided.
108
+         */
109
+        titleKey: PropTypes.string,
110
+
59
         /**
111
         /**
60
          * The unique identifier for the notification. Passed back by the
112
          * The unique identifier for the notification. Passed back by the
61
          * {@code Flag} component in the onDismissed callback.
113
          * {@code Flag} component in the onDismissed callback.
63
         uid: PropTypes.number
115
         uid: PropTypes.number
64
     };
116
     };
65
 
117
 
118
+    /**
119
+     * Initializes a new {@code Notification} instance.
120
+     *
121
+     * @param {Object} props - The read-only properties with which the new
122
+     * instance is to be initialized.
123
+     */
124
+    constructor(props) {
125
+        super(props);
126
+
127
+        // Bind event handler so it is only bound once for every instance.
128
+        this._onDismissed = this._onDismissed.bind(this);
129
+    }
130
+
66
     /**
131
     /**
67
      * Implements React's {@link Component#render()}.
132
      * Implements React's {@link Component#render()}.
68
      *
133
      *
71
      */
136
      */
72
     render() {
137
     render() {
73
         const {
138
         const {
74
-            defaultTitleKey,
139
+            hideErrorSupportLink,
140
+            appearance,
141
+            titleKey,
75
             descriptionArguments,
142
             descriptionArguments,
76
             descriptionKey,
143
             descriptionKey,
144
+            description,
77
             isDismissAllowed,
145
             isDismissAllowed,
78
             onDismissed,
146
             onDismissed,
79
             t,
147
             t,
83
 
151
 
84
         return (
152
         return (
85
             <Flag
153
             <Flag
86
-                appearance = 'normal'
87
-                description = { t(descriptionKey, descriptionArguments) }
88
-                icon = { (
89
-                    <EditorInfoIcon
90
-                        label = 'info'
91
-                        size = 'medium' />
92
-                ) }
154
+                actions = { this._mapAppearanceToButtons(hideErrorSupportLink) }
155
+                appearance = { appearance }
156
+                description = { description
157
+                    || t(descriptionKey, descriptionArguments) }
158
+                icon = { this._mapAppearanceToIcon() }
93
                 id = { uid }
159
                 id = { uid }
94
                 isDismissAllowed = { isDismissAllowed }
160
                 isDismissAllowed = { isDismissAllowed }
95
                 onDismissed = { onDismissed }
161
                 onDismissed = { onDismissed }
96
-                title = { title || t(defaultTitleKey) } />
162
+                title = { title || t(titleKey) } />
97
         );
163
         );
98
     }
164
     }
165
+
166
+    /**
167
+     * Calls back into {@code FlagGroup} to dismiss the notification.
168
+     *
169
+     * @private
170
+     * @returns {void}
171
+     */
172
+    _onDismissed() {
173
+        this.props.onDismissed(this.props.uid);
174
+    }
175
+
176
+    /**
177
+     * Opens the support page.
178
+     *
179
+     * @returns {void}
180
+     * @private
181
+     */
182
+    _onOpenSupportLink() {
183
+        window.open(interfaceConfig.SUPPORT_URL, '_blank', 'noopener');
184
+    }
185
+
186
+    /**
187
+     * Creates action button configurations for the notification based on
188
+     * notification appearance.
189
+     *
190
+     * @param {boolean} hideErrorSupportLink - Indicates if the support link
191
+     * should be hidden in the error messages.
192
+     * @private
193
+     * @returns {Object[]}
194
+     */
195
+    _mapAppearanceToButtons(hideErrorSupportLink) {
196
+        switch (this.props.appearance) {
197
+        case NOTIFICATION_TYPE.ERROR: {
198
+            const buttons = [
199
+                {
200
+                    content: this.props.t('dialog.dismiss'),
201
+                    onClick: this._onDismissed
202
+                }
203
+            ];
204
+
205
+            if (!hideErrorSupportLink) {
206
+                buttons.push({
207
+                    content: this.props.t('dialog.contactSupport'),
208
+                    onClick: this._onOpenSupportLink
209
+                });
210
+            }
211
+
212
+            return buttons;
213
+        }
214
+        case NOTIFICATION_TYPE.WARNING:
215
+            return [
216
+                {
217
+                    content: this.props.t('dialog.Ok'),
218
+                    onClick: this._onDismissed
219
+                }
220
+            ];
221
+
222
+        default:
223
+            return [];
224
+        }
225
+    }
226
+
227
+    /**
228
+     * Creates an icon component depending on the configured notification
229
+     * appearance.
230
+     *
231
+     * @private
232
+     * @returns {ReactElement}
233
+     */
234
+    _mapAppearanceToIcon() {
235
+        const appearance = this.props.appearance;
236
+        const secIconColor = ICON_COLOR[this.props.appearance];
237
+        const iconSize = 'medium';
238
+
239
+        switch (appearance) {
240
+        case NOTIFICATION_TYPE.ERROR:
241
+            return (
242
+                <ErrorIcon
243
+                    label = { appearance }
244
+                    secondaryColor = { secIconColor }
245
+                    size = { iconSize } />
246
+            );
247
+
248
+        case NOTIFICATION_TYPE.WARNING:
249
+            return (
250
+                <WarningIcon
251
+                    label = { appearance }
252
+                    secondaryColor = { secIconColor }
253
+                    size = { iconSize } />
254
+            );
255
+
256
+        default:
257
+            return (
258
+                <EditorInfoIcon
259
+                    label = { appearance }
260
+                    secondaryColor = { secIconColor }
261
+                    size = { iconSize } />
262
+            );
263
+        }
264
+    }
99
 }
265
 }
100
 
266
 
101
 export default translate(Notification);
267
 export default translate(Notification);

+ 12
- 63
react/features/notifications/components/NotificationWithToggle.web.js 查看文件

1
-import Flag from '@atlaskit/flag';
2
-import WarningIcon from '@atlaskit/icon/glyph/warning';
3
 import { ToggleStateless } from '@atlaskit/toggle';
1
 import { ToggleStateless } from '@atlaskit/toggle';
4
 import PropTypes from 'prop-types';
2
 import PropTypes from 'prop-types';
5
 import React, { Component } from 'react';
3
 import React, { Component } from 'react';
6
 
4
 
7
 import { translate } from '../../base/i18n';
5
 import { translate } from '../../base/i18n';
8
 
6
 
7
+import { default as Notification } from './Notification';
8
+import { NOTIFICATION_TYPE } from '../constants';
9
+
9
 /**
10
 /**
10
  * React {@code Component} for displaying a notification with a toggle element.
11
  * React {@code Component} for displaying a notification with a toggle element.
11
  *
12
  *
18
      * @static
19
      * @static
19
      */
20
      */
20
     static propTypes = {
21
     static propTypes = {
22
+        ...Notification.propTypes,
23
+
21
         /**
24
         /**
22
          * Any additional text to display at the end of the notification message
25
          * Any additional text to display at the end of the notification message
23
          * body.
26
          * body.
24
          */
27
          */
25
         additionalMessage: PropTypes.string,
28
         additionalMessage: PropTypes.string,
26
 
29
 
27
-        /**
28
-         * Whether or not the dismiss button should be displayed. This is passed
29
-         * in by {@code FlagGroup}.
30
-         */
31
-        isDismissAllowed: PropTypes.bool,
32
-
33
-        /**
34
-         * The translation key to be used as the main body of the notification.
35
-         */
36
-        messageKey: PropTypes.string,
37
-
38
-        /**
39
-         * Callback invoked when the user clicks to dismiss the notification.
40
-         * This is passed in by {@code FlagGroup}.
41
-         */
42
-        onDismissed: PropTypes.func,
43
-
44
         /**
30
         /**
45
          * Optional callback to invoke when the notification is dismissed. The
31
          * Optional callback to invoke when the notification is dismissed. The
46
          * current value of the toggle element will be passed in.
32
          * current value of the toggle element will be passed in.
58
          */
44
          */
59
         subtitleKey: PropTypes.string,
45
         subtitleKey: PropTypes.string,
60
 
46
 
61
-        /**
62
-         * Invoked to obtain translated strings.
63
-         */
64
-        t: PropTypes.func,
65
-
66
-        /**
67
-         * The translation key to be used as the title of the notification.
68
-         */
69
-        titleKey: PropTypes.string,
70
-
71
         /*
47
         /*
72
          * The translation key to be used as a label describing what setting the
48
          * The translation key to be used as a label describing what setting the
73
          * toggle will change.
49
          * toggle will change.
74
          */
50
          */
75
-        toggleLabelKey: PropTypes.string,
76
-
77
-        /**
78
-         * The unique identifier for the notification. Passed back by the
79
-         * {@code Flag} component in the onDismissed callback.
80
-         */
81
-        uid: PropTypes.number
51
+        toggleLabelKey: PropTypes.string
82
     };
52
     };
83
 
53
 
84
     /**
54
     /**
111
      * @returns {ReactElement}
81
      * @returns {ReactElement}
112
      */
82
      */
113
     render() {
83
     render() {
114
-        const {
115
-            isDismissAllowed,
116
-            t,
117
-            titleKey,
118
-            uid
119
-        } = this.props;
120
-
121
         return (
84
         return (
122
-            <Flag
123
-                actions = { [
124
-                    {
125
-                        content: t('dialog.Ok'),
126
-                        onClick: this._onDismissed
127
-                    }
128
-                ] }
129
-                appearance = 'warning'
130
-                description = { this._renderDescription() }
131
-                icon = { (
132
-                    <WarningIcon
133
-                        label = 'Warning'
134
-                        size = 'medium' />
135
-                ) }
136
-                id = { uid }
137
-                isDismissAllowed = { isDismissAllowed }
138
-                onDismissed = { this._onDismissed }
139
-                title = { t(titleKey) } />
85
+            <Notification
86
+                appearance = { NOTIFICATION_TYPE.WARNING }
87
+                { ...this.props }
88
+                description = { this._renderDescription() } />
140
         );
89
         );
141
     }
90
     }
142
 
91
 
181
     _renderDescription() {
130
     _renderDescription() {
182
         const {
131
         const {
183
             additionalMessage,
132
             additionalMessage,
184
-            messageKey,
133
+            descriptionKey,
185
             showToggle,
134
             showToggle,
186
             subtitleKey,
135
             subtitleKey,
187
             t,
136
             t,
191
         return (
140
         return (
192
             <div className = 'notification-with-toggle'>
141
             <div className = 'notification-with-toggle'>
193
                 <div>{ t(subtitleKey) }</div>
142
                 <div>{ t(subtitleKey) }</div>
194
-                { messageKey ? <div>{ t(messageKey) }</div> : null }
143
+                { descriptionKey ? <div>{ t(descriptionKey) }</div> : null }
195
                 { additionalMessage ? <div>{ additionalMessage }</div>
144
                 { additionalMessage ? <div>{ additionalMessage }</div>
196
                     : null }
145
                     : null }
197
                 { showToggle
146
                 { showToggle

+ 12
- 0
react/features/notifications/constants.js 查看文件

1
+/**
2
+ * The set of possible notification types.
3
+ *
4
+ * @enum {string}
5
+ */
6
+export const NOTIFICATION_TYPE = {
7
+    ERROR: 'error',
8
+    INFO: 'info',
9
+    NORMAL: 'normal',
10
+    SUCCESS: 'success',
11
+    WARNING: 'warning'
12
+};

+ 10
- 7
react/features/room-lock/middleware.js 查看文件

75
         // TODO Remove this logic when displaying of error messages on web is
75
         // TODO Remove this logic when displaying of error messages on web is
76
         // handled through react/redux.
76
         // handled through react/redux.
77
         const { error } = action;
77
         const { error } = action;
78
-        let title;
79
-        let message;
78
+        let descriptionKey;
79
+        let titleKey;
80
 
80
 
81
         if (error === JitsiConferenceErrors.PASSWORD_NOT_SUPPORTED) {
81
         if (error === JitsiConferenceErrors.PASSWORD_NOT_SUPPORTED) {
82
             logger.warn('room passwords not supported');
82
             logger.warn('room passwords not supported');
83
-            title = 'dialog.warning';
84
-            message = 'dialog.passwordNotSupported';
83
+            descriptionKey = 'dialog.passwordNotSupported';
84
+            titleKey = 'dialog.passwordNotSupportedTitle';
85
         } else {
85
         } else {
86
             logger.warn('setting password failed', error);
86
             logger.warn('setting password failed', error);
87
-            title = 'dialog.lockTitle';
88
-            message = 'dialog.lockMessage';
87
+            descriptionKey = 'dialog.lockMessage';
88
+            titleKey = 'dialog.lockTitle';
89
         }
89
         }
90
-        APP.UI.messageHandler.showError(title, message);
90
+        APP.UI.messageHandler.showError({
91
+            descriptionKey,
92
+            titleKey
93
+        });
91
     }
94
     }
92
 
95
 
93
     return next(action);
96
     return next(action);

正在加载...
取消
保存