Handle scheduled video processing (#5242)
This commit is contained in:
parent
8bdd24ea76
commit
afe9582b3a
@ -8,6 +8,8 @@ type VirtualFields =
|
||||
| 'classType'
|
||||
| 'getBytes';
|
||||
|
||||
export type OmitVirtualFields<T> = Omit<T, VirtualFields>;
|
||||
|
||||
export function bytesToDataUri(bytes: Buffer, shouldOmitPrefix = false, mimeType: string = 'image/jpeg') {
|
||||
const prefix = shouldOmitPrefix ? '' : `data:${mimeType};base64,`;
|
||||
|
||||
@ -16,7 +18,7 @@ export function bytesToDataUri(bytes: Buffer, shouldOmitPrefix = false, mimeType
|
||||
|
||||
export function omitVirtualClassFields<T extends GramJs.VirtualClass<T> & { flags?: any }>(
|
||||
instance: T,
|
||||
): Omit<T, VirtualFields> {
|
||||
): OmitVirtualFields<T> {
|
||||
const {
|
||||
flags,
|
||||
CONSTRUCTOR_ID,
|
||||
|
||||
@ -215,6 +215,7 @@ export function buildApiMessageWithChatId(
|
||||
const hasComments = mtpMessage.replies?.comments;
|
||||
const senderBoosts = mtpMessage.fromBoostsApplied;
|
||||
const factCheck = mtpMessage.factcheck && buildApiFactCheck(mtpMessage.factcheck);
|
||||
const isVideoProcessingPending = mtpMessage.videoProcessingPending;
|
||||
|
||||
const isInvertedMedia = mtpMessage.invertMedia;
|
||||
|
||||
@ -262,6 +263,7 @@ export function buildApiMessageWithChatId(
|
||||
factCheck,
|
||||
effectId: mtpMessage.effect?.toString(),
|
||||
isInvertedMedia,
|
||||
isVideoProcessingPending,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUse
|
||||
const {
|
||||
fullUser: {
|
||||
about, commonChatsCount, pinnedMsgId, botInfo, blocked,
|
||||
profilePhoto, voiceMessagesForbidden, premiumGifts,
|
||||
profilePhoto, voiceMessagesForbidden, premiumGifts, hasScheduled,
|
||||
fallbackPhoto, personalPhoto, translationsDisabled, storiesPinnedAvailable,
|
||||
contactRequirePremium, businessWorkHours, businessLocation, businessIntro,
|
||||
birthday, personalChannelId, personalChannelMessage, sponsoredEnabled, stargiftsCount,
|
||||
@ -51,6 +51,7 @@ export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUse
|
||||
personalChannelMessageId: personalChannelMessage,
|
||||
areAdsEnabled: sponsoredEnabled,
|
||||
starGiftCount: stargiftsCount,
|
||||
hasScheduledMessages: hasScheduled,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -511,6 +511,7 @@ async function getFullChatInfo(chatId: string): Promise<FullChatData | undefined
|
||||
chatPhoto,
|
||||
translationsDisabled,
|
||||
reactionsLimit,
|
||||
hasScheduled,
|
||||
} = result.fullChat;
|
||||
|
||||
if (chatPhoto) {
|
||||
@ -540,6 +541,7 @@ async function getFullChatInfo(chatId: string): Promise<FullChatData | undefined
|
||||
recentRequesterIds: recentRequesters?.map((userId) => buildApiPeerId(userId, 'user')),
|
||||
isTranslationDisabled: translationsDisabled,
|
||||
isPreHistoryHidden: true,
|
||||
hasScheduledMessages: hasScheduled,
|
||||
},
|
||||
chats,
|
||||
userStatusesById,
|
||||
@ -602,6 +604,7 @@ async function getFullChannelInfo(
|
||||
boostsUnrestrict,
|
||||
canViewRevenue: canViewMonetization,
|
||||
paidReactionsAvailable,
|
||||
hasScheduled,
|
||||
} = result.fullChat;
|
||||
|
||||
if (chatPhoto) {
|
||||
@ -692,6 +695,7 @@ async function getFullChannelInfo(
|
||||
boostsApplied,
|
||||
boostsToUnrestrict: boostsUnrestrict,
|
||||
isPaidReactionAvailable: paidReactionsAvailable,
|
||||
hasScheduledMessages: hasScheduled,
|
||||
},
|
||||
chats,
|
||||
userStatusesById: statusesById,
|
||||
|
||||
@ -45,7 +45,7 @@ import { getEmojiOnlyCountForMessage } from '../../../global/helpers/getEmojiOnl
|
||||
import { fetchFile } from '../../../util/files';
|
||||
import { compact, split } from '../../../util/iteratees';
|
||||
import { getMessageKey } from '../../../util/keys/messageKey';
|
||||
import { getServerTimeOffset } from '../../../util/serverTime';
|
||||
import { getServerTime, getServerTimeOffset } from '../../../util/serverTime';
|
||||
import { interpolateArray } from '../../../util/waveform';
|
||||
import {
|
||||
buildApiChatFromPreview,
|
||||
@ -1942,22 +1942,42 @@ function handleMultipleLocalMessagesUpdate(
|
||||
return;
|
||||
}
|
||||
|
||||
update.updates.forEach((u) => {
|
||||
if (u instanceof GramJs.UpdateMessageID) {
|
||||
const localMessage = localMessages[u.randomId.toString()];
|
||||
handleLocalMessageUpdate(localMessage, u);
|
||||
} else {
|
||||
handleGramJsUpdate(u);
|
||||
}
|
||||
const updateMessageIds = update.updates.filter((u): u is GramJs.UpdateMessageID => (
|
||||
u instanceof GramJs.UpdateMessageID
|
||||
));
|
||||
|
||||
// Server can return `UpdateNewScheduledMessage` that we currently process as video that requires processing
|
||||
updateMessageIds.forEach((updateMessageId) => {
|
||||
const updateNewScheduledMessage = update.updates
|
||||
.find((scheduledUpdate): scheduledUpdate is GramJs.UpdateNewScheduledMessage => {
|
||||
if (!(scheduledUpdate instanceof GramJs.UpdateNewScheduledMessage)) return false;
|
||||
return scheduledUpdate.message.id === updateMessageId.id;
|
||||
});
|
||||
|
||||
const localMessage = localMessages[updateMessageId.randomId.toString()];
|
||||
handleLocalMessageUpdate(localMessage, updateMessageId, updateNewScheduledMessage);
|
||||
});
|
||||
|
||||
const otherUpdates = update.updates.filter((u) => {
|
||||
if (u instanceof GramJs.UpdateMessageID) return false;
|
||||
if (u instanceof GramJs.UpdateNewScheduledMessage) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
handleGramJsUpdate(otherUpdates);
|
||||
}
|
||||
|
||||
function handleLocalMessageUpdate(localMessage: ApiMessage, update: GramJs.TypeUpdates) {
|
||||
function handleLocalMessageUpdate(
|
||||
localMessage: ApiMessage, update: GramJs.TypeUpdates, scheduledMessageUpdate?: GramJs.UpdateNewScheduledMessage,
|
||||
) {
|
||||
let messageUpdate;
|
||||
if (update instanceof GramJs.UpdateShortSentMessage || update instanceof GramJs.UpdateMessageID) {
|
||||
messageUpdate = update;
|
||||
} else if ('updates' in update) {
|
||||
messageUpdate = update.updates.find((u): u is GramJs.UpdateMessageID => u instanceof GramJs.UpdateMessageID);
|
||||
scheduledMessageUpdate = update.updates.find((u): u is GramJs.UpdateNewScheduledMessage => (
|
||||
u instanceof GramJs.UpdateNewScheduledMessage
|
||||
));
|
||||
}
|
||||
|
||||
if (!messageUpdate) {
|
||||
@ -1987,16 +2007,20 @@ function handleLocalMessageUpdate(localMessage: ApiMessage, update: GramJs.TypeU
|
||||
processMessageAndUpdateThreadInfo(mtpMessage);
|
||||
}
|
||||
|
||||
// Edge case for "Send When Online"
|
||||
const isSentBefore = 'date' in messageUpdate && messageUpdate.date * 1000 < Date.now() + getServerTimeOffset() * 1000;
|
||||
const newScheduledMessage = scheduledMessageUpdate?.message && buildApiMessage(scheduledMessageUpdate.message);
|
||||
|
||||
sendApiUpdate({
|
||||
'@type': localMessage.isScheduled && !isSentBefore
|
||||
? 'updateScheduledMessageSendSucceeded'
|
||||
: 'updateMessageSendSucceeded',
|
||||
chatId: localMessage.chatId,
|
||||
localId: localMessage.id,
|
||||
message: {
|
||||
// Edge case for "Send When Online"
|
||||
const isSentBefore = 'date' in messageUpdate && messageUpdate.date < getServerTime();
|
||||
|
||||
if (newScheduledMessage?.isVideoProcessingPending) {
|
||||
sendApiUpdate({
|
||||
'@type': 'updateVideoProcessingPending',
|
||||
chatId: localMessage.chatId,
|
||||
localId: localMessage.id,
|
||||
newScheduledMessageId: newScheduledMessage?.id,
|
||||
});
|
||||
} else {
|
||||
const updatedMessage: ApiMessage = {
|
||||
...localMessage,
|
||||
...(newContent && {
|
||||
content: {
|
||||
@ -2007,9 +2031,18 @@ function handleLocalMessageUpdate(localMessage: ApiMessage, update: GramJs.TypeU
|
||||
id: messageUpdate.id,
|
||||
sendingState: undefined,
|
||||
...('date' in messageUpdate && { date: messageUpdate.date }),
|
||||
},
|
||||
poll,
|
||||
});
|
||||
};
|
||||
|
||||
sendApiUpdate({
|
||||
'@type': localMessage.isScheduled && !isSentBefore
|
||||
? 'updateScheduledMessageSendSucceeded'
|
||||
: 'updateMessageSendSucceeded',
|
||||
chatId: localMessage.chatId,
|
||||
localId: localMessage.id,
|
||||
message: updatedMessage,
|
||||
poll,
|
||||
});
|
||||
}
|
||||
|
||||
handleGramJsUpdate(update);
|
||||
}
|
||||
|
||||
@ -384,6 +384,7 @@ export function updater(update: Update) {
|
||||
sendApiUpdate({
|
||||
'@type': 'deleteScheduledMessages',
|
||||
ids: update.messages,
|
||||
newIds: update.sentMessages,
|
||||
chatId: getApiChatIdFromMtpPeer(update.peer),
|
||||
});
|
||||
} else if (update instanceof GramJs.UpdateDeleteChannelMessages) {
|
||||
|
||||
@ -139,6 +139,7 @@ export interface ApiChatFullInfo {
|
||||
isTranslationDisabled?: true;
|
||||
hasPinnedStories?: boolean;
|
||||
isPaidReactionAvailable?: boolean;
|
||||
hasScheduledMessages?: boolean;
|
||||
|
||||
boostsApplied?: number;
|
||||
boostsToUnrestrict?: number;
|
||||
|
||||
@ -754,6 +754,7 @@ export interface ApiMessage {
|
||||
factCheck?: ApiFactCheck;
|
||||
effectId?: string;
|
||||
isInvertedMedia?: true;
|
||||
isVideoProcessingPending?: true;
|
||||
}
|
||||
|
||||
export interface ApiReactions {
|
||||
|
||||
@ -285,6 +285,13 @@ export type ApiUpdateMessageSendSucceeded = {
|
||||
poll?: ApiPoll;
|
||||
};
|
||||
|
||||
export type ApiUpdateVideoProcessingPending = {
|
||||
'@type': 'updateVideoProcessingPending';
|
||||
chatId: string;
|
||||
localId: number;
|
||||
newScheduledMessageId: number;
|
||||
};
|
||||
|
||||
export type ApiUpdateMessageSendFailed = {
|
||||
'@type': 'updateMessageSendFailed';
|
||||
chatId: string;
|
||||
@ -332,7 +339,8 @@ export type ApiUpdateDeleteMessages = {
|
||||
export type ApiUpdateDeleteScheduledMessages = {
|
||||
'@type': 'deleteScheduledMessages';
|
||||
ids: number[];
|
||||
chatId?: string;
|
||||
newIds?: number[];
|
||||
chatId: string;
|
||||
};
|
||||
|
||||
export type ApiUpdateDeleteHistory = {
|
||||
@ -801,7 +809,7 @@ export type ApiUpdate = (
|
||||
ApiUpdateNewMessage | ApiUpdateMessage | ApiUpdateThreadInfo | ApiUpdateCommonBoxMessages |
|
||||
ApiUpdateDeleteMessages | ApiUpdateMessagePoll | ApiUpdateMessagePollVote | ApiUpdateDeleteHistory |
|
||||
ApiUpdateMessageSendSucceeded | ApiUpdateMessageSendFailed | ApiUpdateServiceNotification |
|
||||
ApiDeleteContact | ApiUpdateUser | ApiUpdateUserStatus | ApiUpdateUserFullInfo |
|
||||
ApiDeleteContact | ApiUpdateUser | ApiUpdateUserStatus | ApiUpdateUserFullInfo | ApiUpdateVideoProcessingPending |
|
||||
ApiUpdateAvatar | ApiUpdateMessageImage | ApiUpdateDraftMessage |
|
||||
ApiUpdateError | ApiUpdateResetContacts | ApiUpdateStartEmojiInteraction |
|
||||
ApiUpdateFavoriteStickers | ApiUpdateStickerSet | ApiUpdateStickerSets | ApiUpdateStickerSetsOrder |
|
||||
|
||||
@ -60,6 +60,7 @@ export interface ApiUserFullInfo {
|
||||
businessWorkHours?: ApiBusinessWorkHours;
|
||||
businessIntro?: ApiBusinessIntro;
|
||||
starGiftCount?: number;
|
||||
hasScheduledMessages?: boolean;
|
||||
}
|
||||
|
||||
export type ApiFakeType = 'fake' | 'scam';
|
||||
|
||||
@ -1384,6 +1384,10 @@
|
||||
"CloseMiniApps" = "Close Mini Apps";
|
||||
"DoNotAskAgain" = "Don't ask again";
|
||||
"PaymentInfoDone" = "Proceed to checkout";
|
||||
"VideoConversionTitle" = "Improving Video...";
|
||||
"VideoConversionText" = "The video will be published after it's optimized for the best viewing experience.";
|
||||
"VideoConversionDone" = "Video published.";
|
||||
"VideoConversionView" = "View";
|
||||
"BotSuggestedStatusFor" = "Do you want to set this emoji status suggested by **{bot}** for **{duration}**?";
|
||||
"BotSuggestedStatus" = "Do you want to set this emoji status suggested by **{bot}**?";
|
||||
"BotSuggestedStatusTitle" = "Set Emoji Status";
|
||||
|
||||
@ -84,7 +84,6 @@ import {
|
||||
selectPerformanceSettingsValue,
|
||||
selectRequestedDraft,
|
||||
selectRequestedDraftFiles,
|
||||
selectScheduledIds,
|
||||
selectTabState,
|
||||
selectTheme,
|
||||
selectTopicFromMessage,
|
||||
@ -1771,7 +1770,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
onActivate={handleActivateBotCommandMenu}
|
||||
ariaLabel="Open bot command keyboard"
|
||||
>
|
||||
<i className="icon icon-bot-commands-filled" />
|
||||
<Icon name="bot-commands-filled" />
|
||||
</ResponsiveHoverButton>
|
||||
)}
|
||||
{canShowSendAs && (sendAsUser || sendAsChat) && (
|
||||
@ -1866,7 +1865,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
onClick={handleAllScheduledClick}
|
||||
ariaLabel="Open scheduled messages"
|
||||
>
|
||||
<i className="icon icon-schedule" />
|
||||
<Icon name="schedule" />
|
||||
</Button>
|
||||
)}
|
||||
{Boolean(botKeyboardMessageId) && !activeVoiceRecording && !editingMessage && (
|
||||
@ -1877,7 +1876,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
onActivate={openBotKeyboard}
|
||||
ariaLabel="Open bot command keyboard"
|
||||
>
|
||||
<i className="icon icon-bot-command" />
|
||||
<Icon name="bot-command" />
|
||||
</ResponsiveHoverButton>
|
||||
)}
|
||||
</>
|
||||
@ -1974,7 +1973,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
onClick={stopRecordingVoice}
|
||||
ariaLabel="Cancel voice recording"
|
||||
>
|
||||
<i className="icon icon-delete" />
|
||||
<Icon name="delete" />
|
||||
</Button>
|
||||
)}
|
||||
{isInStoryViewer && !activeVoiceRecording && (
|
||||
@ -1997,14 +1996,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
/>
|
||||
)}
|
||||
{(!sentStoryReaction || isSentStoryReactionHeart) && (
|
||||
<i
|
||||
className={buildClassName(
|
||||
'icon',
|
||||
'icon-heart',
|
||||
isSentStoryReactionHeart && 'story-reaction-heart',
|
||||
)}
|
||||
aria-hidden
|
||||
/>
|
||||
<Icon name="heart" className={buildClassName(isSentStoryReactionHeart && 'story-reaction-heart')} />
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
@ -2027,11 +2019,11 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
mainButtonState === MainButtonState.Send && canShowCustomSendMenu ? handleContextMenu : undefined
|
||||
}
|
||||
>
|
||||
<i className="icon icon-send" />
|
||||
<i className="icon icon-microphone-alt" />
|
||||
{onForward && <i className="icon icon-forward" />}
|
||||
{isInMessageList && <i className="icon icon-schedule" />}
|
||||
{isInMessageList && <i className="icon icon-check" />}
|
||||
<Icon name="send" />
|
||||
<Icon name="microphone-alt" />
|
||||
{onForward && <Icon name="forward" />}
|
||||
{isInMessageList && <Icon name="schedule" />}
|
||||
{isInMessageList && <Icon name="check" />}
|
||||
</Button>
|
||||
{effectEmoji && (
|
||||
<span className="effect-icon" onClick={handleRemoveEffect}>
|
||||
@ -2083,11 +2075,10 @@ export default memo(withGlobal<OwnProps>(
|
||||
const isChatWithBot = Boolean(chatBot);
|
||||
const isChatWithSelf = selectIsChatWithSelf(global, chatId);
|
||||
const isChatWithUser = isUserId(chatId);
|
||||
const chatBotFullInfo = isChatWithBot ? selectUserFullInfo(global, chatBot.id) : undefined;
|
||||
const userFullInfo = isChatWithUser ? selectUserFullInfo(global, chatId) : undefined;
|
||||
const chatFullInfo = !isChatWithUser ? selectChatFullInfo(global, chatId) : undefined;
|
||||
const messageWithActualBotKeyboard = (isChatWithBot || !isChatWithUser)
|
||||
&& selectNewestMessageWithBotKeyboardButtons(global, chatId, threadId);
|
||||
const scheduledIds = selectScheduledIds(global, chatId, threadId);
|
||||
const {
|
||||
language, shouldSuggestStickers, shouldSuggestCustomEmoji, shouldUpdateStickerSetOrder,
|
||||
} = global.settings.byKey;
|
||||
@ -2117,7 +2108,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
&& messageListType === currentMessageList?.type
|
||||
&& !isStoryViewerOpen;
|
||||
const user = selectUser(global, chatId);
|
||||
const canSendVoiceByPrivacy = (user && !selectUserFullInfo(global, user.id)?.noVoiceMessages) ?? true;
|
||||
const canSendVoiceByPrivacy = (user && !userFullInfo?.noVoiceMessages) ?? true;
|
||||
const slowMode = chatFullInfo?.slowMode;
|
||||
const isCurrentUserPremium = selectIsCurrentUserPremium(global);
|
||||
|
||||
@ -2140,7 +2131,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
const noWebPage = selectNoWebPage(global, chatId, threadId);
|
||||
|
||||
const isContactRequirePremium = selectUserFullInfo(global, chatId)?.isContactRequirePremium;
|
||||
const areEffectsSupported = isChatWithUser && !isChatWithBot
|
||||
&& !isInScheduledList && !isChatWithSelf && type !== 'story' && chatId !== SERVICE_NOTIFICATIONS_USER_ID;
|
||||
const canPlayEffect = selectPerformanceSettingsValue(global, 'stickerEffects');
|
||||
@ -2165,7 +2155,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
isSelectModeActive: selectIsInSelectMode(global),
|
||||
withScheduledButton: (
|
||||
messageListType === 'thread'
|
||||
&& Boolean(scheduledIds?.length)
|
||||
&& (userFullInfo || chatFullInfo)?.hasScheduledMessages
|
||||
),
|
||||
isInScheduledList,
|
||||
botKeyboardMessageId,
|
||||
@ -2187,8 +2177,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
emojiKeywords: emojiKeywords?.keywords,
|
||||
inlineBots: tabState.inlineBots.byUsername,
|
||||
isInlineBotLoading: tabState.inlineBots.isLoading,
|
||||
botCommands: chatBotFullInfo ? (chatBotFullInfo.botInfo?.commands || false) : undefined,
|
||||
botMenuButton: chatBotFullInfo?.botInfo?.menuButton,
|
||||
botCommands: userFullInfo ? (userFullInfo.botInfo?.commands || false) : undefined,
|
||||
botMenuButton: userFullInfo?.botInfo?.menuButton,
|
||||
sendAsUser,
|
||||
sendAsChat,
|
||||
sendAsId,
|
||||
@ -2218,7 +2208,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
canSendQuickReplies,
|
||||
noWebPage,
|
||||
webPagePreview: selectTabState(global).webPagePreview,
|
||||
isContactRequirePremium,
|
||||
isContactRequirePremium: userFullInfo?.isContactRequirePremium,
|
||||
effect,
|
||||
effectReactions,
|
||||
areEffectsSupported,
|
||||
|
||||
@ -163,6 +163,7 @@ const MessageMeta: FC<OwnProps> = ({
|
||||
</>
|
||||
)}
|
||||
{message.isEdited && `${lang('EditedMessage')} `}
|
||||
{message.isVideoProcessingPending && `${lang('lng_approximate')} `}
|
||||
{date}
|
||||
</span>
|
||||
{outgoingStatus && (
|
||||
|
||||
@ -249,6 +249,7 @@ const Video = <T,>({
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
disablePictureInPicture
|
||||
draggable={!isProtected}
|
||||
onTimeUpdate={handleTimeUpdate}
|
||||
onReady={markPlayerReady}
|
||||
|
||||
@ -132,7 +132,7 @@ const HeaderPinnedMessage = ({
|
||||
if (isSynced && (threadId === MAIN_THREAD_ID || chat?.isForum)) {
|
||||
loadPinnedMessages({ chatId, threadId });
|
||||
}
|
||||
}, [chatId, threadId, isSynced, chat]);
|
||||
}, [chatId, threadId, isSynced, chat?.isForum]);
|
||||
|
||||
useEnsureMessage(chatId, pinnedMessageId, pinnedMessage);
|
||||
|
||||
|
||||
@ -51,7 +51,7 @@ export const MEDIA_PROGRESSIVE_CACHE_DISABLED = false;
|
||||
export const MEDIA_PROGRESSIVE_CACHE_NAME = 'tt-media-progressive';
|
||||
export const MEDIA_CACHE_MAX_BYTES = 512 * 1024; // 512 KB
|
||||
export const CUSTOM_BG_CACHE_NAME = 'tt-custom-bg';
|
||||
export const LANG_CACHE_NAME = 'tt-lang-packs-v44';
|
||||
export const LANG_CACHE_NAME = 'tt-lang-packs-v45';
|
||||
export const ASSET_CACHE_NAME = 'tt-assets';
|
||||
export const AUTODOWNLOAD_FILESIZE_MB_LIMITS = [1, 5, 10, 50, 100, 500];
|
||||
export const DATA_BROADCAST_CHANNEL_NAME = 'tt-global';
|
||||
|
||||
@ -84,6 +84,7 @@ import {
|
||||
updateListedIds,
|
||||
updateMessageTranslation,
|
||||
updateOutlyingLists,
|
||||
updatePeerFullInfo,
|
||||
updateQuickReplies,
|
||||
updateQuickReplyMessages,
|
||||
updateRequestedMessageTranslation,
|
||||
@ -1213,6 +1214,10 @@ addActionHandler('loadScheduledHistory', async (global, actions, payload): Promi
|
||||
global = getGlobal();
|
||||
global = updateScheduledMessages(global, chat.id, byId);
|
||||
global = replaceThreadParam(global, chat.id, MAIN_THREAD_ID, 'scheduledIds', ids);
|
||||
if (!ids.length) {
|
||||
global = updatePeerFullInfo(global, chat.id, { hasScheduledMessages: false });
|
||||
}
|
||||
|
||||
if (chat?.isForum) {
|
||||
const scheduledPerThread: Record<ThreadId, number[]> = {};
|
||||
messages.forEach((message) => {
|
||||
|
||||
@ -162,6 +162,8 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const isLocal = isLocalMessageId(message.id!);
|
||||
|
||||
const chat = selectChat(global, update.chatId);
|
||||
if (!chat) {
|
||||
return undefined;
|
||||
@ -169,19 +171,21 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
||||
|
||||
const hasMention = Boolean(update.message.id && update.message.hasUnreadMention);
|
||||
|
||||
global = updateChat(global, update.chatId, {
|
||||
unreadCount: chat.unreadCount ? chat.unreadCount + 1 : 1,
|
||||
});
|
||||
|
||||
if (hasMention) {
|
||||
global = addUnreadMentions(global, update.chatId, chat, [update.message.id!], true);
|
||||
}
|
||||
|
||||
const topic = chat.isForum ? selectTopicFromMessage(global, message as ApiMessage) : undefined;
|
||||
if (topic) {
|
||||
global = updateTopic(global, update.chatId, topic.id, {
|
||||
unreadCount: topic.unreadCount ? topic.unreadCount + 1 : 1,
|
||||
if (!isLocal) {
|
||||
global = updateChat(global, update.chatId, {
|
||||
unreadCount: chat.unreadCount ? chat.unreadCount + 1 : 1,
|
||||
});
|
||||
|
||||
if (hasMention) {
|
||||
global = addUnreadMentions(global, update.chatId, chat, [update.message.id!], true);
|
||||
}
|
||||
|
||||
const topic = chat.isForum ? selectTopicFromMessage(global, message as ApiMessage) : undefined;
|
||||
if (topic) {
|
||||
global = updateTopic(global, update.chatId, topic.id, {
|
||||
unreadCount: topic.unreadCount ? topic.unreadCount + 1 : 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setGlobal(global);
|
||||
|
||||
@ -43,6 +43,7 @@ import {
|
||||
updateChatMessage,
|
||||
updateListedIds,
|
||||
updateMessageTranslations,
|
||||
updatePeerFullInfo,
|
||||
updatePoll,
|
||||
updatePollVote,
|
||||
updateQuickReplies,
|
||||
@ -87,6 +88,8 @@ import {
|
||||
|
||||
const ANIMATION_DELAY = 350;
|
||||
const SNAP_ANIMATION_DELAY = 1000;
|
||||
const VIDEO_PROCESSING_NOTIFICATION_DELAY = 1000;
|
||||
let lastVideoProcessingNotificationTime = 0;
|
||||
|
||||
addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
||||
switch (update['@type']) {
|
||||
@ -233,6 +236,10 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
||||
global = updatePoll(global, poll.id, poll);
|
||||
}
|
||||
|
||||
global = updatePeerFullInfo(global, chatId, {
|
||||
hasScheduledMessages: true,
|
||||
});
|
||||
|
||||
setGlobal(global);
|
||||
|
||||
break;
|
||||
@ -335,6 +342,49 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'updateVideoProcessingPending': {
|
||||
const {
|
||||
chatId, localId, newScheduledMessageId,
|
||||
} = update;
|
||||
|
||||
global = deleteChatMessages(global, chatId, [localId]);
|
||||
global = updatePeerFullInfo(global, chatId, {
|
||||
hasScheduledMessages: true,
|
||||
});
|
||||
|
||||
setGlobal(global);
|
||||
|
||||
Object.values(global.byTabId).forEach(({ id: tabId }) => {
|
||||
const currentMessageList = selectCurrentMessageList(global, tabId);
|
||||
if (currentMessageList?.chatId !== chatId) return;
|
||||
|
||||
const now = Date.now();
|
||||
if (now - lastVideoProcessingNotificationTime < VIDEO_PROCESSING_NOTIFICATION_DELAY) {
|
||||
return;
|
||||
}
|
||||
lastVideoProcessingNotificationTime = now;
|
||||
|
||||
actions.showNotification({
|
||||
message: {
|
||||
key: 'VideoConversionText',
|
||||
},
|
||||
title: {
|
||||
key: 'VideoConversionTitle',
|
||||
},
|
||||
tabId,
|
||||
});
|
||||
|
||||
actions.focusMessage({
|
||||
chatId,
|
||||
messageId: newScheduledMessageId,
|
||||
messageListType: 'scheduled',
|
||||
tabId,
|
||||
});
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'updateMessageSendSucceeded': {
|
||||
const {
|
||||
chatId, localId, message, poll,
|
||||
@ -524,7 +574,37 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
||||
}
|
||||
|
||||
case 'deleteScheduledMessages': {
|
||||
const { ids, chatId } = update;
|
||||
const { ids, newIds, chatId } = update;
|
||||
|
||||
const hadVideoProcessing = ids?.some((id) => (
|
||||
selectScheduledMessage(global, chatId, id)?.isVideoProcessingPending
|
||||
));
|
||||
const processedVideoId = newIds?.find((id) => {
|
||||
const message = selectChatMessage(global, chatId, id);
|
||||
return message?.content.video;
|
||||
});
|
||||
|
||||
if (hadVideoProcessing && processedVideoId) {
|
||||
Object.values(global.byTabId).forEach(({ id: tabId }) => {
|
||||
actions.showNotification({
|
||||
message: {
|
||||
key: 'VideoConversionDone',
|
||||
},
|
||||
actionText: {
|
||||
key: 'VideoConversionView',
|
||||
},
|
||||
action: {
|
||||
action: 'focusMessage',
|
||||
payload: {
|
||||
chatId,
|
||||
messageId: processedVideoId,
|
||||
tabId,
|
||||
},
|
||||
},
|
||||
tabId,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
deleteScheduledMessages(chatId, ids, actions, global);
|
||||
break;
|
||||
@ -1150,12 +1230,8 @@ export function deleteMessages<T extends GlobalState>(
|
||||
}
|
||||
|
||||
function deleteScheduledMessages<T extends GlobalState>(
|
||||
chatId: string | undefined, ids: number[], actions: RequiredGlobalActions, global: T,
|
||||
chatId: string, ids: number[], actions: RequiredGlobalActions, global: T,
|
||||
) {
|
||||
if (!chatId) {
|
||||
return;
|
||||
}
|
||||
|
||||
ids.forEach((id) => {
|
||||
global = updateScheduledMessage(global, chatId, id, {
|
||||
isDeleting: true,
|
||||
|
||||
4
src/types/language.d.ts
vendored
4
src/types/language.d.ts
vendored
@ -1153,6 +1153,10 @@ export interface LangPair {
|
||||
'CloseMiniApps': undefined;
|
||||
'DoNotAskAgain': undefined;
|
||||
'PaymentInfoDone': undefined;
|
||||
'VideoConversionTitle': undefined;
|
||||
'VideoConversionText': undefined;
|
||||
'VideoConversionDone': undefined;
|
||||
'VideoConversionView': undefined;
|
||||
'BotSuggestedStatusTitle': undefined;
|
||||
'BotSuggestedStatusUpdated': undefined;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user