Various fixes for drafts (#4036)
This commit is contained in:
parent
dcba75a11a
commit
df76638fe1
@ -256,6 +256,7 @@ export function sendMessage(
|
||||
noWebPage,
|
||||
sendAs,
|
||||
shouldUpdateStickerSetOrder,
|
||||
wasDrafted,
|
||||
}: {
|
||||
chat: ApiChat;
|
||||
lastMessageId?: number;
|
||||
@ -274,6 +275,7 @@ export function sendMessage(
|
||||
noWebPage?: boolean;
|
||||
sendAs?: ApiPeer;
|
||||
shouldUpdateStickerSetOrder?: boolean;
|
||||
wasDrafted?: boolean;
|
||||
},
|
||||
onProgress?: ApiOnProgress,
|
||||
) {
|
||||
@ -298,6 +300,7 @@ export function sendMessage(
|
||||
id: localMessage.id,
|
||||
chatId: chat.id,
|
||||
message: localMessage,
|
||||
wasDrafted,
|
||||
});
|
||||
|
||||
// This is expected to arrive after `updateMessageSendSucceeded` which replaces the local ID,
|
||||
@ -1284,6 +1287,7 @@ export async function forwardMessages({
|
||||
noAuthors,
|
||||
noCaptions,
|
||||
isCurrentUserPremium,
|
||||
wasDrafted,
|
||||
}: {
|
||||
fromChat: ApiChat;
|
||||
toChat: ApiChat;
|
||||
@ -1296,6 +1300,7 @@ export async function forwardMessages({
|
||||
noAuthors?: boolean;
|
||||
noCaptions?: boolean;
|
||||
isCurrentUserPremium?: boolean;
|
||||
wasDrafted?: boolean;
|
||||
}) {
|
||||
const messageIds = messages.map(({ id }) => id);
|
||||
const randomIds = messages.map(generateRandomBigInt);
|
||||
@ -1318,6 +1323,7 @@ export async function forwardMessages({
|
||||
id: localMessage.id,
|
||||
chatId: toChat.id,
|
||||
message: localMessage,
|
||||
wasDrafted,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -191,6 +191,7 @@ export type ApiUpdateNewScheduledMessage = {
|
||||
chatId: string;
|
||||
id: number;
|
||||
message: ApiMessage;
|
||||
wasDrafted?: boolean;
|
||||
};
|
||||
|
||||
export type ApiUpdateNewMessage = {
|
||||
@ -199,6 +200,7 @@ export type ApiUpdateNewMessage = {
|
||||
id: number;
|
||||
message: Partial<ApiMessage>;
|
||||
shouldForceReply?: boolean;
|
||||
wasDrafted?: boolean;
|
||||
};
|
||||
|
||||
export type ApiUpdateMessage = {
|
||||
|
||||
@ -14,16 +14,21 @@ import './LastMessageMeta.scss';
|
||||
type OwnProps = {
|
||||
message: ApiMessage;
|
||||
outgoingStatus?: ApiMessageOutgoingStatus;
|
||||
draftDate?: number;
|
||||
};
|
||||
|
||||
const LastMessageMeta: FC<OwnProps> = ({ message, outgoingStatus }) => {
|
||||
const LastMessageMeta: FC<OwnProps> = ({ message, outgoingStatus, draftDate }) => {
|
||||
const lang = useLang();
|
||||
|
||||
const shouldUseDraft = draftDate && draftDate > message.date;
|
||||
return (
|
||||
<div className="LastMessageMeta">
|
||||
{outgoingStatus && (
|
||||
{outgoingStatus && !shouldUseDraft && (
|
||||
<MessageOutgoingStatus status={outgoingStatus} />
|
||||
)}
|
||||
<span className="time">{formatPastTimeShort(lang, message.date * 1000)}</span>
|
||||
<span className="time">
|
||||
{formatPastTimeShort(lang, (shouldUseDraft ? draftDate : message.date) * 1000)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -30,10 +30,6 @@
|
||||
background-color: var(--color-reply-active);
|
||||
box-shadow: 0 1px 2px var(--color-default-shadow);
|
||||
|
||||
.embedded-thumb {
|
||||
margin-inline-start: 0.5rem;
|
||||
}
|
||||
|
||||
&:dir(rtl) {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
@ -293,6 +293,7 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
<LastMessageMeta
|
||||
message={chat.lastMessage}
|
||||
outgoingStatus={lastMessageOutgoingStatus}
|
||||
draftDate={draft?.date}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -105,8 +105,11 @@ export default function useChatListEntry({
|
||||
}
|
||||
|
||||
const isDraftReplyToTopic = draft && draft.replyInfo?.replyToMsgId === lastMessageTopic?.id;
|
||||
const isEmptyLocalReply = draft?.replyInfo && !draft.text && draft.isLocal;
|
||||
|
||||
if (draft && (!chat?.isForum || (isTopic && !isDraftReplyToTopic))) {
|
||||
const canDisplayDraft = !chat?.isForum && draft && !isEmptyLocalReply && (!isTopic || !isDraftReplyToTopic);
|
||||
|
||||
if (canDisplayDraft) {
|
||||
return (
|
||||
<p className="last-message" dir={lang.isRtl ? 'auto' : 'ltr'}>
|
||||
<span className="draft">{lang('Draft')}</span>
|
||||
|
||||
@ -53,6 +53,7 @@ type StateProps = {
|
||||
forwardsHaveCaptions?: boolean;
|
||||
isCurrentUserPremium?: boolean;
|
||||
isContextMenuDisabled?: boolean;
|
||||
isReplyToDiscussion?: boolean;
|
||||
};
|
||||
|
||||
type OwnProps = {
|
||||
@ -75,6 +76,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
||||
shouldForceShowEditing,
|
||||
isCurrentUserPremium,
|
||||
isContextMenuDisabled,
|
||||
isReplyToDiscussion,
|
||||
onClear,
|
||||
}) => {
|
||||
const {
|
||||
@ -105,7 +107,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
||||
const {
|
||||
shouldRender, transitionClassNames,
|
||||
} = useShowTransition(
|
||||
canAnimate && isShown && !isReplyToTopicStart,
|
||||
canAnimate && isShown && !isReplyToTopicStart && !isReplyToDiscussion,
|
||||
undefined,
|
||||
!shouldAnimate,
|
||||
undefined,
|
||||
@ -167,8 +169,10 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
||||
getPeerColorClass(sender),
|
||||
);
|
||||
|
||||
const isShowingReply = replyInfo && !shouldForceShowEditing;
|
||||
|
||||
const leftIcon = useMemo(() => {
|
||||
if (replyInfo && !shouldForceShowEditing) {
|
||||
if (isShowingReply) {
|
||||
return 'icon-reply';
|
||||
}
|
||||
if (editingId) {
|
||||
@ -179,7 +183,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [editingId, isForwarding, replyInfo, shouldForceShowEditing]);
|
||||
}, [editingId, isForwarding, isShowingReply]);
|
||||
|
||||
const customText = forwardedMessagesCount && forwardedMessagesCount > 1
|
||||
? lang('ForwardedMessageCount', forwardedMessagesCount)
|
||||
@ -214,7 +218,8 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
||||
message={strippedMessage}
|
||||
sender={!noAuthors ? sender : undefined}
|
||||
customText={customText}
|
||||
title={editingId ? lang('EditMessage') : noAuthors ? lang('HiddenSendersNameDescription') : undefined}
|
||||
title={(editingId && !isShowingReply) ? lang('EditMessage')
|
||||
: noAuthors ? lang('HiddenSendersNameDescription') : undefined}
|
||||
onClick={handleMessageClick}
|
||||
/>
|
||||
<Button
|
||||
@ -358,6 +363,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
const isContextMenuDisabled = isForwarding && forwardMessageIds!.length === 1
|
||||
&& Boolean(message?.content.storyData);
|
||||
|
||||
const isReplyToDiscussion = replyInfo?.replyToMsgId === threadId && !replyInfo.replyToPeerId;
|
||||
|
||||
return {
|
||||
replyInfo,
|
||||
editingId,
|
||||
@ -370,6 +377,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
forwardsHaveCaptions,
|
||||
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
||||
isContextMenuDisabled,
|
||||
isReplyToDiscussion,
|
||||
};
|
||||
},
|
||||
)(ComposerEmbeddedMessage));
|
||||
|
||||
@ -39,7 +39,9 @@ import {
|
||||
areSortedArraysIntersecting, buildCollectionByKey, omit, partition, split, unique,
|
||||
} from '../../../util/iteratees';
|
||||
import { translate } from '../../../util/langProvider';
|
||||
import { debounce, onTickEnd, rafPromise } from '../../../util/schedulers';
|
||||
import {
|
||||
debounce, onTickEnd, rafPromise,
|
||||
} from '../../../util/schedulers';
|
||||
import { IS_IOS } from '../../../util/windowEnvironment';
|
||||
import { callApi, cancelApiProgress } from '../../../api/gramjs';
|
||||
import {
|
||||
@ -292,7 +294,8 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType =>
|
||||
}
|
||||
|
||||
const chat = selectChat(global, chatId!)!;
|
||||
const draftReplyInfo = !isStoryReply ? selectDraft(global, chatId!, threadId!)?.replyInfo : undefined;
|
||||
const draft = selectDraft(global, chatId!, threadId!);
|
||||
const draftReplyInfo = !isStoryReply ? draft?.replyInfo : undefined;
|
||||
|
||||
const storyReplyInfo = isStoryReply ? {
|
||||
type: 'story',
|
||||
@ -313,7 +316,6 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType =>
|
||||
};
|
||||
|
||||
if (!isStoryReply) {
|
||||
actions.resetDraftReplyInfo({ tabId });
|
||||
actions.clearWebPagePreview({ tabId });
|
||||
}
|
||||
|
||||
@ -325,6 +327,7 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType =>
|
||||
sendMessage(global, {
|
||||
...restParams,
|
||||
attachment: attachments ? attachments[0] : undefined,
|
||||
wasDrafted: Boolean(draft),
|
||||
});
|
||||
} else if (isGrouped) {
|
||||
const {
|
||||
@ -346,6 +349,7 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType =>
|
||||
entities: isFirst ? entities : undefined,
|
||||
attachment: firstAttachment,
|
||||
groupedId: restAttachments.length > 0 ? groupedId : undefined,
|
||||
wasDrafted: Boolean(draft),
|
||||
});
|
||||
|
||||
restAttachments.forEach((attachment: ApiAttachment) => {
|
||||
@ -368,6 +372,7 @@ addActionHandler('sendMessage', (global, actions, payload): ActionReturnType =>
|
||||
text,
|
||||
entities,
|
||||
replyInfo: replyToForFirstMessage,
|
||||
wasDrafted: Boolean(draft),
|
||||
});
|
||||
}
|
||||
|
||||
@ -462,7 +467,9 @@ addActionHandler('saveDraft', (global, actions, payload): ActionReturnType => {
|
||||
replyInfo: currentDraft?.replyInfo,
|
||||
};
|
||||
|
||||
saveDraft(global, chatId, threadId, newDraft);
|
||||
saveDraft({
|
||||
global, chatId, threadId, draft: newDraft,
|
||||
});
|
||||
});
|
||||
|
||||
addActionHandler('clearDraft', (global, actions, payload): ActionReturnType => {
|
||||
@ -480,9 +487,9 @@ addActionHandler('clearDraft', (global, actions, payload): ActionReturnType => {
|
||||
replyInfo: currentReplyInfo,
|
||||
} : undefined;
|
||||
|
||||
if (!isLocalOnly) {
|
||||
saveDraft(global, chatId, threadId, newDraft);
|
||||
}
|
||||
saveDraft({
|
||||
global, chatId, threadId, draft: newDraft, isLocalOnly,
|
||||
});
|
||||
});
|
||||
|
||||
addActionHandler('updateDraftReplyInfo', (global, actions, payload): ActionReturnType => {
|
||||
@ -509,7 +516,9 @@ addActionHandler('updateDraftReplyInfo', (global, actions, payload): ActionRetur
|
||||
replyInfo: updatedReplyInfo,
|
||||
};
|
||||
|
||||
saveDraft(global, chatId, threadId, newDraft);
|
||||
saveDraft({
|
||||
global, chatId, threadId, draft: newDraft, isLocalOnly: true, noLocalTimeUpdate: true,
|
||||
});
|
||||
});
|
||||
|
||||
addActionHandler('resetDraftReplyInfo', (global, actions, payload): ActionReturnType => {
|
||||
@ -526,10 +535,16 @@ addActionHandler('resetDraftReplyInfo', (global, actions, payload): ActionReturn
|
||||
replyInfo: undefined,
|
||||
};
|
||||
|
||||
saveDraft(global, chatId, threadId, newDraft);
|
||||
saveDraft({
|
||||
global, chatId, threadId, draft: newDraft, isLocalOnly: Boolean(newDraft),
|
||||
});
|
||||
});
|
||||
|
||||
async function saveDraft<T extends GlobalState>(global: T, chatId: string, threadId: number, draft?: ApiDraft) {
|
||||
async function saveDraft<T extends GlobalState>({
|
||||
global, chatId, threadId, draft, isLocalOnly, noLocalTimeUpdate,
|
||||
} : {
|
||||
global: T; chatId: string; threadId: number; draft?: ApiDraft; isLocalOnly?: boolean; noLocalTimeUpdate?: boolean;
|
||||
}) {
|
||||
const chat = selectChat(global, chatId);
|
||||
const user = selectUser(global, chatId);
|
||||
if (!chat || (user && isDeletedUser(user))) return;
|
||||
@ -544,10 +559,14 @@ async function saveDraft<T extends GlobalState>(global: T, chatId: string, threa
|
||||
} : undefined;
|
||||
|
||||
global = replaceThreadParam(global, chatId, threadId, 'draft', newDraft);
|
||||
global = updateChat(global, chatId, { draftDate: newDraft?.date });
|
||||
if (!noLocalTimeUpdate) {
|
||||
global = updateChat(global, chatId, { draftDate: newDraft?.date });
|
||||
}
|
||||
|
||||
setGlobal(global);
|
||||
|
||||
if (isLocalOnly) return;
|
||||
|
||||
const result = await callApi('saveDraft', {
|
||||
chat,
|
||||
draft: newDraft,
|
||||
@ -929,6 +948,7 @@ addActionHandler('forwardMessages', (global, actions, payload): ActionReturnType
|
||||
}
|
||||
|
||||
const sendAs = selectSendAs(global, toChatId!);
|
||||
const draft = selectDraft(global, toChatId!, toThreadId || MAIN_THREAD_ID);
|
||||
|
||||
const [realMessages, serviceMessages] = partition(messages, (m) => !isServiceNotificationMessage(m));
|
||||
if (realMessages.length) {
|
||||
@ -946,6 +966,7 @@ addActionHandler('forwardMessages', (global, actions, payload): ActionReturnType
|
||||
noAuthors,
|
||||
noCaptions,
|
||||
isCurrentUserPremium,
|
||||
wasDrafted: Boolean(draft),
|
||||
});
|
||||
})();
|
||||
}
|
||||
@ -1290,6 +1311,7 @@ async function sendMessage<T extends GlobalState>(global: T, params: {
|
||||
scheduledAt?: number;
|
||||
sendAs?: ApiPeer;
|
||||
groupedId?: string;
|
||||
wasDrafted?: boolean;
|
||||
}) {
|
||||
let localId: number | undefined;
|
||||
const progressCallback = params.attachment ? (progress: number, messageLocalId: number) => {
|
||||
|
||||
@ -71,7 +71,7 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
||||
switch (update['@type']) {
|
||||
case 'newMessage': {
|
||||
const {
|
||||
chatId, id, message, shouldForceReply,
|
||||
chatId, id, message, shouldForceReply, wasDrafted,
|
||||
} = update;
|
||||
global = updateWithLocalMedia(global, chatId, id, message);
|
||||
global = updateListedAndViewportIds(global, actions, message as ApiMessage);
|
||||
@ -89,6 +89,11 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
||||
|
||||
Object.values(global.byTabId).forEach(({ id: tabId }) => {
|
||||
const isLocal = isMessageLocal(message as ApiMessage);
|
||||
// Force update for last message on drafted messages to prevent flickering
|
||||
if (isLocal && wasDrafted) {
|
||||
global = updateChatLastMessage(global, chatId, newMessage);
|
||||
}
|
||||
|
||||
if (selectIsMessageInCurrentMessageList(global, chatId, message as ApiMessage, tabId)) {
|
||||
if (isLocal && message.isOutgoing && !(message.content?.action) && !storyReplyInfo?.storyId
|
||||
&& !message.content?.storyData) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user