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