Fix forwarding attachments that haven't been downloaded
This commit is contained in:
parent
ac76271772
commit
0db5a3b888
3 changed files with 155 additions and 85 deletions
|
@ -36,9 +36,11 @@ import {
|
||||||
MESSAGE_CHANGED,
|
MESSAGE_CHANGED,
|
||||||
MESSAGE_DELETED,
|
MESSAGE_DELETED,
|
||||||
MESSAGE_EXPIRED,
|
MESSAGE_EXPIRED,
|
||||||
|
actions as conversationsActions,
|
||||||
} from './conversations';
|
} from './conversations';
|
||||||
import { SHOW_TOAST } from './toast';
|
import { SHOW_TOAST } from './toast';
|
||||||
import type { ShowToastActionType } from './toast';
|
import type { ShowToastActionType } from './toast';
|
||||||
|
import { isDownloaded } from '../../types/Attachment';
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
|
||||||
|
@ -564,6 +566,14 @@ function toggleForwardMessagesModal(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const attachments = message.get('attachments') ?? [];
|
||||||
|
|
||||||
|
if (!attachments.every(isDownloaded)) {
|
||||||
|
dispatch(
|
||||||
|
conversationsActions.kickOffAttachmentDownload({ messageId })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const messagePropsSelector = getMessagePropsSelector(getState());
|
const messagePropsSelector = getMessagePropsSelector(getState());
|
||||||
const messageProps = messagePropsSelector(message.attributes);
|
const messageProps = messagePropsSelector(message.attributes);
|
||||||
|
|
||||||
|
@ -832,6 +842,21 @@ export function confirmAuthorizeArtCreator(): ThunkAction<
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function copyOverMessageAttributesIntoForwardMessages(
|
||||||
|
messagesProps: ReadonlyArray<ForwardMessagePropsType>,
|
||||||
|
attributes: ReadonlyDeep<MessageAttributesType>
|
||||||
|
): ReadonlyArray<ForwardMessagePropsType> {
|
||||||
|
return messagesProps.map(messageProps => {
|
||||||
|
if (messageProps.id !== attributes.id) {
|
||||||
|
return messageProps;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...messageProps,
|
||||||
|
attachments: attributes.attachments,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Reducer
|
// Reducer
|
||||||
|
|
||||||
export function getEmptyState(): GlobalModalsStateType {
|
export function getEmptyState(): GlobalModalsStateType {
|
||||||
|
@ -1099,57 +1124,77 @@ export function reducer(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (state.forwardMessagesProps != null) {
|
||||||
action.type === MESSAGE_CHANGED ||
|
|
||||||
action.type === MESSAGE_DELETED ||
|
|
||||||
action.type === MESSAGE_EXPIRED
|
|
||||||
) {
|
|
||||||
if (!state.editHistoryMessages) {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.type === MESSAGE_DELETED || action.type === MESSAGE_EXPIRED) {
|
|
||||||
const hasMessageId = state.editHistoryMessages.some(
|
|
||||||
edit => edit.id === action.payload.id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!hasMessageId) {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
editHistoryMessages: undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.type === MESSAGE_CHANGED) {
|
if (action.type === MESSAGE_CHANGED) {
|
||||||
if (!action.payload.data.editHistory) {
|
if (
|
||||||
return state;
|
!state.forwardMessagesProps.messages.some(message => {
|
||||||
}
|
return message.id === action.payload.id;
|
||||||
|
})
|
||||||
const hasMessageId = state.editHistoryMessages.some(
|
) {
|
||||||
edit => edit.id === action.payload.id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!hasMessageId) {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextEditHistoryMessages = copyOverMessageAttributesIntoEditHistory(
|
|
||||||
action.payload.data
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!nextEditHistoryMessages) {
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
editHistoryMessages: nextEditHistoryMessages,
|
forwardMessagesProps: {
|
||||||
|
...state.forwardMessagesProps,
|
||||||
|
messages: copyOverMessageAttributesIntoForwardMessages(
|
||||||
|
state.forwardMessagesProps.messages,
|
||||||
|
action.payload.data
|
||||||
|
),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.editHistoryMessages != null) {
|
||||||
|
if (
|
||||||
|
action.type === MESSAGE_CHANGED ||
|
||||||
|
action.type === MESSAGE_DELETED ||
|
||||||
|
action.type === MESSAGE_EXPIRED
|
||||||
|
) {
|
||||||
|
if (action.type === MESSAGE_DELETED || action.type === MESSAGE_EXPIRED) {
|
||||||
|
const hasMessageId = state.editHistoryMessages.some(
|
||||||
|
edit => edit.id === action.payload.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasMessageId) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
editHistoryMessages: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === MESSAGE_CHANGED) {
|
||||||
|
if (!action.payload.data.editHistory) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasMessageId = state.editHistoryMessages.some(
|
||||||
|
edit => edit.id === action.payload.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasMessageId) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextEditHistoryMessages =
|
||||||
|
copyOverMessageAttributesIntoEditHistory(action.payload.data);
|
||||||
|
|
||||||
|
if (!nextEditHistoryMessages) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
editHistoryMessages: nextEditHistoryMessages,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,16 @@
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import type { ForwardMessagesPropsType } from '../ducks/globalModals';
|
import type {
|
||||||
|
ForwardMessagePropsType,
|
||||||
|
ForwardMessagesPropsType,
|
||||||
|
} from '../ducks/globalModals';
|
||||||
import type { StateType } from '../reducer';
|
import type { StateType } from '../reducer';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
import { ForwardMessagesModal } from '../../components/ForwardMessagesModal';
|
import { ForwardMessagesModal } from '../../components/ForwardMessagesModal';
|
||||||
import { LinkPreviewSourceType } from '../../types/LinkPreview';
|
import { LinkPreviewSourceType } from '../../types/LinkPreview';
|
||||||
import * as Errors from '../../types/errors';
|
import * as Errors from '../../types/errors';
|
||||||
|
import type { GetConversationByIdType } from '../selectors/conversations';
|
||||||
import {
|
import {
|
||||||
getAllComposableConversations,
|
getAllComposableConversations,
|
||||||
getConversationSelector,
|
getConversationSelector,
|
||||||
|
@ -31,12 +35,53 @@ import { useLinkPreviewActions } from '../ducks/linkPreviews';
|
||||||
import { SmartCompositionTextArea } from './CompositionTextArea';
|
import { SmartCompositionTextArea } from './CompositionTextArea';
|
||||||
import { useToastActions } from '../ducks/toast';
|
import { useToastActions } from '../ducks/toast';
|
||||||
import { hydrateRanges } from '../../types/BodyRange';
|
import { hydrateRanges } from '../../types/BodyRange';
|
||||||
|
import { isDownloaded } from '../../types/Attachment';
|
||||||
|
|
||||||
|
function toMessageForwardDraft(
|
||||||
|
props: ForwardMessagePropsType,
|
||||||
|
getConversation: GetConversationByIdType
|
||||||
|
): MessageForwardDraft {
|
||||||
|
return {
|
||||||
|
attachments: props.attachments ?? [],
|
||||||
|
bodyRanges: hydrateRanges(props.bodyRanges, getConversation),
|
||||||
|
hasContact: Boolean(props.contact),
|
||||||
|
isSticker: Boolean(props.isSticker),
|
||||||
|
messageBody: props.text,
|
||||||
|
originalMessageId: props.id,
|
||||||
|
previews: props.previews ?? [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function SmartForwardMessagesModal(): JSX.Element | null {
|
export function SmartForwardMessagesModal(): JSX.Element | null {
|
||||||
const forwardMessagesProps = useSelector<
|
const forwardMessagesProps = useSelector<
|
||||||
StateType,
|
StateType,
|
||||||
ForwardMessagesPropsType | undefined
|
ForwardMessagesPropsType | undefined
|
||||||
>(state => state.globalModals.forwardMessagesProps);
|
>(state => state.globalModals.forwardMessagesProps);
|
||||||
|
|
||||||
|
if (forwardMessagesProps == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!forwardMessagesProps.messages.every(message => {
|
||||||
|
return message.attachments?.every(isDownloaded) ?? true;
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SmartForwardMessagesModalInner
|
||||||
|
forwardMessagesProps={forwardMessagesProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SmartForwardMessagesModalInner({
|
||||||
|
forwardMessagesProps,
|
||||||
|
}: {
|
||||||
|
forwardMessagesProps: ForwardMessagesPropsType;
|
||||||
|
}): JSX.Element | null {
|
||||||
const candidateConversations = useSelector(getAllComposableConversations);
|
const candidateConversations = useSelector(getAllComposableConversations);
|
||||||
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
|
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
|
||||||
const getConversation = useSelector(getConversationSelector);
|
const getConversation = useSelector(getConversationSelector);
|
||||||
|
@ -51,19 +96,9 @@ export function SmartForwardMessagesModal(): JSX.Element | null {
|
||||||
|
|
||||||
const [drafts, setDrafts] = useState<ReadonlyArray<MessageForwardDraft>>(
|
const [drafts, setDrafts] = useState<ReadonlyArray<MessageForwardDraft>>(
|
||||||
() => {
|
() => {
|
||||||
return (
|
return forwardMessagesProps.messages.map((props): MessageForwardDraft => {
|
||||||
forwardMessagesProps?.messages.map((props): MessageForwardDraft => {
|
return toMessageForwardDraft(props, getConversation);
|
||||||
return {
|
});
|
||||||
attachments: props.attachments ?? [],
|
|
||||||
bodyRanges: hydrateRanges(props.bodyRanges, getConversation),
|
|
||||||
hasContact: Boolean(props.contact),
|
|
||||||
isSticker: Boolean(props.isSticker),
|
|
||||||
messageBody: props.text,
|
|
||||||
originalMessageId: props.id,
|
|
||||||
previews: props.previews ?? [],
|
|
||||||
};
|
|
||||||
}) ?? []
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import { orderBy } from 'lodash';
|
import { orderBy } from 'lodash';
|
||||||
import type { AttachmentType } from '../types/Attachment';
|
import type { AttachmentType } from '../types/Attachment';
|
||||||
import { isVoiceMessage } from '../types/Attachment';
|
import { isVoiceMessage, isDownloaded } from '../types/Attachment';
|
||||||
import type {
|
import type {
|
||||||
LinkPreviewType,
|
LinkPreviewType,
|
||||||
LinkPreviewWithHydratedData,
|
LinkPreviewWithHydratedData,
|
||||||
|
@ -57,41 +57,31 @@ export function isDraftEditable(draft: MessageForwardDraft): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isDraftForwardable(draft: MessageForwardDraft): boolean {
|
function isDraftEmpty(draft: MessageForwardDraft) {
|
||||||
const messageLength = draft.messageBody?.length ?? 0;
|
const { messageBody, attachments, isSticker, hasContact } = draft;
|
||||||
if (messageLength > 0) {
|
if (isSticker || hasContact) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
if (draft.isSticker) {
|
if (attachments != null && attachments.length > 0) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
if (draft.hasContact) {
|
if (messageBody != null && messageBody.length > 0) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
const attachmentsLength = draft.attachments?.length ?? 0;
|
return true;
|
||||||
if (attachmentsLength > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isMessageForwardable(message: MessageAttributesType): boolean {
|
export function isDraftForwardable(draft: MessageForwardDraft): boolean {
|
||||||
const { body, attachments, sticker, contact } = message;
|
const { attachments } = draft;
|
||||||
const messageLength = body?.length ?? 0;
|
if (isDraftEmpty(draft)) {
|
||||||
if (messageLength > 0) {
|
return false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (sticker) {
|
if (attachments != null && attachments.length > 0) {
|
||||||
return true;
|
if (!attachments.every(isDownloaded)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (contact?.length) {
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const attachmentsLength = attachments?.length ?? 0;
|
|
||||||
if (attachmentsLength > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sortByMessageOrder<T>(
|
export function sortByMessageOrder<T>(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue