Media: Allow sending messages while uploading (#3170)
This commit is contained in:
parent
d25892caa4
commit
a953869683
@ -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;
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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) && (
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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';
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user