New UI for audio playback and global audio player
Introduce new UI and behavior for playing audio attachments in conversations. Previously, playback stopped unexpectedly during window resizes and scrolling through the messages due to the row height recomputation in `react-virtualized`. With this commit we introduce `<GlobalAudioContext/>` instance that wraps whole conversation and provides an `<audio/>` element that doesn't get re-rendered (or destroyed) whenever `react-virtualized` recomputes messages. The audio players (with a freshly designed UI) now share this global `<audio/>` instance and manage access to it using `audioPlayer.owner` state from the redux. New UI computes on the fly, caches, and displays waveforms for each audio attachment. Storybook had to be slightly modified to accomodate testing of Android bubbles by introducing the new knob for `authorColor`.
This commit is contained in:
parent
1ca4960924
commit
12d7f24d0f
30 changed files with 1176 additions and 102 deletions
|
@ -79,6 +79,17 @@ export type DirectionType = typeof Directions[number];
|
|||
export const ConversationTypes = ['direct', 'group'] as const;
|
||||
export type ConversationTypesType = typeof ConversationTypes[number];
|
||||
|
||||
export type AudioAttachmentProps = {
|
||||
id: string;
|
||||
i18n: LocalizerType;
|
||||
buttonRef: React.RefObject<HTMLButtonElement>;
|
||||
direction: DirectionType;
|
||||
theme: ThemeType | undefined;
|
||||
url: string;
|
||||
withContentAbove: boolean;
|
||||
withContentBelow: boolean;
|
||||
};
|
||||
|
||||
export type PropsData = {
|
||||
id: string;
|
||||
conversationId: string;
|
||||
|
@ -136,6 +147,8 @@ export type PropsData = {
|
|||
isBlocked: boolean;
|
||||
isMessageRequestAccepted: boolean;
|
||||
bodyRanges?: BodyRangesType;
|
||||
|
||||
renderAudioAttachment: (props: AudioAttachmentProps) => JSX.Element;
|
||||
};
|
||||
|
||||
export type PropsHousekeeping = {
|
||||
|
@ -219,10 +232,10 @@ const EXPIRED_DELAY = 600;
|
|||
export class Message extends React.PureComponent<Props, State> {
|
||||
public menuTriggerRef: Trigger | undefined;
|
||||
|
||||
public audioRef: React.RefObject<HTMLAudioElement> = React.createRef();
|
||||
|
||||
public focusRef: React.RefObject<HTMLDivElement> = React.createRef();
|
||||
|
||||
public audioButtonRef: React.RefObject<HTMLButtonElement> = React.createRef();
|
||||
|
||||
public reactionsContainerRef: React.RefObject<
|
||||
HTMLDivElement
|
||||
> = React.createRef();
|
||||
|
@ -676,6 +689,8 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
isSticker,
|
||||
text,
|
||||
theme,
|
||||
|
||||
renderAudioAttachment,
|
||||
} = this.props;
|
||||
|
||||
const { imageBroken } = this.state;
|
||||
|
@ -740,24 +755,16 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
);
|
||||
}
|
||||
if (!firstAttachment.pending && isAudio(attachments)) {
|
||||
return (
|
||||
<audio
|
||||
ref={this.audioRef}
|
||||
controls
|
||||
className={classNames(
|
||||
'module-message__audio-attachment',
|
||||
withContentBelow
|
||||
? 'module-message__audio-attachment--with-content-below'
|
||||
: null,
|
||||
withContentAbove
|
||||
? 'module-message__audio-attachment--with-content-above'
|
||||
: null
|
||||
)}
|
||||
key={firstAttachment.url}
|
||||
>
|
||||
<source src={firstAttachment.url} />
|
||||
</audio>
|
||||
);
|
||||
return renderAudioAttachment({
|
||||
i18n,
|
||||
buttonRef: this.audioButtonRef,
|
||||
id,
|
||||
direction,
|
||||
theme,
|
||||
url: firstAttachment.url,
|
||||
withContentAbove,
|
||||
withContentBelow,
|
||||
});
|
||||
}
|
||||
const { pending, fileName, fileSize, contentType } = firstAttachment;
|
||||
const extension = getExtensionForDisplay({ contentType, fileName });
|
||||
|
@ -2043,17 +2050,13 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
if (
|
||||
!isAttachmentPending &&
|
||||
isAudio(attachments) &&
|
||||
this.audioRef &&
|
||||
this.audioRef.current
|
||||
this.audioButtonRef &&
|
||||
this.audioButtonRef.current
|
||||
) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (this.audioRef.current.paused) {
|
||||
this.audioRef.current.play();
|
||||
} else {
|
||||
this.audioRef.current.pause();
|
||||
}
|
||||
this.audioButtonRef.current.click();
|
||||
}
|
||||
|
||||
if (contact && contact.signalAccount) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue