Fix voice note drafts when switching chats
This commit is contained in:
parent
a25c2036b5
commit
11b2563a3d
7 changed files with 39 additions and 42 deletions
|
@ -45,12 +45,12 @@ export function Default(): JSX.Element {
|
|||
{active && (
|
||||
<CompositionRecording
|
||||
i18n={i18n}
|
||||
conversationId="convo-id"
|
||||
onCancel={handleCancel}
|
||||
onSend={handleSend}
|
||||
errorRecording={_ => action('error')()}
|
||||
addAttachment={action('addAttachment')}
|
||||
completeRecording={action('completeRecording')}
|
||||
saveDraftRecordingIfNeeded={action('saveDraftRecordingIfNeeded')}
|
||||
showToast={action('showToast')}
|
||||
hideToast={action('hideToast')}
|
||||
/>
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { noop } from 'lodash';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useEscapeHandling } from '../hooks/useEscapeHandling';
|
||||
import { usePrevious } from '../hooks/usePrevious';
|
||||
import type { HideToastAction, ShowToastAction } from '../state/ducks/toast';
|
||||
import type { InMemoryAttachmentDraftType } from '../types/Attachment';
|
||||
import { ErrorDialogAudioRecorderType } from '../types/AudioRecorder';
|
||||
|
@ -18,7 +17,6 @@ import { RecordingComposer } from './RecordingComposer';
|
|||
|
||||
export type Props = {
|
||||
i18n: LocalizerType;
|
||||
conversationId: string;
|
||||
onCancel: () => void;
|
||||
onSend: () => void;
|
||||
errorRecording: (e: ErrorDialogAudioRecorderType) => unknown;
|
||||
|
@ -31,47 +29,30 @@ export type Props = {
|
|||
conversationId: string,
|
||||
onRecordingComplete: (rec: InMemoryAttachmentDraftType) => unknown
|
||||
) => unknown;
|
||||
saveDraftRecordingIfNeeded: () => void;
|
||||
showToast: ShowToastAction;
|
||||
hideToast: HideToastAction;
|
||||
};
|
||||
|
||||
export function CompositionRecording({
|
||||
i18n,
|
||||
conversationId,
|
||||
onCancel,
|
||||
onSend,
|
||||
errorRecording,
|
||||
errorDialogAudioRecorderType,
|
||||
addAttachment,
|
||||
completeRecording,
|
||||
saveDraftRecordingIfNeeded,
|
||||
showToast,
|
||||
hideToast,
|
||||
}: Props): JSX.Element {
|
||||
useEscapeHandling(onCancel);
|
||||
|
||||
// when interrupted (blur, switching convos)
|
||||
// stop recording and save draft
|
||||
const handleRecordingInterruption = useCallback(() => {
|
||||
completeRecording(conversationId, attachment => {
|
||||
addAttachment(conversationId, attachment);
|
||||
});
|
||||
}, [conversationId, completeRecording, addAttachment]);
|
||||
|
||||
// switched to another app
|
||||
useEffect(() => {
|
||||
window.addEventListener('blur', handleRecordingInterruption);
|
||||
window.addEventListener('blur', saveDraftRecordingIfNeeded);
|
||||
return () => {
|
||||
window.removeEventListener('blur', handleRecordingInterruption);
|
||||
window.removeEventListener('blur', saveDraftRecordingIfNeeded);
|
||||
};
|
||||
}, [handleRecordingInterruption]);
|
||||
|
||||
// switched conversations
|
||||
const previousConversationId = usePrevious(conversationId, conversationId);
|
||||
useEffect(() => {
|
||||
if (previousConversationId !== conversationId) {
|
||||
handleRecordingInterruption();
|
||||
}
|
||||
});
|
||||
}, [saveDraftRecordingIfNeeded]);
|
||||
|
||||
useEffect(() => {
|
||||
const toast: AnyToast = { toastType: ToastType.VoiceNoteLimit };
|
||||
|
|
|
@ -65,6 +65,10 @@ type AudioPlayerActionType = ReadonlyDeep<
|
|||
| StartRecordingAction
|
||||
>;
|
||||
|
||||
export function getIsRecording(audioRecorder: AudioRecorderStateType): boolean {
|
||||
return audioRecorder.recordingState === RecordingState.Recording;
|
||||
}
|
||||
|
||||
// Action Creators
|
||||
|
||||
export const actions = {
|
||||
|
|
|
@ -96,6 +96,7 @@ import {
|
|||
} from '../../types/CallDisposition';
|
||||
import type { CallHistoryAdd } from './callHistory';
|
||||
import { addCallHistory } from './callHistory';
|
||||
import { saveDraftRecordingIfNeeded } from './composer';
|
||||
|
||||
// State
|
||||
|
||||
|
@ -926,6 +927,8 @@ function acceptCall(
|
|||
return;
|
||||
}
|
||||
|
||||
saveDraftRecordingIfNeeded()(dispatch, getState, undefined);
|
||||
|
||||
switch (call.callMode) {
|
||||
case CallMode.Direct:
|
||||
await calling.acceptDirectCall(conversationId, asVideoCall);
|
||||
|
|
|
@ -33,8 +33,7 @@ import {
|
|||
} from './linkPreviews';
|
||||
import { LinkPreviewSourceType } from '../../types/LinkPreview';
|
||||
import type { AciString } from '../../types/ServiceId';
|
||||
import { completeRecording } from './audioRecorder';
|
||||
import { RecordingState } from '../../types/AudioRecorder';
|
||||
import { completeRecording, getIsRecording } from './audioRecorder';
|
||||
import { SHOW_TOAST } from './toast';
|
||||
import type { AnyToast } from '../../types/Toast';
|
||||
import { ToastType } from '../../types/Toast';
|
||||
|
@ -243,6 +242,7 @@ export const actions = {
|
|||
removeAttachment,
|
||||
replaceAttachments,
|
||||
resetComposer,
|
||||
saveDraftRecordingIfNeeded,
|
||||
scrollToQuotedMessage,
|
||||
sendEditedMessage,
|
||||
sendMultiMediaMessage,
|
||||
|
@ -363,23 +363,33 @@ function scrollToQuotedMessage({
|
|||
};
|
||||
}
|
||||
|
||||
export function handleLeaveConversation(
|
||||
conversationId: string
|
||||
): ThunkAction<void, RootStateType, unknown, never> {
|
||||
export function saveDraftRecordingIfNeeded(): ThunkAction<
|
||||
void,
|
||||
RootStateType,
|
||||
unknown,
|
||||
never
|
||||
> {
|
||||
return (dispatch, getState) => {
|
||||
const { audioRecorder } = getState();
|
||||
const { conversations, audioRecorder } = getState();
|
||||
const { selectedConversationId: conversationId } = conversations;
|
||||
|
||||
if (audioRecorder.recordingState !== RecordingState.Recording) {
|
||||
if (!getIsRecording(audioRecorder) || !conversationId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// save draft of voice note
|
||||
dispatch(
|
||||
completeRecording(conversationId, attachment => {
|
||||
dispatch(
|
||||
addPendingAttachment(conversationId, { ...attachment, pending: true })
|
||||
);
|
||||
dispatch(addAttachment(conversationId, attachment));
|
||||
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('saveDraftRecordingIfNeeded: No conversation found');
|
||||
}
|
||||
|
||||
drop(conversation.updateLastMessage());
|
||||
})
|
||||
);
|
||||
};
|
||||
|
@ -1014,11 +1024,9 @@ function processAttachments({
|
|||
return;
|
||||
}
|
||||
|
||||
const state = getState();
|
||||
const isRecording =
|
||||
state.audioRecorder.recordingState === RecordingState.Recording;
|
||||
const { audioRecorder } = getState();
|
||||
|
||||
if (hasLinkPreviewLoaded() || isRecording) {
|
||||
if (hasLinkPreviewLoaded() || getIsRecording(audioRecorder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ import {
|
|||
setComposerFocus,
|
||||
setQuoteByMessageId,
|
||||
resetComposer,
|
||||
handleLeaveConversation,
|
||||
saveDraftRecordingIfNeeded,
|
||||
} from './composer';
|
||||
import { ReceiptType } from '../../types/Receipt';
|
||||
import { Sound, SoundType } from '../../util/Sound';
|
||||
|
@ -4254,7 +4254,7 @@ function showConversation({
|
|||
|
||||
// notify composer in case we need to stop recording a voice note
|
||||
if (conversations.selectedConversationId) {
|
||||
dispatch(handleLeaveConversation(conversations.selectedConversationId));
|
||||
saveDraftRecordingIfNeeded()(dispatch, getState, undefined);
|
||||
dispatch(
|
||||
onConversationClosed(
|
||||
conversations.selectedConversationId,
|
||||
|
|
|
@ -23,7 +23,8 @@ export const SmartCompositionRecording = memo(
|
|||
const { errorRecording, cancelRecording, completeRecording } =
|
||||
useAudioRecorderActions();
|
||||
|
||||
const { sendMultiMediaMessage, addAttachment } = useComposerActions();
|
||||
const { sendMultiMediaMessage, addAttachment, saveDraftRecordingIfNeeded } =
|
||||
useComposerActions();
|
||||
const { hideToast, showToast } = useToastActions();
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
|
@ -53,12 +54,12 @@ export const SmartCompositionRecording = memo(
|
|||
return (
|
||||
<CompositionRecording
|
||||
i18n={i18n}
|
||||
conversationId={selectedConversationId}
|
||||
onCancel={handleCancel}
|
||||
onSend={handleSend}
|
||||
errorRecording={errorRecording}
|
||||
addAttachment={addAttachment}
|
||||
completeRecording={completeRecording}
|
||||
saveDraftRecordingIfNeeded={saveDraftRecordingIfNeeded}
|
||||
showToast={showToast}
|
||||
hideToast={hideToast}
|
||||
/>
|
||||
|
|
Loading…
Reference in a new issue