fix audio message autoplay as broken with perf improvements

fix bug making "trust contact" first audio message being played on click
to trust
This commit is contained in:
Audric Ackermann 2021-08-02 11:53:37 +10:00
parent 57aa8cba69
commit 2c174bdac7
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4
8 changed files with 69 additions and 81 deletions

View File

@ -1,46 +1,83 @@
// Audio Player
import React, { useEffect, useRef, useState } from 'react';
import H5AudioPlayer, { RHAP_UI } from 'react-h5-audio-player';
import { useDispatch, useSelector } from 'react-redux';
import { useTheme } from 'styled-components';
import { useEncryptedFileFetch } from '../../hooks/useEncryptedFileFetch';
import { setNextMessageToPlayId } from '../../state/ducks/conversations';
import {
getNextMessageToPlayId,
getSortedMessagesOfSelectedConversation,
} from '../../state/selectors/conversations';
import { getAudioAutoplay } from '../../state/selectors/userConfig';
import { SessionIcon, SessionIconSize, SessionIconType } from '../session/icon';
import { SessionButton, SessionButtonColor, SessionButtonType } from '../session/SessionButton';
export const AudioPlayerWithEncryptedFile = (props: {
src: string;
contentType: string;
playNextMessage?: (index: number) => void;
playableMessageIndex?: number;
nextMessageToPlay?: number;
messageId: string;
}) => {
const theme = useTheme();
const dispatch = useDispatch();
const [playbackSpeed, setPlaybackSpeed] = useState(1.0);
const { urlToLoad } = useEncryptedFileFetch(props.src, props.contentType);
const player = useRef<H5AudioPlayer | null>(null);
const autoPlaySetting = useSelector(getAudioAutoplay);
const messageProps = useSelector(getSortedMessagesOfSelectedConversation);
const nextMessageToPlayId = useSelector(getNextMessageToPlayId);
useEffect(() => {
// updates playback speed to value selected in context menu
if (player.current?.audio.current?.playbackRate) {
player.current.audio.current.playbackRate = playbackSpeed;
}
}, [playbackSpeed]);
}, [playbackSpeed, player]);
useEffect(() => {
if (props.playableMessageIndex === props.nextMessageToPlay) {
if (props.messageId === nextMessageToPlayId) {
player.current?.audio.current?.play();
}
});
}, [props.messageId, nextMessageToPlayId, player]);
const triggerPlayNextMessageIfNeeded = (endedMessageId: string) => {
const justEndedMessageIndex = messageProps.findIndex(
m => m.propsForMessage.id === endedMessageId
);
if (justEndedMessageIndex === -1) {
// make sure that even with switching convo or stuff, the next message to play is unset
dispatch(setNextMessageToPlayId(undefined));
return;
}
const isLastMessage = justEndedMessageIndex === 0;
// to prevent autoplaying as soon as a message is received.
if (isLastMessage) {
dispatch(setNextMessageToPlayId(undefined));
return;
}
// justEndedMessageIndex cannot be -1 nor 0, so it is >= 1
const nextMessageIndex = justEndedMessageIndex - 1;
// stop auto-playing when the audio messages change author.
const prevAuthorNumber = messageProps[justEndedMessageIndex].propsForMessage.authorPhoneNumber;
const nextAuthorNumber = messageProps[nextMessageIndex].propsForMessage.authorPhoneNumber;
const differentAuthor = prevAuthorNumber !== nextAuthorNumber;
if (differentAuthor) {
dispatch(setNextMessageToPlayId(undefined));
} else {
dispatch(setNextMessageToPlayId(messageProps[nextMessageIndex].propsForMessage.id));
}
};
const onEnded = () => {
// if audio autoplay is enabled, call method to start playing
// the next playable message
if (
window.inboxStore?.getState().userConfig.audioAutoplay === true &&
props.playNextMessage &&
props.playableMessageIndex !== undefined
) {
props.playNextMessage(props.playableMessageIndex);
if (autoPlaySetting === true && props.messageId) {
triggerPlayNextMessageIfNeeded(props.messageId);
}
};

View File

@ -268,9 +268,7 @@ class MessageInner extends React.PureComponent<Props, State> {
<AudioPlayerWithEncryptedFile
src={firstAttachment.url}
contentType={firstAttachment.contentType}
playNextMessage={this.props.playNextMessage}
playableMessageIndex={this.props.playableMessageIndex}
nextMessageToPlay={this.props.nextMessageToPlay}
messageId={this.props.id}
/>
</div>
);

View File

@ -13,14 +13,12 @@ import {
export const SessionMessagesList = (props: {
scrollToQuoteMessage: (options: QuoteClickOptions) => Promise<void>;
playNextMessage?: (value: number) => void;
}) => {
const messagesProps = useSelector(getSortedMessagesOfSelectedConversation);
let playableMessageIndex = 0;
return (
<>
{messagesProps.map((messageProps: SortedMessageModelProps, index: number) => {
{messagesProps.map((messageProps: SortedMessageModelProps) => {
const timerProps = messageProps.propsForTimerNotification;
const propsForGroupInvitation = messageProps.propsForGroupInvitation;
const propsForDataExtractionNotification = messageProps.propsForDataExtractionNotification;
@ -64,18 +62,14 @@ export const SessionMessagesList = (props: {
return;
}
playableMessageIndex++;
// firstMessageOfSeries tells us to render the avatar only for the first message
// in a series of messages from the same user
return (
<GenericMessageItem
key={messageProps.propsForMessage.id}
playableMessageIndex={playableMessageIndex}
messageId={messageProps.propsForMessage.id}
messageProps={messageProps}
scrollToQuoteMessage={props.scrollToQuoteMessage}
playNextMessage={props.playNextMessage}
/>
);
})}

View File

@ -6,7 +6,6 @@ import { contextMenu } from 'react-contexify';
import {
quotedMessageToAnimate,
ReduxConversationType,
setNextMessageToPlay,
showScrollToBottomButton,
SortedMessageModelProps,
updateHaveDoneFirstScroll,
@ -108,43 +107,13 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
key="typing-bubble"
/>
<SessionMessagesList
scrollToQuoteMessage={this.scrollToQuoteMessage}
playNextMessage={this.playNextMessage}
/>
<SessionMessagesList scrollToQuoteMessage={this.scrollToQuoteMessage} />
<SessionScrollButton onClick={this.scrollToBottom} key="scroll-down-button" />
</div>
);
}
/**
* Sets the targeted index for the next
* @param index index of message that just completed
*/
private playNextMessage(index: any) {
const { messagesProps } = this.props;
let nextIndex: number | undefined = index - 1;
// to prevent autoplaying as soon as a message is received.
const latestMessagePlayed = index <= 0 || messagesProps.length < index - 1;
if (latestMessagePlayed) {
nextIndex = undefined;
window.inboxStore?.dispatch(setNextMessageToPlay(nextIndex));
return;
}
// stop auto-playing when the audio messages change author.
const prevAuthorNumber = messagesProps[index].propsForMessage.authorPhoneNumber;
const nextAuthorNumber = messagesProps[index - 1].propsForMessage.authorPhoneNumber;
const differentAuthor = prevAuthorNumber !== nextAuthorNumber;
if (differentAuthor) {
nextIndex = undefined;
}
window.inboxStore?.dispatch(setNextMessageToPlay(nextIndex));
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~~~~~~~~~~~ SCROLLING METHODS ~~~~~~~~~~~~~
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -13,7 +13,6 @@ import {
} from '../../../state/ducks/conversations';
import {
getFirstUnreadMessageId,
getNextMessageToPlayIndex,
isMessageSelectionMode,
} from '../../../state/selectors/conversations';
import { DataExtractionNotification } from '../../conversation/DataExtractionNotification';
@ -86,12 +85,9 @@ export const TimerNotificationItem = (props: { timerProps: PropsForExpirationTim
export const GenericMessageItem = (props: {
messageId: string;
messageProps: SortedMessageModelProps;
playableMessageIndex?: number;
scrollToQuoteMessage: (options: QuoteClickOptions) => Promise<void>;
playNextMessage?: (value: number) => void;
}) => {
const multiSelectMode = useSelector(isMessageSelectionMode);
const nextMessageToPlay = useSelector(getNextMessageToPlayIndex);
const messageId = props.messageId;
@ -103,19 +99,12 @@ export const GenericMessageItem = (props: {
...props.messageProps.propsForMessage,
firstMessageOfSeries: props.messageProps.firstMessageOfSeries,
multiSelectMode,
nextMessageToPlay,
playNextMessage: props.playNextMessage,
onQuoteClick,
};
return (
<React.Fragment key={messageId}>
<Message
{...regularProps}
playableMessageIndex={props.playableMessageIndex}
multiSelectMode={multiSelectMode}
key={messageId}
/>
<Message {...regularProps} multiSelectMode={multiSelectMode} key={messageId} />
<UnreadIndicator messageId={messageId} />
</React.Fragment>
);

View File

@ -212,8 +212,4 @@ export type MessageRenderingProps = PropsForMessage & {
multiSelectMode: boolean;
firstMessageOfSeries: boolean;
onQuoteClick?: (options: QuoteClickOptions) => Promise<void>;
playableMessageIndex?: number;
nextMessageToPlay?: number;
playNextMessage?: (value: number) => void;
};

View File

@ -265,7 +265,7 @@ export type ConversationsStateType = {
showScrollButton: boolean;
animateQuotedMessageId?: string;
nextMessageToPlay?: number;
nextMessageToPlayId?: string;
mentionMembers: MentionsMembersType;
};
@ -713,8 +713,11 @@ const conversationsSlice = createSlice({
state.animateQuotedMessageId = action.payload;
return state;
},
setNextMessageToPlay(state: ConversationsStateType, action: PayloadAction<number | undefined>) {
state.nextMessageToPlay = action.payload;
setNextMessageToPlayId(
state: ConversationsStateType,
action: PayloadAction<string | undefined>
) {
state.nextMessageToPlayId = action.payload;
return state;
},
updateMentionsMembers(
@ -782,7 +785,7 @@ export const {
quoteMessage,
showScrollToBottomButton,
quotedMessageToAnimate,
setNextMessageToPlay,
setNextMessageToPlayId,
updateMentionsMembers,
} = actions;

View File

@ -335,9 +335,9 @@ export const getQuotedMessageToAnimate = createSelector(
(state: ConversationsStateType): string | undefined => state.animateQuotedMessageId || undefined
);
export const getNextMessageToPlayIndex = createSelector(
export const getNextMessageToPlayId = createSelector(
getConversations,
(state: ConversationsStateType): number | undefined => state.nextMessageToPlay || undefined
(state: ConversationsStateType): string | undefined => state.nextMessageToPlayId || undefined
);
export const getMentionsInput = createSelector(
@ -398,11 +398,13 @@ function sortMessages(
// for non public convos, we order by sent_at or received_at timestamp.
// we assume that a message has either a sent_at or a received_at field set.
const messagesSorted = messages.sort(
(a, b) =>
(b.propsForMessage.timestamp || b.propsForMessage.receivedAt || 0) -
(a.propsForMessage.timestamp || a.propsForMessage.receivedAt || 0)
);
const messagesSorted = messages
.slice()
.sort(
(a, b) =>
(b.propsForMessage.timestamp || b.propsForMessage.receivedAt || 0) -
(a.propsForMessage.timestamp || a.propsForMessage.receivedAt || 0)
);
return messagesSorted;
}