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:
parent
57aa8cba69
commit
2c174bdac7
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -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 ~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue