Media: Allow sending messages while uploading (#3170)

This commit is contained in:
Alexander Zinchuk 2023-05-28 14:32:18 +02:00
parent d25892caa4
commit a953869683
8 changed files with 33 additions and 38 deletions

View File

@ -43,7 +43,6 @@ import {
import {
DELETED_COMMENTS_CHANNEL_ID,
LOCAL_MESSAGE_MIN_ID,
SERVICE_NOTIFICATIONS_USER_ID,
SPONSORED_MESSAGE_CACHE_MS,
SUPPORTED_AUDIO_CONTENT_TYPES,
@ -65,22 +64,14 @@ import { buildApiCallDiscardReason } from './calls';
import { getEmojiOnlyCountForMessage } from '../../../global/helpers/getEmojiOnlyCountForMessage';
import { getServerTimeOffset } from '../../../util/serverTime';
const TIMESTAMP_BASE = 1676e9; // 2023-02-10
const TIMESTAMP_PRECISION = 1e2; // 0.1s
const LOCAL_MESSAGES_LIMIT = 1e6; // 1M
const LOCAL_MEDIA_UPLOADING_TEMP_ID = 'temp';
const INPUT_WAVEFORM_LENGTH = 63;
let localMessageCounter = LOCAL_MESSAGE_MIN_ID;
// Local IDs need to be fractional to allow service notifications to be placed between real messages.
// It also allows to avoid collisions when sending messages from multiple tabs due to timestamp-based whole part.
// To support up to 1M local messages, the whole part must be below 8.5B (https://stackoverflow.com/a/57225494/903919).
// The overflow will happen when `datePart` is >3.59B which will be in June 2034.
function getNextLocalMessageId() {
const datePart = Math.round((Date.now() - TIMESTAMP_BASE) / TIMESTAMP_PRECISION);
return LOCAL_MESSAGE_MIN_ID + datePart + (++localMessageCounter / LOCAL_MESSAGES_LIMIT);
let localMessageCounter = 0;
function getNextLocalMessageId(lastMessageId = 0) {
return lastMessageId + (++localMessageCounter / LOCAL_MESSAGES_LIMIT);
}
let currentUserId!: string;
@ -1308,7 +1299,7 @@ export function buildLocalMessage(
scheduledAt?: number,
sendAs?: ApiChat | ApiUser,
): ApiMessage {
const localId = getNextLocalMessageId();
const localId = getNextLocalMessageId(chat.lastMessage?.id);
const media = attachment && buildUploadingMedia(attachment);
const isChannel = chat.type === 'chatTypeChannel';
const isForum = chat.isForum;

View File

@ -214,7 +214,7 @@ export async function fetchMessage({ chat, messageId }: { chat: ApiChat; message
return { message, users };
}
let queue = Promise.resolve();
let mediaQueue = Promise.resolve();
export function sendMessage(
{
@ -236,6 +236,7 @@ export function sendMessage(
shouldUpdateStickerSetOrder,
}: {
chat: ApiChat;
lastMessageId?: number;
text?: string;
entities?: ApiMessageEntity[];
replyingTo?: number;
@ -306,8 +307,7 @@ export function sendMessage(
}, randomId, localMessage, onProgress);
}
const prevQueue = queue;
queue = (async () => {
const sendPromise = (async () => {
let media: GramJs.TypeInputMedia | undefined;
if (attachment) {
try {
@ -318,7 +318,7 @@ export function sendMessage(
console.warn(err);
}
await prevQueue;
await mediaQueue;
return;
}
@ -337,8 +337,6 @@ export function sendMessage(
});
}
await prevQueue;
const RequestClass = media ? GramJs.messages.SendMedia : GramJs.messages.SendMessage;
try {
@ -369,7 +367,7 @@ export function sendMessage(
}
})();
return queue;
return sendPromise;
}
const groupedUploads: Record<string, {
@ -417,8 +415,8 @@ function sendGroupedMedia(
groupIndex = groupedUploads[groupedId].counter++;
const prevQueue = queue;
queue = (async () => {
const prevMediaQueue = mediaQueue;
mediaQueue = (async () => {
let media;
try {
media = await uploadMedia(localMessage, attachment, onProgress!);
@ -430,7 +428,7 @@ function sendGroupedMedia(
groupedUploads[groupedId].counter--;
await prevQueue;
await prevMediaQueue;
return;
}
@ -440,7 +438,7 @@ function sendGroupedMedia(
media as GramJs.InputMediaUploadedPhoto | GramJs.InputMediaUploadedDocument,
);
await prevQueue;
await prevMediaQueue;
if (!inputMedia) {
groupedUploads[groupedId].counter--;
@ -482,7 +480,7 @@ function sendGroupedMedia(
if (update) handleMultipleLocalMessagesUpdate(localMessages, update);
})();
return queue;
return mediaQueue;
}
async function fetchInputMedia(

View File

@ -1,6 +1,8 @@
import type { RefObject } from 'react';
import React, {
memo, useRef, useState, useMemo,
} from '../../lib/teact/teact';
import type { FC } from '../../lib/teact/teact';
import React, { memo, useRef, useState } from '../../lib/teact/teact';
import { IS_CANVAS_FILTER_SUPPORTED } from '../../util/windowEnvironment';
import buildClassName from '../../util/buildClassName';
@ -20,7 +22,7 @@ import Link from '../ui/Link';
import './File.scss';
type OwnProps = {
ref?: RefObject<HTMLDivElement>;
ref?: React.RefObject<HTMLDivElement>;
name: string;
extension?: string;
size: number;
@ -80,6 +82,10 @@ const File: FC<OwnProps> = ({
const color = getColorFromExtension(extension);
const sizeString = getFileSizeString(size);
const subtitle = useMemo(() => {
if (!isTransferring || !transferProgress) return sizeString;
return `${getFileSizeString(size * transferProgress)} / ${sizeString}`;
}, [isTransferring, size, sizeString, transferProgress]);
const { width, height } = getDocumentThumbnailDimensions(smaller);
@ -146,7 +152,7 @@ const File: FC<OwnProps> = ({
<div className="file-title" dir="auto" title={name}>{renderText(name)}</div>
<div className="file-subtitle" dir="auto">
<span>
{isTransferring && transferProgress ? `${Math.round(transferProgress * 100)}%` : sizeString}
{subtitle}
</span>
{sender && <span className="file-sender">{renderText(sender)}</span>}
{!sender && Boolean(timestamp) && (

View File

@ -22,7 +22,6 @@ import { LoadMoreDirection } from '../../types';
import {
ANIMATION_END_DELAY,
LOCAL_MESSAGE_MIN_ID,
MESSAGE_LIST_SLICE,
SERVICE_NOTIFICATIONS_USER_ID,
} from '../../config';
@ -58,6 +57,7 @@ import {
getDocumentMediaHash,
getVideoDimensions,
getPhotoFullDimensions,
isLocalMessageId,
} from '../../global/helpers';
import { orderBy } from '../../util/iteratees';
import { DPR } from '../../util/windowEnvironment';
@ -366,7 +366,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
}
// Loading history while sending a message can return the same message and cause ambiguity
const isLastMessageLocal = messageIds && messageIds[messageIds.length - 1] > LOCAL_MESSAGE_MIN_ID;
const isLastMessageLocal = messageIds && isLocalMessageId(messageIds[messageIds.length - 1]);
if (isLastMessageLocal) {
return;
}

View File

@ -9,9 +9,10 @@ import { LoadMoreDirection } from '../../../types';
import type { MessageListType } from '../../../global/types';
import type { Signal } from '../../../util/signals';
import { LOCAL_MESSAGE_MIN_ID } from '../../../config';
import { MESSAGE_LIST_SENSITIVE_AREA } from '../../../util/windowEnvironment';
import { debounce } from '../../../util/schedulers';
import { isLocalMessageId } from '../../../global/helpers';
import { useIntersectionObserver, useOnIntersect } from '../../../hooks/useIntersectionObserver';
import useSyncEffect from '../../../hooks/useSyncEffect';
import { useStateRef } from '../../../hooks/useStateRef';
@ -94,7 +95,7 @@ export default function useScrollHooks(
}
// Loading history while sending a message can return the same message and cause ambiguity
const isFirstMessageLocal = messageIds[0] > LOCAL_MESSAGE_MIN_ID;
const isFirstMessageLocal = isLocalMessageId(messageIds[0]);
if (isFirstMessageLocal) {
return;
}

View File

@ -139,7 +139,6 @@ export const MOBILE_SCREEN_MAX_WIDTH = 600; // px
export const MOBILE_SCREEN_LANDSCAPE_MAX_WIDTH = 950; // px
export const MOBILE_SCREEN_LANDSCAPE_MAX_HEIGHT = 450; // px
export const LOCAL_MESSAGE_MIN_ID = 5e9;
export const MAX_INT_32 = 2 ** 31 - 1;
export const TMP_CHAT_ID = '0';

View File

@ -6,7 +6,6 @@ import type { LangFn } from '../../hooks/useLang';
import {
CONTENT_NOT_SUPPORTED,
LOCAL_MESSAGE_MIN_ID,
RE_LINK_TEMPLATE,
SERVICE_NOTIFICATIONS_USER_ID,
} from '../../config';
@ -199,7 +198,7 @@ export function isMessageLocal(message: ApiMessage) {
}
export function isLocalMessageId(id: number) {
return id > LOCAL_MESSAGE_MIN_ID;
return !Number.isInteger(id);
}
export function isHistoryClearMessage(message: ApiMessage) {

View File

@ -12,7 +12,7 @@ import type {
import { ApiMessageEntityTypes, MAIN_THREAD_ID } from '../../api/types';
import {
GENERAL_TOPIC_ID, LOCAL_MESSAGE_MIN_ID, REPLIES_USER_ID, SERVICE_NOTIFICATIONS_USER_ID,
GENERAL_TOPIC_ID, REPLIES_USER_ID, SERVICE_NOTIFICATIONS_USER_ID,
} from '../../config';
import {
selectChat, selectChatBot, selectChatFullInfo, selectIsChatWithSelf,
@ -46,6 +46,7 @@ import {
isUserRightBanned,
canSendReaction,
getAllowedAttachmentOptions,
isLocalMessageId,
} from '../helpers';
import { findLast } from '../../util/iteratees';
import { selectIsStickerFavorite } from './symbols';
@ -324,7 +325,7 @@ export function selectIsViewportNewest<T extends GlobalState>(
}
// Edge case: outgoing `lastMessage` is updated with a delay to optimize animation
if (lastMessageId > LOCAL_MESSAGE_MIN_ID && !selectChatMessage(global, chatId, lastMessageId)) {
if (isLocalMessageId(lastMessageId) && !selectChatMessage(global, chatId, lastMessageId)) {
return true;
}