diff --git a/res/css/views/rooms/_VoiceRecordComposerTile.scss b/res/css/views/rooms/_VoiceRecordComposerTile.scss index 8c13018c512..5d7e7332134 100644 --- a/res/css/views/rooms/_VoiceRecordComposerTile.scss +++ b/res/css/views/rooms/_VoiceRecordComposerTile.scss @@ -46,6 +46,21 @@ limitations under the License. mask-image: url('$(res)/img/element-icons/trashcan.svg'); } +.mx_VoiceRecordComposerTile_uploadingState { + margin-right: 10px; + color: $secondary-fg-color; +} + +.mx_VoiceRecordComposerTile_failedState { + margin-right: 21px; + + .mx_VoiceRecordComposerTile_uploadState_badge { + display: inline-block; + margin-right: 4px; + vertical-align: middle; + } +} + .mx_MessageComposer_row .mx_VoiceMessagePrimaryContainer { // Note: remaining class properties are in the PlayerContainer CSS. diff --git a/src/components/views/rooms/VoiceRecordComposerTile.tsx b/src/components/views/rooms/VoiceRecordComposerTile.tsx index 7949f417845..019e39d4156 100644 --- a/src/components/views/rooms/VoiceRecordComposerTile.tsx +++ b/src/components/views/rooms/VoiceRecordComposerTile.tsx @@ -17,10 +17,7 @@ limitations under the License. import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import { _t } from "../../../languageHandler"; import React, { ReactNode } from "react"; -import { - RecordingState, - VoiceRecording, -} from "../../../audio/VoiceRecording"; +import { IUpload, RecordingState, VoiceRecording } from "../../../audio/VoiceRecording"; import { Room } from "matrix-js-sdk/src/models/room"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import classNames from "classnames"; @@ -34,6 +31,10 @@ import { MsgType } from "matrix-js-sdk/src/@types/event"; import Modal from "../../../Modal"; import ErrorDialog from "../dialogs/ErrorDialog"; import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../../MediaDeviceHandler"; +import NotificationBadge from "./NotificationBadge"; +import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState"; +import { NotificationColor } from "../../../stores/notifications/NotificationColor"; +import InlineSpinner from "../elements/InlineSpinner"; interface IProps { room: Room; @@ -42,6 +43,7 @@ interface IProps { interface IState { recorder?: VoiceRecording; recordingPhase?: RecordingState; + didUploadFail?: boolean; } /** @@ -69,9 +71,19 @@ export default class VoiceRecordComposerTile extends React.PureComponent { @@ -234,7 +245,25 @@ export default class VoiceRecordComposerTile extends React.PureComponent; } + let uploadIndicator; + if (this.state.recordingPhase === RecordingState.Uploading) { + uploadIndicator = + + ; + } else if (this.state.didUploadFail && this.state.recordingPhase === RecordingState.Ended) { + uploadIndicator = + + { /* Need to stick the badge in a span to ensure it doesn't create a block component */ } + + + { _t("Failed to send") } + ; + } + return (<> + { uploadIndicator } { deleteButton } { this.renderWaveformArea() } { recordingInfo } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 51f015d98ca..b1846f5ae9d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1697,7 +1697,6 @@ "Invited by %(sender)s": "Invited by %(sender)s", "Jump to first unread message.": "Jump to first unread message.", "Mark all as read": "Mark all as read", - "The voice message failed to upload.": "The voice message failed to upload.", "Unable to access your microphone": "Unable to access your microphone", "We were unable to access your microphone. Please check your browser settings and try again.": "We were unable to access your microphone. Please check your browser settings and try again.", "No microphone found": "No microphone found",