Layer 205: Introduce Checklists (#6010)
Co-authored-by: zubiden <19638254+zubiden@users.noreply.github.com>
This commit is contained in:
parent
bcaeee1d94
commit
9bfd2d569c
@ -9,6 +9,9 @@ import {
|
|||||||
SERVICE_NOTIFICATIONS_USER_ID,
|
SERVICE_NOTIFICATIONS_USER_ID,
|
||||||
STORY_EXPIRE_PERIOD,
|
STORY_EXPIRE_PERIOD,
|
||||||
STORY_VIEWERS_EXPIRE_PERIOD,
|
STORY_VIEWERS_EXPIRE_PERIOD,
|
||||||
|
TODO_ITEM_LENGTH_LIMIT,
|
||||||
|
TODO_ITEMS_LIMIT,
|
||||||
|
TODO_TITLE_LENGTH_LIMIT,
|
||||||
} from '../../../config';
|
} from '../../../config';
|
||||||
import localDb from '../localDb';
|
import localDb from '../localDb';
|
||||||
import { buildJson } from './misc';
|
import { buildJson } from './misc';
|
||||||
@ -99,6 +102,9 @@ export interface GramJsAppConfig extends LimitsConfig {
|
|||||||
stars_stargift_resale_amount_min?: number;
|
stars_stargift_resale_amount_min?: number;
|
||||||
stars_stargift_resale_commission_permille?: number;
|
stars_stargift_resale_commission_permille?: number;
|
||||||
poll_answers_max?: number;
|
poll_answers_max?: number;
|
||||||
|
todo_items_max?: number;
|
||||||
|
todo_title_length_max?: number;
|
||||||
|
todo_item_length_max?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildEmojiSounds(appConfig: GramJsAppConfig) {
|
function buildEmojiSounds(appConfig: GramJsAppConfig) {
|
||||||
@ -200,5 +206,8 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
|
|||||||
starsStargiftResaleAmountMax: appConfig.stars_stargift_resale_amount_max,
|
starsStargiftResaleAmountMax: appConfig.stars_stargift_resale_amount_max,
|
||||||
starsStargiftResaleCommissionPermille: appConfig.stars_stargift_resale_commission_permille,
|
starsStargiftResaleCommissionPermille: appConfig.stars_stargift_resale_commission_permille,
|
||||||
pollMaxAnswers: appConfig.poll_answers_max,
|
pollMaxAnswers: appConfig.poll_answers_max,
|
||||||
|
todoItemsMax: appConfig.todo_items_max ?? TODO_ITEMS_LIMIT,
|
||||||
|
todoTitleLengthMax: appConfig.todo_title_length_max ?? TODO_TITLE_LENGTH_LIMIT,
|
||||||
|
todoItemLengthMax: appConfig.todo_item_length_max ?? TODO_ITEM_LENGTH_LIMIT,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import type { ApiMessageAction } from '../../types/messageActions';
|
|||||||
import { buildApiBotApp } from './bots';
|
import { buildApiBotApp } from './bots';
|
||||||
import { buildApiFormattedText, buildApiPhoto } from './common';
|
import { buildApiFormattedText, buildApiPhoto } from './common';
|
||||||
import { buildApiStarGift } from './gifts';
|
import { buildApiStarGift } from './gifts';
|
||||||
|
import { buildTodoItem } from './messageContent';
|
||||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
||||||
|
|
||||||
const UNSUPPORTED_ACTION: ApiMessageAction = {
|
const UNSUPPORTED_ACTION: ApiMessageAction = {
|
||||||
@ -446,6 +447,25 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
|
|||||||
count,
|
count,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (action instanceof GramJs.MessageActionTodoCompletions) {
|
||||||
|
const {
|
||||||
|
completed, incompleted,
|
||||||
|
} = action;
|
||||||
|
return {
|
||||||
|
mediaType: 'action',
|
||||||
|
type: 'todoCompletions',
|
||||||
|
completedIds: completed,
|
||||||
|
incompletedIds: incompleted,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action instanceof GramJs.MessageActionTodoAppendTasks) {
|
||||||
|
const { list } = action;
|
||||||
|
return {
|
||||||
|
mediaType: 'action',
|
||||||
|
type: 'todoAppendTasks',
|
||||||
|
items: list.map(buildTodoItem),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return UNSUPPORTED_ACTION;
|
return UNSUPPORTED_ACTION;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,12 +11,14 @@ import type {
|
|||||||
ApiLocation,
|
ApiLocation,
|
||||||
ApiMediaExtendedPreview,
|
ApiMediaExtendedPreview,
|
||||||
ApiMediaInvoice,
|
ApiMediaInvoice,
|
||||||
|
ApiMediaTodo,
|
||||||
ApiMessageStoryData,
|
ApiMessageStoryData,
|
||||||
ApiPaidMedia,
|
ApiPaidMedia,
|
||||||
ApiPhoto,
|
ApiPhoto,
|
||||||
ApiPoll,
|
ApiPoll,
|
||||||
ApiStarGiftUnique,
|
ApiStarGiftUnique,
|
||||||
ApiSticker,
|
ApiSticker,
|
||||||
|
ApiTodoItem,
|
||||||
ApiVideo,
|
ApiVideo,
|
||||||
ApiVoice,
|
ApiVoice,
|
||||||
ApiWebDocument,
|
ApiWebDocument,
|
||||||
@ -64,7 +66,7 @@ export function buildMessageContent(
|
|||||||
const hasUnsupportedMedia = mtpMessage.media instanceof GramJs.MessageMediaUnsupported;
|
const hasUnsupportedMedia = mtpMessage.media instanceof GramJs.MessageMediaUnsupported;
|
||||||
|
|
||||||
if (mtpMessage.message && !hasUnsupportedMedia
|
if (mtpMessage.message && !hasUnsupportedMedia
|
||||||
&& !content.sticker && !content.pollId && !content.contact && !content.video?.isRound) {
|
&& !content.sticker && !content.pollId && !content.todo && !content.contact && !content.video?.isRound) {
|
||||||
const text = buildMessageTextContent(mtpMessage.message, mtpMessage.entities);
|
const text = buildMessageTextContent(mtpMessage.message, mtpMessage.entities);
|
||||||
const textWithTimestamps = addTimestampEntities(text);
|
const textWithTimestamps = addTimestampEntities(text);
|
||||||
content = {
|
content = {
|
||||||
@ -153,6 +155,9 @@ export function buildMessageMediaContent(
|
|||||||
const pollId = buildPollIdFromMedia(media);
|
const pollId = buildPollIdFromMedia(media);
|
||||||
if (pollId) return { pollId };
|
if (pollId) return { pollId };
|
||||||
|
|
||||||
|
const todo = buildTodoFromMedia(media);
|
||||||
|
if (todo) return { todo };
|
||||||
|
|
||||||
const webPage = buildWebPage(media);
|
const webPage = buildWebPage(media);
|
||||||
if (webPage) return { webPage };
|
if (webPage) return { webPage };
|
||||||
|
|
||||||
@ -513,6 +518,14 @@ export function buildPollFromMedia(media: GramJs.TypeMessageMedia): ApiPoll | un
|
|||||||
return buildPoll(media.poll, media.results);
|
return buildPoll(media.poll, media.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildTodoFromMedia(media: GramJs.TypeMessageMedia): ApiMediaTodo | undefined {
|
||||||
|
if (!(media instanceof GramJs.MessageMediaToDo)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildTodo(media.todo, media.completions);
|
||||||
|
}
|
||||||
|
|
||||||
function buildInvoiceFromMedia(media: GramJs.TypeMessageMedia): ApiMediaInvoice | undefined {
|
function buildInvoiceFromMedia(media: GramJs.TypeMessageMedia): ApiMediaInvoice | undefined {
|
||||||
if (!(media instanceof GramJs.MessageMediaInvoice)) {
|
if (!(media instanceof GramJs.MessageMediaInvoice)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -711,6 +724,36 @@ export function buildPoll(poll: GramJs.Poll, pollResults: GramJs.PollResults): A
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildTodoItem(item: GramJs.TodoItem): ApiTodoItem {
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
title: buildApiFormattedText(item.title),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildTodo(todo: GramJs.TodoList, completions?: GramJs.TodoCompletion[]): ApiMediaTodo {
|
||||||
|
const { title, list: items } = todo;
|
||||||
|
|
||||||
|
const todoItems = items.map(buildTodoItem);
|
||||||
|
|
||||||
|
const todoCompletions = completions?.map((completion) => ({
|
||||||
|
itemId: completion.id,
|
||||||
|
completedBy: completion.completedBy.toString(),
|
||||||
|
completedAt: completion.date,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
mediaType: 'todo',
|
||||||
|
todo: {
|
||||||
|
title: buildApiFormattedText(title),
|
||||||
|
items: todoItems,
|
||||||
|
othersCanAppend: todo.othersCanAppend,
|
||||||
|
othersCanComplete: todo.othersCanComplete,
|
||||||
|
},
|
||||||
|
completions: todoCompletions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function buildMediaInvoice(media: GramJs.MessageMediaInvoice): ApiMediaInvoice {
|
export function buildMediaInvoice(media: GramJs.MessageMediaInvoice): ApiMediaInvoice {
|
||||||
const {
|
const {
|
||||||
description, title, photo, test, totalAmount, currency, receiptMsgId, extendedMedia,
|
description, title, photo, test, totalAmount, currency, receiptMsgId, extendedMedia,
|
||||||
|
|||||||
@ -8,10 +8,12 @@ import type {
|
|||||||
ApiFactCheck,
|
ApiFactCheck,
|
||||||
ApiInputMessageReplyInfo,
|
ApiInputMessageReplyInfo,
|
||||||
ApiInputReplyInfo,
|
ApiInputReplyInfo,
|
||||||
|
ApiMediaTodo,
|
||||||
ApiMessage,
|
ApiMessage,
|
||||||
ApiMessageEntity,
|
ApiMessageEntity,
|
||||||
ApiMessageForwardInfo,
|
ApiMessageForwardInfo,
|
||||||
ApiMessageReportResult,
|
ApiMessageReportResult,
|
||||||
|
ApiNewMediaTodo,
|
||||||
ApiNewPoll,
|
ApiNewPoll,
|
||||||
ApiPeer,
|
ApiPeer,
|
||||||
ApiPhoto,
|
ApiPhoto,
|
||||||
@ -382,6 +384,13 @@ function buildNewPoll(poll: ApiNewPoll, localId: number): ApiPoll {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildNewTodo(todo: ApiNewMediaTodo): ApiMediaTodo {
|
||||||
|
return {
|
||||||
|
mediaType: 'todo',
|
||||||
|
todo: todo.todo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function buildLocalMessage(
|
export function buildLocalMessage(
|
||||||
chat: ApiChat,
|
chat: ApiChat,
|
||||||
lastMessageId?: number,
|
lastMessageId?: number,
|
||||||
@ -392,6 +401,7 @@ export function buildLocalMessage(
|
|||||||
sticker?: ApiSticker,
|
sticker?: ApiSticker,
|
||||||
gif?: ApiVideo,
|
gif?: ApiVideo,
|
||||||
poll?: ApiNewPoll,
|
poll?: ApiNewPoll,
|
||||||
|
todo?: ApiNewMediaTodo,
|
||||||
contact?: ApiContact,
|
contact?: ApiContact,
|
||||||
groupedId?: string,
|
groupedId?: string,
|
||||||
scheduledAt?: number,
|
scheduledAt?: number,
|
||||||
@ -409,6 +419,7 @@ export function buildLocalMessage(
|
|||||||
const resultReplyInfo = replyInfo && buildReplyInfo(replyInfo, chat.isForum);
|
const resultReplyInfo = replyInfo && buildReplyInfo(replyInfo, chat.isForum);
|
||||||
|
|
||||||
const localPoll = poll && buildNewPoll(poll, localId);
|
const localPoll = poll && buildNewPoll(poll, localId);
|
||||||
|
const localTodo = todo && buildNewTodo(todo);
|
||||||
|
|
||||||
const formattedText = text ? addTimestampEntities({ text, entities }) : undefined;
|
const formattedText = text ? addTimestampEntities({ text, entities }) : undefined;
|
||||||
|
|
||||||
@ -423,6 +434,7 @@ export function buildLocalMessage(
|
|||||||
contact,
|
contact,
|
||||||
storyData: story && { mediaType: 'storyData', ...story },
|
storyData: story && { mediaType: 'storyData', ...story },
|
||||||
pollId: localPoll?.id,
|
pollId: localPoll?.id,
|
||||||
|
todo: localTodo,
|
||||||
}),
|
}),
|
||||||
date: scheduledAt || Math.round(Date.now() / 1000) + getServerTimeOffset(),
|
date: scheduledAt || Math.round(Date.now() / 1000) + getServerTimeOffset(),
|
||||||
isOutgoing: !isChannel,
|
isOutgoing: !isChannel,
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import type {
|
|||||||
ApiInputReplyInfo,
|
ApiInputReplyInfo,
|
||||||
ApiInputStorePaymentPurpose,
|
ApiInputStorePaymentPurpose,
|
||||||
ApiMessageEntity,
|
ApiMessageEntity,
|
||||||
|
ApiNewMediaTodo,
|
||||||
ApiNewPoll,
|
ApiNewPoll,
|
||||||
ApiPhoneCall,
|
ApiPhoneCall,
|
||||||
ApiPhoto,
|
ApiPhoto,
|
||||||
@ -263,6 +264,28 @@ export function buildInputPollFromExisting(poll: ApiPoll, shouldClose = false) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildInputTodo(todo: ApiNewMediaTodo) {
|
||||||
|
const { title, items } = todo.todo;
|
||||||
|
|
||||||
|
const todoItems = items.map((item) => {
|
||||||
|
return new GramJs.TodoItem({
|
||||||
|
id: item.id,
|
||||||
|
title: buildInputTextWithEntities(item.title),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const todoList = new GramJs.TodoList({
|
||||||
|
title: buildInputTextWithEntities(title),
|
||||||
|
list: todoItems,
|
||||||
|
othersCanAppend: todo.todo.othersCanAppend || undefined,
|
||||||
|
othersCanComplete: todo.todo.othersCanComplete || undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
return new GramJs.InputMediaTodo({
|
||||||
|
todo: todoList,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function buildFilterFromApiFolder(folder: ApiChatFolder): GramJs.DialogFilter | GramJs.DialogFilterChatlist {
|
export function buildFilterFromApiFolder(folder: ApiChatFolder): GramJs.DialogFilter | GramJs.DialogFilterChatlist {
|
||||||
const {
|
const {
|
||||||
emoticon,
|
emoticon,
|
||||||
|
|||||||
@ -18,11 +18,13 @@ import type {
|
|||||||
ApiMessageEntity,
|
ApiMessageEntity,
|
||||||
ApiMessageSearchContext,
|
ApiMessageSearchContext,
|
||||||
ApiMessageSearchType,
|
ApiMessageSearchType,
|
||||||
|
ApiNewMediaTodo,
|
||||||
ApiOnProgress,
|
ApiOnProgress,
|
||||||
ApiPeer,
|
ApiPeer,
|
||||||
ApiPoll,
|
ApiPoll,
|
||||||
ApiReaction,
|
ApiReaction,
|
||||||
ApiSendMessageAction,
|
ApiSendMessageAction,
|
||||||
|
ApiTodoItem,
|
||||||
ApiUser,
|
ApiUser,
|
||||||
ApiUserStatus,
|
ApiUserStatus,
|
||||||
MediaContent,
|
MediaContent,
|
||||||
@ -47,7 +49,7 @@ import {
|
|||||||
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 { getServerTime, getServerTimeOffset } from '../../../util/serverTime';
|
import { getServerTime } from '../../../util/serverTime';
|
||||||
import { interpolateArray } from '../../../util/waveform';
|
import { interpolateArray } from '../../../util/waveform';
|
||||||
import {
|
import {
|
||||||
buildApiChatFromPreview,
|
buildApiChatFromPreview,
|
||||||
@ -82,6 +84,7 @@ import {
|
|||||||
buildInputReplyTo,
|
buildInputReplyTo,
|
||||||
buildInputStory,
|
buildInputStory,
|
||||||
buildInputTextWithEntities,
|
buildInputTextWithEntities,
|
||||||
|
buildInputTodo,
|
||||||
buildInputUser,
|
buildInputUser,
|
||||||
buildMessageFromUpdate,
|
buildMessageFromUpdate,
|
||||||
buildMtpMessageEntity,
|
buildMtpMessageEntity,
|
||||||
@ -263,7 +266,7 @@ export function sendMessageLocal(
|
|||||||
params: SendMessageParams,
|
params: SendMessageParams,
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
chat, lastMessageId, text, entities, replyInfo, attachment, sticker, story, gif, poll, contact,
|
chat, lastMessageId, text, entities, replyInfo, attachment, sticker, story, gif, poll, todo, contact,
|
||||||
scheduledAt, groupedId, sendAs, wasDrafted, isInvertedMedia, effectId, isPending, messagePriceInStars,
|
scheduledAt, groupedId, sendAs, wasDrafted, isInvertedMedia, effectId, isPending, messagePriceInStars,
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
@ -282,6 +285,7 @@ export function sendMessageLocal(
|
|||||||
sticker,
|
sticker,
|
||||||
gif,
|
gif,
|
||||||
poll,
|
poll,
|
||||||
|
todo,
|
||||||
contact,
|
contact,
|
||||||
groupedId,
|
groupedId,
|
||||||
scheduledAt,
|
scheduledAt,
|
||||||
@ -311,7 +315,7 @@ export function sendApiMessage(
|
|||||||
onProgress?: ApiOnProgress,
|
onProgress?: ApiOnProgress,
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
chat, text, entities, replyInfo, attachment, sticker, story, gif, poll, contact,
|
chat, text, entities, replyInfo, attachment, sticker, story, gif, poll, todo, contact,
|
||||||
isSilent, scheduledAt, groupedId, noWebPage, sendAs, shouldUpdateStickerSetOrder,
|
isSilent, scheduledAt, groupedId, noWebPage, sendAs, shouldUpdateStickerSetOrder,
|
||||||
isInvertedMedia, effectId, webPageMediaSize, webPageUrl, messagePriceInStars,
|
isInvertedMedia, effectId, webPageMediaSize, webPageUrl, messagePriceInStars,
|
||||||
} = params;
|
} = params;
|
||||||
@ -368,6 +372,8 @@ export function sendApiMessage(
|
|||||||
media = buildInputMediaDocument(gif);
|
media = buildInputMediaDocument(gif);
|
||||||
} else if (poll) {
|
} else if (poll) {
|
||||||
media = buildInputPoll(poll, randomId);
|
media = buildInputPoll(poll, randomId);
|
||||||
|
} else if (todo) {
|
||||||
|
media = buildInputTodo(todo);
|
||||||
} else if (story) {
|
} else if (story) {
|
||||||
media = buildInputStory(story);
|
media = buildInputStory(story);
|
||||||
} else if (webPageUrl && webPageMediaSize) {
|
} else if (webPageUrl && webPageMediaSize) {
|
||||||
@ -629,7 +635,7 @@ export async function editMessage({
|
|||||||
attachment?: ApiAttachment;
|
attachment?: ApiAttachment;
|
||||||
noWebPage?: boolean;
|
noWebPage?: boolean;
|
||||||
}, onProgress?: ApiOnProgress) {
|
}, onProgress?: ApiOnProgress) {
|
||||||
const isScheduled = message.date * 1000 > Date.now() + getServerTimeOffset() * 1000;
|
const isScheduled = message.date * 1000 > getServerTime() * 1000;
|
||||||
|
|
||||||
const media = attachment && buildUploadingMedia(attachment);
|
const media = attachment && buildUploadingMedia(attachment);
|
||||||
|
|
||||||
@ -702,6 +708,110 @@ export async function editMessage({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function editTodo({
|
||||||
|
chat,
|
||||||
|
message,
|
||||||
|
todo,
|
||||||
|
}: {
|
||||||
|
chat: ApiChat;
|
||||||
|
message: ApiMessage;
|
||||||
|
todo: ApiNewMediaTodo;
|
||||||
|
}) {
|
||||||
|
const media = buildInputTodo(todo);
|
||||||
|
const isScheduled = message.date * 1000 > getServerTime() * 1000;
|
||||||
|
|
||||||
|
const newContent: MediaContent = {
|
||||||
|
...message.content,
|
||||||
|
todo: {
|
||||||
|
mediaType: 'todo',
|
||||||
|
todo: todo.todo,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const messageUpdate: Partial<ApiMessage> = {
|
||||||
|
...message,
|
||||||
|
content: newContent,
|
||||||
|
};
|
||||||
|
|
||||||
|
sendApiUpdate({
|
||||||
|
'@type': isScheduled ? 'updateScheduledMessage' : 'updateMessage',
|
||||||
|
id: message.id,
|
||||||
|
chatId: chat.id,
|
||||||
|
message: messageUpdate,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await invokeRequest(new GramJs.messages.EditMessage({
|
||||||
|
media,
|
||||||
|
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||||
|
id: message.id,
|
||||||
|
}), { shouldThrow: true });
|
||||||
|
} catch (err) {
|
||||||
|
if (DEBUG) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { message: messageErr } = err as Error;
|
||||||
|
|
||||||
|
sendApiUpdate({
|
||||||
|
'@type': 'error',
|
||||||
|
error: {
|
||||||
|
message: messageErr,
|
||||||
|
hasErrorKey: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rollback changes
|
||||||
|
sendApiUpdate({
|
||||||
|
'@type': 'updateMessage',
|
||||||
|
id: message.id,
|
||||||
|
chatId: chat.id,
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function appendTodoList({
|
||||||
|
chat,
|
||||||
|
message,
|
||||||
|
items,
|
||||||
|
}: {
|
||||||
|
chat: ApiChat;
|
||||||
|
message: ApiMessage;
|
||||||
|
items: ApiTodoItem[];
|
||||||
|
}) {
|
||||||
|
const todoItems = items.map((item) => {
|
||||||
|
return new GramJs.TodoItem({
|
||||||
|
id: item.id,
|
||||||
|
title: buildInputTextWithEntities(item.title),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await invokeRequest(new GramJs.messages.AppendTodoList({
|
||||||
|
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||||
|
msgId: message.id,
|
||||||
|
list: todoItems,
|
||||||
|
}), { shouldThrow: true });
|
||||||
|
} catch (err) {
|
||||||
|
if (DEBUG) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { message: messageErr } = err as Error;
|
||||||
|
|
||||||
|
sendApiUpdate({
|
||||||
|
'@type': 'error',
|
||||||
|
error: {
|
||||||
|
message: messageErr,
|
||||||
|
hasErrorKey: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function rescheduleMessage({
|
export async function rescheduleMessage({
|
||||||
chat,
|
chat,
|
||||||
message,
|
message,
|
||||||
@ -1507,6 +1617,24 @@ export async function sendPollVote({
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function toggleTodoCompleted({
|
||||||
|
chat, messageId, completedIds, incompletedIds,
|
||||||
|
}: {
|
||||||
|
chat: ApiChat;
|
||||||
|
messageId: number;
|
||||||
|
completedIds: number[];
|
||||||
|
incompletedIds: number[];
|
||||||
|
}) {
|
||||||
|
const { id, accessHash } = chat;
|
||||||
|
|
||||||
|
await invokeRequest(new GramJs.messages.ToggleTodoCompleted({
|
||||||
|
peer: buildInputPeer(id, accessHash),
|
||||||
|
msgId: messageId,
|
||||||
|
completed: completedIds,
|
||||||
|
incompleted: incompletedIds,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
export async function closePoll({
|
export async function closePoll({
|
||||||
chat, messageId, poll,
|
chat, messageId, poll,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@ -252,11 +252,11 @@ export async function deleteContact({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addNoPaidMessagesException({ user, shouldRefundCharged }: {
|
export async function toggleNoPaidMessagesException({ user, shouldRefundCharged }: {
|
||||||
user: ApiUser;
|
user: ApiUser;
|
||||||
shouldRefundCharged?: boolean;
|
shouldRefundCharged?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const result = await invokeRequest(new GramJs.account.AddNoPaidMessagesException({
|
const result = await invokeRequest(new GramJs.account.ToggleNoPaidMessagesException ({
|
||||||
refundCharged: shouldRefundCharged ? true : undefined,
|
refundCharged: shouldRefundCharged ? true : undefined,
|
||||||
userId: buildInputUser(user.id, user.accessHash),
|
userId: buildInputUser(user.id, user.accessHash),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import type { ApiGroupCall, ApiPhoneCallDiscardReason } from './calls';
|
import type { ApiGroupCall, ApiPhoneCallDiscardReason } from './calls';
|
||||||
import type { ApiBotApp, ApiFormattedText, ApiPhoto } from './messages';
|
import type { ApiBotApp, ApiFormattedText, ApiPhoto } from './messages';
|
||||||
|
import type { ApiTodoItem } from './messages';
|
||||||
import type { ApiStarGiftRegular, ApiStarGiftUnique } from './stars';
|
import type { ApiStarGiftRegular, ApiStarGiftUnique } from './stars';
|
||||||
|
|
||||||
interface ActionMediaType {
|
interface ActionMediaType {
|
||||||
@ -281,6 +282,17 @@ export interface ApiMessageActionPaidMessagesPrice extends ActionMediaType {
|
|||||||
isAllowedInChannel?: boolean;
|
isAllowedInChannel?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApiMessageActionTodoCompletions extends ActionMediaType {
|
||||||
|
type: 'todoCompletions';
|
||||||
|
completedIds: number[];
|
||||||
|
incompletedIds: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiMessageActionTodoAppendTasks extends ActionMediaType {
|
||||||
|
type: 'todoAppendTasks';
|
||||||
|
items: ApiTodoItem[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface ApiMessageActionUnsupported extends ActionMediaType {
|
export interface ApiMessageActionUnsupported extends ActionMediaType {
|
||||||
type: 'unsupported';
|
type: 'unsupported';
|
||||||
}
|
}
|
||||||
@ -298,4 +310,5 @@ export type ApiMessageAction = ApiMessageActionUnsupported | ApiMessageActionCha
|
|||||||
| ApiMessageActionChannelJoined | ApiMessageActionGiftCode | ApiMessageActionGiveawayLaunch
|
| ApiMessageActionChannelJoined | ApiMessageActionGiftCode | ApiMessageActionGiveawayLaunch
|
||||||
| ApiMessageActionGiveawayResults | ApiMessageActionPaymentRefunded | ApiMessageActionGiftStars
|
| ApiMessageActionGiveawayResults | ApiMessageActionPaymentRefunded | ApiMessageActionGiftStars
|
||||||
| ApiMessageActionPrizeStars | ApiMessageActionStarGift | ApiMessageActionStarGiftUnique
|
| ApiMessageActionPrizeStars | ApiMessageActionStarGift | ApiMessageActionStarGiftUnique
|
||||||
| ApiMessageActionPaidMessagesRefunded | ApiMessageActionPaidMessagesPrice;
|
| ApiMessageActionPaidMessagesRefunded | ApiMessageActionPaidMessagesPrice | ApiMessageActionTodoCompletions
|
||||||
|
| ApiMessageActionTodoAppendTasks;
|
||||||
|
|||||||
@ -332,6 +332,34 @@ export type ApiNewPoll = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface ApiTodoItem {
|
||||||
|
id: number;
|
||||||
|
title: ApiFormattedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiTodoList {
|
||||||
|
title: ApiFormattedText;
|
||||||
|
items: ApiTodoItem[];
|
||||||
|
othersCanAppend?: boolean;
|
||||||
|
othersCanComplete?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiTodoCompletion {
|
||||||
|
itemId: number;
|
||||||
|
completedBy: string;
|
||||||
|
completedAt: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiMediaTodo {
|
||||||
|
mediaType: 'todo';
|
||||||
|
todo: ApiTodoList;
|
||||||
|
completions?: ApiTodoCompletion[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiNewMediaTodo = {
|
||||||
|
todo: ApiTodoList;
|
||||||
|
};
|
||||||
|
|
||||||
export interface ApiWebPage {
|
export interface ApiWebPage {
|
||||||
mediaType: 'webpage';
|
mediaType: 'webpage';
|
||||||
id: number;
|
id: number;
|
||||||
@ -516,6 +544,7 @@ export type MediaContent = {
|
|||||||
sticker?: ApiSticker;
|
sticker?: ApiSticker;
|
||||||
contact?: ApiContact;
|
contact?: ApiContact;
|
||||||
pollId?: string;
|
pollId?: string;
|
||||||
|
todo?: ApiMediaTodo;
|
||||||
action?: ApiMessageAction;
|
action?: ApiMessageAction;
|
||||||
webPage?: ApiWebPage;
|
webPage?: ApiWebPage;
|
||||||
audio?: ApiAudio;
|
audio?: ApiAudio;
|
||||||
|
|||||||
@ -253,6 +253,9 @@ export interface ApiAppConfig {
|
|||||||
starsStargiftResaleAmountMax?: number;
|
starsStargiftResaleAmountMax?: number;
|
||||||
starsStargiftResaleCommissionPermille?: number;
|
starsStargiftResaleCommissionPermille?: number;
|
||||||
pollMaxAnswers?: number;
|
pollMaxAnswers?: number;
|
||||||
|
todoItemsMax?: number;
|
||||||
|
todoTitleLengthMax?: number;
|
||||||
|
todoItemLengthMax?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiConfig {
|
export interface ApiConfig {
|
||||||
|
|||||||
@ -68,6 +68,7 @@
|
|||||||
"PremiumPreviewVoiceToTextDescription" = "Ability to read the transcript of any incoming voice message.";
|
"PremiumPreviewVoiceToTextDescription" = "Ability to read the transcript of any incoming voice message.";
|
||||||
"PremiumPreviewProfileBadgeDescription" = "An exclusive badge next to your name showing that you subscribe to Telegram Premium.";
|
"PremiumPreviewProfileBadgeDescription" = "An exclusive badge next to your name showing that you subscribe to Telegram Premium.";
|
||||||
"PremiumPreviewDownloadSpeedDescription" = "No more limits on the speed with which media and documents are downloaded.";
|
"PremiumPreviewDownloadSpeedDescription" = "No more limits on the speed with which media and documents are downloaded.";
|
||||||
|
"PremiumPreviewTodoDescription" = "Plan, assign, and complete tasks - seamlessly and efficiently.";
|
||||||
"PremiumPreviewUploadsDescription" = "4 GB per each document, unlimited storage for your chats and media overall.";
|
"PremiumPreviewUploadsDescription" = "4 GB per each document, unlimited storage for your chats and media overall.";
|
||||||
"PremiumPreviewAdvancedChatManagementDescription" = "Tools to set the default folder, auto-archive and hide new chats from non-contacts.";
|
"PremiumPreviewAdvancedChatManagementDescription" = "Tools to set the default folder, auto-archive and hide new chats from non-contacts.";
|
||||||
"PremiumPreviewAnimatedProfilesDescription" = "Video avatars animated in chat lists and chats to allow for additional self-expression.";
|
"PremiumPreviewAnimatedProfilesDescription" = "Video avatars animated in chat lists and chats to allow for additional self-expression.";
|
||||||
@ -2024,3 +2025,41 @@
|
|||||||
"MonoforumComposerPlaceholder" = "Choose a message to reply";
|
"MonoforumComposerPlaceholder" = "Choose a message to reply";
|
||||||
"ChannelSendMessage" = "Direct Messages";
|
"ChannelSendMessage" = "Direct Messages";
|
||||||
"AutomaticTranslation" = "Automatic Translation";
|
"AutomaticTranslation" = "Automatic Translation";
|
||||||
|
"TitleNewToDoList" = "New Checklist";
|
||||||
|
"TitleEditToDoList" = "Edit Checklist";
|
||||||
|
"TitleAppendToDoList" = "Append Checklist";
|
||||||
|
"InputTitle" = "Title";
|
||||||
|
"TitleToDoList" = "Checklist";
|
||||||
|
"TitleTask" = "Task";
|
||||||
|
"TitleAddTask" = "Add a task";
|
||||||
|
"AllowOthersAddTasks" = "Allow Others to Add Tasks";
|
||||||
|
"AllowOthersMarkAsDone" = "Allow Others to Mark As Done";
|
||||||
|
"AriaToDoCancel" = "Cancel checklist creation";
|
||||||
|
"TitleGroupToDoList" = "Group Checklist";
|
||||||
|
"TitleUserToDoList" = "{peer}'s Checklist";
|
||||||
|
"TitleYourToDoList" = "Your Checklist";
|
||||||
|
"DescriptionCompletedToDoTasks" = "{number} of {count} completed";
|
||||||
|
"MessageActionTodoCompletionsAsDone" = "{peer} marked \"{task}\" as done";
|
||||||
|
"MessageActionTodoCompletionsAsDoneYou" = "You marked \"{task}\" as done";
|
||||||
|
"MessageActionTodoCompletionsAsDoneMultiple" = "{peer} marked {tasks} as done";
|
||||||
|
"MessageActionTodoCompletionsAsDoneMultipleYou" = "You marked {tasks} as done";
|
||||||
|
"MessageActionTodoCompletionsAsNotDone" = "{peer} marked \"{task}\" as not done";
|
||||||
|
"MessageActionTodoCompletionsAsNotDoneYou" = "You marked \"{task}\" as not done";
|
||||||
|
"MessageActionTodoCompletionsAsNotDoneMultiple" = "{peer} marked {tasks} as not done";
|
||||||
|
"MessageActionTodoCompletionsAsNotDoneMultipleYou" = "You marked {tasks} as not done";
|
||||||
|
"MessageActionTodoTaskCount_one" = "{count} task";
|
||||||
|
"MessageActionTodoTaskCount_other" = "{count} tasks";
|
||||||
|
"ToDoListNewTasks" = "New Tasks";
|
||||||
|
"MenuButtonAppendTodoList" = "Add a Task";
|
||||||
|
"MessageActionAppendTodo" = "{peer} added a new task \"{task}\" to {list}";
|
||||||
|
"MessageActionAppendTodoYou" = "You added a new task \"{task}\" to {list}";
|
||||||
|
"MessageActionAppendTodoMultiple" = "{peer} added {tasks} to {list}";
|
||||||
|
"MessageActionAppendTodoMultipleYou" = "You added {tasks} to {list}";
|
||||||
|
"PremiumMore" = "More";
|
||||||
|
"SubscribeToTelegramPremiumForToggleTask" = "Subscribe to **Telegram Premium** to toggle tasks";
|
||||||
|
"SubscribeToTelegramPremiumForCreateToDo" = "Subscribe to **Telegram Premium** to create Checklists";
|
||||||
|
"SubscribeToTelegramPremiumForAppendToDo" = "Subscribe to **Telegram Premium** to append Checklists";
|
||||||
|
"HintTodoListTasksCount" = "You can add {count} more tasks";
|
||||||
|
"ToDoListErrorChooseTitle" = "Please enter a title.";
|
||||||
|
"ToDoListErrorChooseTasks" = "Please enter at least one task.";
|
||||||
|
"PremiumPreviewTodo" = "Checklists";
|
||||||
|
|||||||
@ -68,6 +68,7 @@ export { default as ReactionPicker } from '../components/middle/message/reaction
|
|||||||
|
|
||||||
export { default as AttachmentModal } from '../components/middle/composer/AttachmentModal';
|
export { default as AttachmentModal } from '../components/middle/composer/AttachmentModal';
|
||||||
export { default as PollModal } from '../components/middle/composer/PollModal';
|
export { default as PollModal } from '../components/middle/composer/PollModal';
|
||||||
|
export { default as ToDoListModal } from '../components/middle/composer/ToDoListModal';
|
||||||
export { default as SymbolMenu } from '../components/middle/composer/SymbolMenu';
|
export { default as SymbolMenu } from '../components/middle/composer/SymbolMenu';
|
||||||
export { default as ChatCommandTooltip } from '../components/middle/composer/ChatCommandTooltip';
|
export { default as ChatCommandTooltip } from '../components/middle/composer/ChatCommandTooltip';
|
||||||
export { default as BotCommandMenu } from '../components/middle/composer/BotCommandMenu';
|
export { default as BotCommandMenu } from '../components/middle/composer/BotCommandMenu';
|
||||||
|
|||||||
@ -220,7 +220,7 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
|
||||||
font-size: smaller;
|
font-size: 0.875rem;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
||||||
background: var(--color-primary);
|
background: var(--color-primary);
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import type {
|
|||||||
ApiFormattedText,
|
ApiFormattedText,
|
||||||
ApiMessage,
|
ApiMessage,
|
||||||
ApiMessageEntity,
|
ApiMessageEntity,
|
||||||
|
ApiNewMediaTodo,
|
||||||
ApiNewPoll,
|
ApiNewPoll,
|
||||||
ApiPeer,
|
ApiPeer,
|
||||||
ApiQuickReply,
|
ApiQuickReply,
|
||||||
@ -178,6 +179,7 @@ import PollModal from '../middle/composer/PollModal.async';
|
|||||||
import SendAsMenu from '../middle/composer/SendAsMenu.async';
|
import SendAsMenu from '../middle/composer/SendAsMenu.async';
|
||||||
import StickerTooltip from '../middle/composer/StickerTooltip.async';
|
import StickerTooltip from '../middle/composer/StickerTooltip.async';
|
||||||
import SymbolMenuButton from '../middle/composer/SymbolMenuButton';
|
import SymbolMenuButton from '../middle/composer/SymbolMenuButton';
|
||||||
|
import ToDoListModal from '../middle/composer/ToDoListModal.async';
|
||||||
import WebPagePreview from '../middle/composer/WebPagePreview';
|
import WebPagePreview from '../middle/composer/WebPagePreview';
|
||||||
import MessageEffect from '../middle/message/MessageEffect';
|
import MessageEffect from '../middle/message/MessageEffect';
|
||||||
import ReactionSelector from '../middle/message/reactions/ReactionSelector';
|
import ReactionSelector from '../middle/message/reactions/ReactionSelector';
|
||||||
@ -235,6 +237,7 @@ type StateProps =
|
|||||||
isForwarding?: boolean;
|
isForwarding?: boolean;
|
||||||
forwardedMessagesCount?: number;
|
forwardedMessagesCount?: number;
|
||||||
pollModal: TabState['pollModal'];
|
pollModal: TabState['pollModal'];
|
||||||
|
todoListModal: TabState['todoListModal'];
|
||||||
botKeyboardMessageId?: number;
|
botKeyboardMessageId?: number;
|
||||||
botKeyboardPlaceholder?: string;
|
botKeyboardPlaceholder?: string;
|
||||||
withScheduledButton?: boolean;
|
withScheduledButton?: boolean;
|
||||||
@ -357,6 +360,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
isForwarding,
|
isForwarding,
|
||||||
forwardedMessagesCount,
|
forwardedMessagesCount,
|
||||||
pollModal,
|
pollModal,
|
||||||
|
todoListModal,
|
||||||
botKeyboardMessageId,
|
botKeyboardMessageId,
|
||||||
botKeyboardPlaceholder,
|
botKeyboardPlaceholder,
|
||||||
inputPlaceholder,
|
inputPlaceholder,
|
||||||
@ -433,6 +437,8 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
showDialog,
|
showDialog,
|
||||||
openPollModal,
|
openPollModal,
|
||||||
closePollModal,
|
closePollModal,
|
||||||
|
openTodoListModal,
|
||||||
|
closeTodoListModal,
|
||||||
loadScheduledHistory,
|
loadScheduledHistory,
|
||||||
openThread,
|
openThread,
|
||||||
addRecentEmoji,
|
addRecentEmoji,
|
||||||
@ -540,7 +546,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
const [nextText, setNextText] = useState<ApiFormattedText | undefined>(undefined);
|
const [nextText, setNextText] = useState<ApiFormattedText | undefined>(undefined);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
canSendStickers, canSendGifs, canAttachMedia, canAttachPolls, canAttachEmbedLinks,
|
canSendStickers, canSendGifs, canAttachMedia, canAttachPolls, canAttachEmbedLinks, canAttachToDoLists,
|
||||||
canSendVoices, canSendPlainText, canSendAudios, canSendVideos, canSendPhotos, canSendDocuments,
|
canSendVoices, canSendPlainText, canSendAudios, canSendVideos, canSendPhotos, canSendDocuments,
|
||||||
} = useMemo(
|
} = useMemo(
|
||||||
() => getAllowedAttachmentOptions(chat,
|
() => getAllowedAttachmentOptions(chat,
|
||||||
@ -1216,6 +1222,22 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
handleActionWithPaymentConfirmation(handleSend, isSilent, scheduledAt);
|
handleActionWithPaymentConfirmation(handleSend, isSilent, scheduledAt);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleTodoListCreate = useLastCallback(() => {
|
||||||
|
if (!isCurrentUserPremium) {
|
||||||
|
showNotification({
|
||||||
|
message: lang('SubscribeToTelegramPremiumForCreateToDo'),
|
||||||
|
action: {
|
||||||
|
action: 'openPremiumModal',
|
||||||
|
payload: { initialSection: 'todo' },
|
||||||
|
},
|
||||||
|
actionText: lang('PremiumMore'),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
openTodoListModal({ chatId });
|
||||||
|
});
|
||||||
|
|
||||||
const handleClickBotMenu = useLastCallback(() => {
|
const handleClickBotMenu = useLastCallback(() => {
|
||||||
if (botMenuButton?.type !== 'webApp') {
|
if (botMenuButton?.type !== 'webApp') {
|
||||||
return;
|
return;
|
||||||
@ -1455,6 +1477,28 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleToDoListSend = useLastCallback((todo: ApiNewMediaTodo) => {
|
||||||
|
if (!currentMessageList) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInScheduledList) {
|
||||||
|
requestCalendar((scheduledAt) => {
|
||||||
|
handleActionWithPaymentConfirmation(
|
||||||
|
handleMessageSchedule,
|
||||||
|
{ todo },
|
||||||
|
scheduledAt,
|
||||||
|
currentMessageList,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
handleActionWithPaymentConfirmation(
|
||||||
|
sendMessage,
|
||||||
|
{ messageList: currentMessageList, todo, isSilent: isSilentPosting },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const sendSilent = useLastCallback((additionalArgs?: ScheduledMessageArgs) => {
|
const sendSilent = useLastCallback((additionalArgs?: ScheduledMessageArgs) => {
|
||||||
if (isInScheduledList) {
|
if (isInScheduledList) {
|
||||||
requestCalendar((scheduledAt) => {
|
requestCalendar((scheduledAt) => {
|
||||||
@ -1878,6 +1922,11 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
onClear={closePollModal}
|
onClear={closePollModal}
|
||||||
onSend={handlePollSend}
|
onSend={handlePollSend}
|
||||||
/>
|
/>
|
||||||
|
<ToDoListModal
|
||||||
|
modal={todoListModal}
|
||||||
|
onClear={closeTodoListModal}
|
||||||
|
onSend={handleToDoListSend}
|
||||||
|
/>
|
||||||
<SendAsMenu
|
<SendAsMenu
|
||||||
isOpen={isSendAsMenuOpen}
|
isOpen={isSendAsMenuOpen}
|
||||||
onClose={closeSendAsMenu}
|
onClose={closeSendAsMenu}
|
||||||
@ -2147,12 +2196,14 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
isButtonVisible={!activeVoiceRecording}
|
isButtonVisible={!activeVoiceRecording}
|
||||||
canAttachMedia={canAttachMedia}
|
canAttachMedia={canAttachMedia}
|
||||||
canAttachPolls={canAttachPolls}
|
canAttachPolls={canAttachPolls}
|
||||||
|
canAttachToDoLists={canAttachToDoLists}
|
||||||
canSendPhotos={canSendPhotos}
|
canSendPhotos={canSendPhotos}
|
||||||
canSendVideos={canSendVideos}
|
canSendVideos={canSendVideos}
|
||||||
canSendDocuments={canSendDocuments}
|
canSendDocuments={canSendDocuments}
|
||||||
canSendAudios={canSendAudios}
|
canSendAudios={canSendAudios}
|
||||||
onFileSelect={handleFileSelect}
|
onFileSelect={handleFileSelect}
|
||||||
onPollCreate={openPollModal}
|
onPollCreate={openPollModal}
|
||||||
|
onTodoListCreate={handleTodoListCreate}
|
||||||
isScheduled={isInScheduledList}
|
isScheduled={isInScheduledList}
|
||||||
attachBots={isInMessageList ? attachBots : undefined}
|
attachBots={isInMessageList ? attachBots : undefined}
|
||||||
peerType={attachMenuPeerType}
|
peerType={attachMenuPeerType}
|
||||||
@ -2458,6 +2509,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
isForwarding,
|
isForwarding,
|
||||||
forwardedMessagesCount: isForwarding ? forwardMessageIds!.length : undefined,
|
forwardedMessagesCount: isForwarding ? forwardMessageIds!.length : undefined,
|
||||||
pollModal: tabState.pollModal,
|
pollModal: tabState.pollModal,
|
||||||
|
todoListModal: tabState.todoListModal,
|
||||||
stickersForEmoji: global.stickers.forEmoji.stickers,
|
stickersForEmoji: global.stickers.forEmoji.stickers,
|
||||||
customEmojiForEmoji: global.customEmojis.forEmoji.stickers,
|
customEmojiForEmoji: global.customEmojis.forEmoji.stickers,
|
||||||
chatFullInfo,
|
chatFullInfo,
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import type {
|
|||||||
ApiPremiumSubscriptionOption,
|
ApiPremiumSubscriptionOption,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import type { GlobalState } from '../../../global/types';
|
import type { GlobalState } from '../../../global/types';
|
||||||
|
import type { LangPair } from '../../../types/language';
|
||||||
|
|
||||||
import { PREMIUM_BOTTOM_VIDEOS, PREMIUM_FEATURE_SECTIONS, PREMIUM_LIMITS_ORDER } from '../../../config';
|
import { PREMIUM_BOTTOM_VIDEOS, PREMIUM_FEATURE_SECTIONS, PREMIUM_LIMITS_ORDER } from '../../../config';
|
||||||
import { requestMutation } from '../../../lib/fasterdom/fasterdom';
|
import { requestMutation } from '../../../lib/fasterdom/fasterdom';
|
||||||
@ -55,6 +56,7 @@ export const PREMIUM_FEATURE_TITLES: Record<ApiPremiumSection, string> = {
|
|||||||
last_seen: 'PremiumPreviewLastSeen',
|
last_seen: 'PremiumPreviewLastSeen',
|
||||||
message_privacy: 'PremiumPreviewMessagePrivacy',
|
message_privacy: 'PremiumPreviewMessagePrivacy',
|
||||||
effects: 'Premium.MessageEffects',
|
effects: 'Premium.MessageEffects',
|
||||||
|
todo: 'PremiumPreviewTodo',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PREMIUM_FEATURE_DESCRIPTIONS: Record<ApiPremiumSection, string> = {
|
export const PREMIUM_FEATURE_DESCRIPTIONS: Record<ApiPremiumSection, string> = {
|
||||||
@ -76,6 +78,7 @@ export const PREMIUM_FEATURE_DESCRIPTIONS: Record<ApiPremiumSection, string> = {
|
|||||||
last_seen: 'PremiumPreviewLastSeenDescription',
|
last_seen: 'PremiumPreviewLastSeenDescription',
|
||||||
message_privacy: 'PremiumPreviewMessagePrivacyDescription',
|
message_privacy: 'PremiumPreviewMessagePrivacyDescription',
|
||||||
effects: 'Premium.MessageEffectsInfo',
|
effects: 'Premium.MessageEffectsInfo',
|
||||||
|
todo: 'PremiumPreviewTodoDescription',
|
||||||
};
|
};
|
||||||
|
|
||||||
const LIMITS_TITLES: Record<ApiLimitTypeForPromo, string> = {
|
const LIMITS_TITLES: Record<ApiLimitTypeForPromo, string> = {
|
||||||
@ -290,6 +293,7 @@ const PremiumFeatureModal: FC<OwnProps> = ({
|
|||||||
|
|
||||||
const i = promo.videoSections.indexOf(section);
|
const i = promo.videoSections.indexOf(section);
|
||||||
if (i === -1) return undefined;
|
if (i === -1) return undefined;
|
||||||
|
const shouldUseNewLang = promo.videoSections[i] === 'todo';
|
||||||
return (
|
return (
|
||||||
<div className={styles.slide}>
|
<div className={styles.slide}>
|
||||||
<div className={styles.frame}>
|
<div className={styles.frame}>
|
||||||
@ -303,10 +307,23 @@ const PremiumFeatureModal: FC<OwnProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<h1 className={styles.title}>
|
<h1 className={styles.title}>
|
||||||
{oldLang(PREMIUM_FEATURE_TITLES[promo.videoSections[i]])}
|
{shouldUseNewLang
|
||||||
|
? lang(
|
||||||
|
PREMIUM_FEATURE_TITLES[promo.videoSections[i]] as keyof LangPair,
|
||||||
|
undefined,
|
||||||
|
{ withNodes: true, renderTextFilters: ['br'] },
|
||||||
|
)
|
||||||
|
: oldLang(PREMIUM_FEATURE_TITLES[promo.videoSections[i]])}
|
||||||
</h1>
|
</h1>
|
||||||
<div className={styles.description}>
|
<div className={styles.description}>
|
||||||
{renderText(oldLang(PREMIUM_FEATURE_DESCRIPTIONS[promo.videoSections[i]]), ['br'])}
|
{renderText(shouldUseNewLang
|
||||||
|
? lang(
|
||||||
|
PREMIUM_FEATURE_DESCRIPTIONS[promo.videoSections[i]] as keyof LangPair,
|
||||||
|
undefined,
|
||||||
|
{ withNodes: true, renderTextFilters: ['br'] },
|
||||||
|
)
|
||||||
|
: oldLang(PREMIUM_FEATURE_DESCRIPTIONS[promo.videoSections[i]]), ['br'],
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import type {
|
|||||||
ApiPremiumPromo, ApiPremiumSection, ApiPremiumSubscriptionOption, ApiSticker, ApiStickerSet, ApiUser,
|
ApiPremiumPromo, ApiPremiumSection, ApiPremiumSubscriptionOption, ApiSticker, ApiStickerSet, ApiUser,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import type { GlobalState } from '../../../global/types';
|
import type { GlobalState } from '../../../global/types';
|
||||||
|
import type { LangPair } from '../../../types/language';
|
||||||
|
|
||||||
import { PREMIUM_FEATURE_SECTIONS, TME_LINK_PREFIX } from '../../../config';
|
import { PREMIUM_FEATURE_SECTIONS, TME_LINK_PREFIX } from '../../../config';
|
||||||
import { getUserFullName } from '../../../global/helpers';
|
import { getUserFullName } from '../../../global/helpers';
|
||||||
@ -83,6 +84,7 @@ const PREMIUM_FEATURE_COLOR_ICONS: Record<ApiPremiumSection, string> = {
|
|||||||
last_seen: PremiumLastSeen,
|
last_seen: PremiumLastSeen,
|
||||||
message_privacy: PremiumMessagePrivacy,
|
message_privacy: PremiumMessagePrivacy,
|
||||||
effects: PremiumEffects,
|
effects: PremiumEffects,
|
||||||
|
todo: PremiumBadge,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
@ -148,8 +150,10 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
|||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
setHeaderHidden(true);
|
setHeaderHidden(true);
|
||||||
setCurrentSection(undefined);
|
setCurrentSection(undefined);
|
||||||
|
} else if (initialSection) {
|
||||||
|
setCurrentSection(initialSection);
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen, initialSection]);
|
||||||
|
|
||||||
const handleOpenSection = useLastCallback((section: ApiPremiumSection) => {
|
const handleOpenSection = useLastCallback((section: ApiPremiumSection) => {
|
||||||
setCurrentSection(section);
|
setCurrentSection(section);
|
||||||
@ -401,14 +405,19 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className={buildClassName(styles.list, isPremium && styles.noButton)}>
|
<div className={buildClassName(styles.list, isPremium && styles.noButton)}>
|
||||||
{filteredSections.map((section, index) => {
|
{filteredSections.map((section, index) => {
|
||||||
|
const shouldUseNewLang = section === 'todo';
|
||||||
return (
|
return (
|
||||||
<PremiumFeatureItem
|
<PremiumFeatureItem
|
||||||
key={section}
|
key={section}
|
||||||
title={oldLang(PREMIUM_FEATURE_TITLES[section])}
|
title={shouldUseNewLang
|
||||||
|
? lang(PREMIUM_FEATURE_TITLES[section] as keyof LangPair)
|
||||||
|
: oldLang(PREMIUM_FEATURE_TITLES[section])}
|
||||||
text={section === 'double_limits'
|
text={section === 'double_limits'
|
||||||
? oldLang(PREMIUM_FEATURE_DESCRIPTIONS[section],
|
? oldLang(PREMIUM_FEATURE_DESCRIPTIONS[section],
|
||||||
[limitChannels, limitFolders, limitPins, limitLinks, LIMIT_ACCOUNTS])
|
[limitChannels, limitFolders, limitPins, limitLinks, LIMIT_ACCOUNTS])
|
||||||
: oldLang(PREMIUM_FEATURE_DESCRIPTIONS[section])}
|
: shouldUseNewLang
|
||||||
|
? lang(PREMIUM_FEATURE_DESCRIPTIONS[section] as keyof LangPair)
|
||||||
|
: oldLang(PREMIUM_FEATURE_DESCRIPTIONS[section])}
|
||||||
icon={PREMIUM_FEATURE_COLOR_ICONS[section]}
|
icon={PREMIUM_FEATURE_COLOR_ICONS[section]}
|
||||||
index={index}
|
index={index}
|
||||||
count={filteredSections.length}
|
count={filteredSections.length}
|
||||||
|
|||||||
@ -47,6 +47,7 @@ export type OwnProps = {
|
|||||||
isButtonVisible: boolean;
|
isButtonVisible: boolean;
|
||||||
canAttachMedia: boolean;
|
canAttachMedia: boolean;
|
||||||
canAttachPolls: boolean;
|
canAttachPolls: boolean;
|
||||||
|
canAttachToDoLists: boolean;
|
||||||
canSendPhotos: boolean;
|
canSendPhotos: boolean;
|
||||||
canSendVideos: boolean;
|
canSendVideos: boolean;
|
||||||
canSendDocuments: boolean;
|
canSendDocuments: boolean;
|
||||||
@ -58,6 +59,7 @@ export type OwnProps = {
|
|||||||
theme: ThemeKey;
|
theme: ThemeKey;
|
||||||
onFileSelect: (files: File[]) => void;
|
onFileSelect: (files: File[]) => void;
|
||||||
onPollCreate: NoneToVoidFunction;
|
onPollCreate: NoneToVoidFunction;
|
||||||
|
onTodoListCreate: NoneToVoidFunction;
|
||||||
onMenuOpen: NoneToVoidFunction;
|
onMenuOpen: NoneToVoidFunction;
|
||||||
onMenuClose: NoneToVoidFunction;
|
onMenuClose: NoneToVoidFunction;
|
||||||
canEditMedia?: boolean;
|
canEditMedia?: boolean;
|
||||||
@ -72,6 +74,7 @@ const AttachMenu: FC<OwnProps> = ({
|
|||||||
isButtonVisible,
|
isButtonVisible,
|
||||||
canAttachMedia,
|
canAttachMedia,
|
||||||
canAttachPolls,
|
canAttachPolls,
|
||||||
|
canAttachToDoLists,
|
||||||
canSendPhotos,
|
canSendPhotos,
|
||||||
canSendVideos,
|
canSendVideos,
|
||||||
canSendDocuments,
|
canSendDocuments,
|
||||||
@ -85,6 +88,7 @@ const AttachMenu: FC<OwnProps> = ({
|
|||||||
onMenuOpen,
|
onMenuOpen,
|
||||||
onMenuClose,
|
onMenuClose,
|
||||||
onPollCreate,
|
onPollCreate,
|
||||||
|
onTodoListCreate,
|
||||||
canEditMedia,
|
canEditMedia,
|
||||||
editingMessage,
|
editingMessage,
|
||||||
messageListType,
|
messageListType,
|
||||||
@ -263,6 +267,9 @@ const AttachMenu: FC<OwnProps> = ({
|
|||||||
{canAttachPolls && !editingMessage && (
|
{canAttachPolls && !editingMessage && (
|
||||||
<MenuItem icon="poll" onClick={onPollCreate}>{oldLang('Poll')}</MenuItem>
|
<MenuItem icon="poll" onClick={onPollCreate}>{oldLang('Poll')}</MenuItem>
|
||||||
)}
|
)}
|
||||||
|
{canAttachToDoLists && !editingMessage && (
|
||||||
|
<MenuItem icon="select" onClick={onTodoListCreate}>{lang('TitleToDoList')}</MenuItem>
|
||||||
|
)}
|
||||||
|
|
||||||
{!editingMessage && !canEditMedia && !isScheduled && bots?.map((bot) => (
|
{!editingMessage && !canEditMedia && !isScheduled && bots?.map((bot) => (
|
||||||
<AttachBotItem
|
<AttachBotItem
|
||||||
|
|||||||
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
.options-header {
|
.options-header {
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
@ -69,13 +71,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.note {
|
.note {
|
||||||
font-size: smaller;
|
font-size: 0.875rem;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.poll-error {
|
.poll-error {
|
||||||
margin: -1rem 0 1rem 0.25rem;
|
margin: -1rem 0 1rem 0.25rem;
|
||||||
font-size: smaller;
|
font-size: 0.875rem;
|
||||||
color: var(--color-error);
|
color: var(--color-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
16
src/components/middle/composer/ToDoListModal.async.tsx
Normal file
16
src/components/middle/composer/ToDoListModal.async.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { OwnProps } from './ToDoListModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const ToDoListModalAsync: FC<OwnProps> = (props) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const ToDoListModal = useModuleLoader(Bundles.Extra, 'ToDoListModal', !modal);
|
||||||
|
|
||||||
|
return ToDoListModal ? <ToDoListModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ToDoListModalAsync;
|
||||||
105
src/components/middle/composer/ToDoListModal.scss
Normal file
105
src/components/middle/composer/ToDoListModal.scss
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
@use '../../../styles/mixins';
|
||||||
|
|
||||||
|
.ToDoListModal {
|
||||||
|
.modal-dialog {
|
||||||
|
max-width: 26.25rem;
|
||||||
|
max-height: calc(100vh - 5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
min-height: 4.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header-condensed {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.readonly-title {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-header {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-count-hint {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-left: 1rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-list {
|
||||||
|
overflow: auto;
|
||||||
|
overflow-y: scroll;
|
||||||
|
|
||||||
|
max-height: 20rem;
|
||||||
|
margin: 1rem -0.75rem -0.5rem;
|
||||||
|
padding: 0 0.75rem;
|
||||||
|
border-top: 1px solid var(--color-chat-hover);
|
||||||
|
|
||||||
|
@include mixins.adapt-padding-to-scrollbar(0.75rem);
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-wrapper {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.form-control {
|
||||||
|
padding-right: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-remove-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.125rem;
|
||||||
|
right: 0.3125rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.options-footer {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
|
||||||
|
.dialog-checkbox-group {
|
||||||
|
margin: 0 -1.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options-header {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group:last-child {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.8125rem;
|
||||||
|
margin-left: -1.125rem;
|
||||||
|
|
||||||
|
.Radio, &:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Checkbox,
|
||||||
|
.Radio {
|
||||||
|
.Checkbox-main,
|
||||||
|
.Radio-main {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
383
src/components/middle/composer/ToDoListModal.tsx
Normal file
383
src/components/middle/composer/ToDoListModal.tsx
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
import type { ChangeEvent } from 'react';
|
||||||
|
import type { ElementRef } from '../../../lib/teact/teact';
|
||||||
|
import {
|
||||||
|
memo, useEffect, useRef, useState,
|
||||||
|
} from '../../../lib/teact/teact';
|
||||||
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
|
import type { ApiNewMediaTodo } from '../../../api/types';
|
||||||
|
import type { ApiMessage } from '../../../api/types';
|
||||||
|
import type { TabState } from '../../../global/types/tabState';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TODO_ITEM_LENGTH_LIMIT,
|
||||||
|
TODO_ITEMS_LIMIT,
|
||||||
|
TODO_TITLE_LENGTH_LIMIT,
|
||||||
|
} from '../../../config';
|
||||||
|
import { requestMeasure, requestNextMutation } from '../../../lib/fasterdom/fasterdom';
|
||||||
|
import { selectChatMessage } from '../../../global/selectors';
|
||||||
|
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
||||||
|
import { generateUniqueNumberId } from '../../../util/generateUniqueId';
|
||||||
|
|
||||||
|
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import Icon from '../../common/icons/Icon';
|
||||||
|
import Button from '../../ui/Button';
|
||||||
|
import Checkbox from '../../ui/Checkbox';
|
||||||
|
import InputText from '../../ui/InputText';
|
||||||
|
import Modal from '../../ui/Modal';
|
||||||
|
|
||||||
|
import './ToDoListModal.scss';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
modal: TabState['todoListModal'];
|
||||||
|
onSend: (todoList: ApiNewMediaTodo) => void;
|
||||||
|
onClear: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StateProps = {
|
||||||
|
editingMessage?: ApiMessage;
|
||||||
|
maxItemsCount?: number;
|
||||||
|
maxTitleLength?: number;
|
||||||
|
maxItemLength?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Item = {
|
||||||
|
id: number;
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAX_LIST_HEIGHT = 320;
|
||||||
|
const MAX_OPTION_LENGTH = 100;
|
||||||
|
|
||||||
|
const ToDoListModal = ({
|
||||||
|
modal,
|
||||||
|
maxItemsCount = TODO_ITEMS_LIMIT,
|
||||||
|
maxTitleLength = TODO_TITLE_LENGTH_LIMIT,
|
||||||
|
maxItemLength = TODO_ITEM_LENGTH_LIMIT,
|
||||||
|
editingMessage,
|
||||||
|
onSend,
|
||||||
|
onClear,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
|
const { editTodo, closeTodoListModal, appendTodoList } = getActions();
|
||||||
|
|
||||||
|
const titleInputRef = useRef<HTMLInputElement>();
|
||||||
|
const itemsListRef = useRef<HTMLDivElement>();
|
||||||
|
|
||||||
|
const [title, setTitle] = useState<string>('');
|
||||||
|
const [items, setItems] = useState<Item[]>([{ id: generateUniqueNumberId(), text: '' }]);
|
||||||
|
const [isOthersCanAppend, setIsOthersCanAppend] = useState(true);
|
||||||
|
const [isOthersCanComplete, setIsOthersCanComplete] = useState(true);
|
||||||
|
const [hasErrors, setHasErrors] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const isOpen = Boolean(modal);
|
||||||
|
const renderingModal = useCurrentOrPrev(modal);
|
||||||
|
const isAddTaskMode = renderingModal?.isAddTaskMode;
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
const editingTodo = editingMessage?.content.todo?.todo;
|
||||||
|
|
||||||
|
const focusInput = useLastCallback((ref: ElementRef<HTMLInputElement>) => {
|
||||||
|
if (isOpen && ref.current) {
|
||||||
|
ref.current.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (editingTodo) {
|
||||||
|
setTitle(editingTodo.title.text);
|
||||||
|
setIsOthersCanAppend(editingTodo.othersCanAppend ?? false);
|
||||||
|
setIsOthersCanComplete(editingTodo.othersCanComplete ?? false);
|
||||||
|
if (!isAddTaskMode) {
|
||||||
|
const editingItems = editingTodo.items.map((item) => ({
|
||||||
|
id: item.id,
|
||||||
|
text: item.title.text,
|
||||||
|
}));
|
||||||
|
if (editingItems.length < maxItemsCount) {
|
||||||
|
editingItems.push({ id: generateUniqueNumberId(), text: '' });
|
||||||
|
}
|
||||||
|
setItems(editingItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [editingTodo, isAddTaskMode, maxItemsCount]);
|
||||||
|
|
||||||
|
useEffect(() => (isOpen ? captureEscKeyListener(onClear) : undefined), [isOpen, onClear]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) {
|
||||||
|
setTitle('');
|
||||||
|
setItems([{ id: generateUniqueNumberId(), text: '' }]);
|
||||||
|
setIsOthersCanAppend(true);
|
||||||
|
setIsOthersCanComplete(true);
|
||||||
|
setHasErrors(false);
|
||||||
|
}
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
useEffect(() => focusInput(titleInputRef), [focusInput, isOpen]);
|
||||||
|
|
||||||
|
const addNewItem = useLastCallback((newItems: Item[]) => {
|
||||||
|
const id = generateUniqueNumberId();
|
||||||
|
setItems([...newItems, { id, text: '' }]);
|
||||||
|
|
||||||
|
requestNextMutation(() => {
|
||||||
|
const list = itemsListRef.current;
|
||||||
|
if (!list) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestMeasure(() => {
|
||||||
|
list.scrollTo({ top: list.scrollHeight, behavior: 'smooth' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleCreate = useLastCallback(() => {
|
||||||
|
setHasErrors(false);
|
||||||
|
if (!isOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const todoItems = items
|
||||||
|
.map((item) => {
|
||||||
|
const text = item.text.trim();
|
||||||
|
|
||||||
|
if (!text) return undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
title: {
|
||||||
|
text: text.substring(0, maxItemLength),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}).filter(Boolean);
|
||||||
|
|
||||||
|
const titleTrimmed = title.trim().substring(0, maxTitleLength);
|
||||||
|
if (!titleTrimmed || todoItems.length === 0) {
|
||||||
|
setTitle(titleTrimmed);
|
||||||
|
if (todoItems.length) {
|
||||||
|
const itemsTrimmed = items.map((o) => (
|
||||||
|
{ ...o, text: o.text.trim().substring(0, maxItemLength) }))
|
||||||
|
.filter((o) => o.text.length);
|
||||||
|
if (itemsTrimmed.length === 0) {
|
||||||
|
addNewItem([]);
|
||||||
|
} else {
|
||||||
|
setItems([...itemsTrimmed, { id: generateUniqueNumberId(), text: '' }]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addNewItem([]);
|
||||||
|
}
|
||||||
|
setHasErrors(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAddTaskMode && editingMessage) {
|
||||||
|
appendTodoList({
|
||||||
|
chatId: editingMessage.chatId,
|
||||||
|
messageId: editingMessage.id,
|
||||||
|
items: todoItems,
|
||||||
|
});
|
||||||
|
closeTodoListModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload: ApiNewMediaTodo = {
|
||||||
|
todo: {
|
||||||
|
title: {
|
||||||
|
text: titleTrimmed,
|
||||||
|
},
|
||||||
|
items: todoItems,
|
||||||
|
othersCanAppend: isOthersCanAppend,
|
||||||
|
othersCanComplete: isOthersCanComplete,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (editingMessage) {
|
||||||
|
editTodo({
|
||||||
|
chatId: editingMessage.chatId,
|
||||||
|
todo: payload,
|
||||||
|
messageId: editingMessage.id,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
onSend(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
closeTodoListModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateItem = useLastCallback((index: number, text: string) => {
|
||||||
|
const newItems = [...items];
|
||||||
|
newItems[index] = { ...newItems[index], text };
|
||||||
|
if (newItems[newItems.length - 1].text.trim().length && newItems.length < maxItemsCount) {
|
||||||
|
addNewItem(newItems);
|
||||||
|
} else {
|
||||||
|
setItems(newItems);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const removeItem = useLastCallback((index: number) => {
|
||||||
|
const newItems = [...items];
|
||||||
|
newItems.splice(index, 1);
|
||||||
|
setItems(newItems);
|
||||||
|
|
||||||
|
requestNextMutation(() => {
|
||||||
|
if (!itemsListRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemsListRef.current.classList.toggle('overflown', itemsListRef.current.scrollHeight > MAX_LIST_HEIGHT);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleIsOthersCanAppendChange = useLastCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setIsOthersCanAppend(e.target.checked);
|
||||||
|
});
|
||||||
|
const handleIsOthersCanCompleteChange = useLastCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setIsOthersCanComplete(e.target.checked);
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleKeyPress = useLastCallback((e: React.KeyboardEvent) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
handleCreate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleTitleChange = useLastCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setTitle(e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const getTitleError = useLastCallback(() => {
|
||||||
|
if (hasErrors && !title.trim().length) {
|
||||||
|
return lang('ToDoListErrorChooseTitle');
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getItemsError = useLastCallback((index: number) => {
|
||||||
|
const itemsTrimmed = items.map((o) => o.text.trim()).filter((o) => o.length);
|
||||||
|
if (hasErrors && itemsTrimmed.length < 1 && !items[index].text.trim().length) {
|
||||||
|
return lang('ToDoListErrorChooseTasks');
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderHeader() {
|
||||||
|
const title = isAddTaskMode ? 'TitleAppendToDoList' : editingMessage ? 'TitleEditToDoList' : 'TitleNewToDoList';
|
||||||
|
return (
|
||||||
|
<div className="modal-header-condensed">
|
||||||
|
<Button round color="translucent" size="smaller" ariaLabel={lang('AriaToDoCancel')} onClick={onClear}>
|
||||||
|
<Icon name="close" />
|
||||||
|
</Button>
|
||||||
|
<div className="modal-title">{lang(title)}</div>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
size="smaller"
|
||||||
|
className="modal-action-button"
|
||||||
|
onClick={handleCreate}
|
||||||
|
>
|
||||||
|
{lang(isAddTaskMode ? 'Add' : editingMessage ? 'Save' : 'Create')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderItems() {
|
||||||
|
return items.map((item, index) => (
|
||||||
|
<div className="item-wrapper">
|
||||||
|
<InputText
|
||||||
|
maxLength={MAX_OPTION_LENGTH}
|
||||||
|
label={index !== items.length - 1 || items.length === maxItemsCount
|
||||||
|
? lang('TitleTask')
|
||||||
|
: lang('TitleAddTask')}
|
||||||
|
error={getItemsError(index)}
|
||||||
|
value={item.text}
|
||||||
|
|
||||||
|
onChange={(e) => updateItem(index, e.currentTarget.value)}
|
||||||
|
onKeyPress={handleKeyPress}
|
||||||
|
/>
|
||||||
|
{index !== items.length - 1 && (
|
||||||
|
<Button
|
||||||
|
className="item-remove-button"
|
||||||
|
round
|
||||||
|
color="translucent"
|
||||||
|
size="smaller"
|
||||||
|
ariaLabel={lang('Delete')}
|
||||||
|
|
||||||
|
onClick={() => removeItem(index)}
|
||||||
|
>
|
||||||
|
<Icon name="close" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} onClose={onClear} header={renderHeader()} className="ToDoListModal">
|
||||||
|
{!isAddTaskMode && (
|
||||||
|
<InputText
|
||||||
|
ref={titleInputRef}
|
||||||
|
label={lang('InputTitle')}
|
||||||
|
value={title}
|
||||||
|
error={getTitleError()}
|
||||||
|
onChange={handleTitleChange}
|
||||||
|
onKeyPress={handleKeyPress}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isAddTaskMode && (
|
||||||
|
<div className="readonly-title">
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="options-divider" />
|
||||||
|
|
||||||
|
<div className="options-list custom-scroll" ref={itemsListRef}>
|
||||||
|
<h3 className="items-header">
|
||||||
|
{lang(isAddTaskMode ? 'ToDoListNewTasks' : 'TitleToDoList')}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
{renderItems()}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="items-count-hint">
|
||||||
|
{lang('HintTodoListTasksCount', {
|
||||||
|
count: maxItemsCount - items.length - (isAddTaskMode && editingTodo ? editingTodo.items.length : 0),
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="options-divider" />
|
||||||
|
|
||||||
|
{!isAddTaskMode && (
|
||||||
|
<div className="options-footer">
|
||||||
|
<div className="dialog-checkbox-group">
|
||||||
|
<Checkbox
|
||||||
|
label={lang('AllowOthersAddTasks')}
|
||||||
|
checked={isOthersCanAppend}
|
||||||
|
onChange={handleIsOthersCanAppendChange}
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label={lang('AllowOthersMarkAsDone')}
|
||||||
|
checked={isOthersCanComplete}
|
||||||
|
onChange={handleIsOthersCanCompleteChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>(
|
||||||
|
(global, { modal }): StateProps => {
|
||||||
|
const { appConfig } = global;
|
||||||
|
const editingMessage = modal?.messageId ? selectChatMessage(global, modal.chatId, modal.messageId) : undefined;
|
||||||
|
return {
|
||||||
|
editingMessage,
|
||||||
|
maxItemsCount: appConfig?.todoItemsMax,
|
||||||
|
maxTitleLength: appConfig?.todoTitleLengthMax,
|
||||||
|
maxItemLength: appConfig?.todoItemLengthMax,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(ToDoListModal));
|
||||||
@ -64,7 +64,12 @@
|
|||||||
|
|
||||||
.messageLink {
|
.messageLink {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
min-width: 0;
|
min-width: 1ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noEllipsis {
|
||||||
|
overflow: visible;
|
||||||
|
min-width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.singleLine, .messageLink {
|
.singleLine, .messageLink {
|
||||||
|
|||||||
@ -94,6 +94,8 @@ const SINGLE_LINE_ACTIONS = new Set<ApiMessageAction['type']>([
|
|||||||
'pinMessage',
|
'pinMessage',
|
||||||
'chatEditPhoto',
|
'chatEditPhoto',
|
||||||
'chatDeletePhoto',
|
'chatDeletePhoto',
|
||||||
|
'todoCompletions',
|
||||||
|
'todoAppendTasks',
|
||||||
'unsupported',
|
'unsupported',
|
||||||
]);
|
]);
|
||||||
const HIDDEN_TEXT_ACTIONS = new Set<ApiMessageAction['type']>(['giftCode', 'prizeStars', 'suggestProfilePhoto']);
|
const HIDDEN_TEXT_ACTIONS = new Set<ApiMessageAction['type']>(['giftCode', 'prizeStars', 'suggestProfilePhoto']);
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
getMainUsername,
|
getMainUsername,
|
||||||
getMessageInvoice, getMessageText, isChatChannel,
|
getMessageInvoice, getMessageText, isChatChannel,
|
||||||
} from '../../../global/helpers';
|
} from '../../../global/helpers';
|
||||||
|
import { getMessageContent } from '../../../global/helpers';
|
||||||
import { getPeerTitle } from '../../../global/helpers/peers';
|
import { getPeerTitle } from '../../../global/helpers/peers';
|
||||||
import { getMessageReplyInfo } from '../../../global/helpers/replies';
|
import { getMessageReplyInfo } from '../../../global/helpers/replies';
|
||||||
import {
|
import {
|
||||||
@ -534,14 +535,14 @@ const ActionMessageText = ({
|
|||||||
if (isRecurringInit) {
|
if (isRecurringInit) {
|
||||||
return lang(
|
return lang(
|
||||||
'ActionPaymentInitRecurringFor',
|
'ActionPaymentInitRecurringFor',
|
||||||
{ amount: cost, user: chatLink, invoice: renderMessageLink(replyMessage!, invoiceTitle, asPreview) },
|
{ amount: cost, user: chatLink, invoice: renderMessageLink(replyMessage, invoiceTitle, asPreview) },
|
||||||
{ withNodes: true },
|
{ withNodes: true },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return lang(
|
return lang(
|
||||||
'ActionPaymentDoneFor',
|
'ActionPaymentDoneFor',
|
||||||
{ amount: cost, user: chatLink, invoice: renderMessageLink(replyMessage!, invoiceTitle, asPreview) },
|
{ amount: cost, user: chatLink, invoice: renderMessageLink(replyMessage, invoiceTitle, asPreview) },
|
||||||
{ withNodes: true },
|
{ withNodes: true },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -749,6 +750,146 @@ const ActionMessageText = ({
|
|||||||
}, { withNodes: true, withMarkdown: true });
|
}, { withNodes: true, withMarkdown: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'todoCompletions': {
|
||||||
|
const { completedIds, incompletedIds } = action;
|
||||||
|
|
||||||
|
let completedItem;
|
||||||
|
let incompletedItem;
|
||||||
|
const { todo } = replyMessage ? getMessageContent(replyMessage) : {};
|
||||||
|
if (todo) {
|
||||||
|
const todoItems = todo.todo.items;
|
||||||
|
completedItem = todoItems.find((item) => completedIds.includes(item.id));
|
||||||
|
incompletedItem = todoItems.find((item) => incompletedIds.includes(item.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (incompletedItem) {
|
||||||
|
const incompletedTaskTitle = incompletedItem.title;
|
||||||
|
|
||||||
|
const incompletedTaskLink = renderMessageLink(
|
||||||
|
replyMessage,
|
||||||
|
renderTextWithEntities({
|
||||||
|
text: incompletedTaskTitle.text,
|
||||||
|
entities: incompletedTaskTitle.entities,
|
||||||
|
asPreview: true,
|
||||||
|
}),
|
||||||
|
asPreview,
|
||||||
|
);
|
||||||
|
|
||||||
|
return translateWithYou(lang, 'MessageActionTodoCompletionsAsNotDone', isOutgoing, {
|
||||||
|
peer: senderLink,
|
||||||
|
task: incompletedTaskLink,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (completedItem) {
|
||||||
|
const completedTaskTitle = completedItem.title;
|
||||||
|
const completedTaskLink = renderMessageLink(
|
||||||
|
replyMessage,
|
||||||
|
renderTextWithEntities({
|
||||||
|
text: completedTaskTitle.text,
|
||||||
|
entities: completedTaskTitle.entities,
|
||||||
|
asPreview: true,
|
||||||
|
}),
|
||||||
|
asPreview,
|
||||||
|
);
|
||||||
|
|
||||||
|
return translateWithYou(lang, 'MessageActionTodoCompletionsAsDone', isOutgoing, {
|
||||||
|
peer: senderLink,
|
||||||
|
task: completedTaskLink,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (completedIds) {
|
||||||
|
const completedText = lang('MessageActionTodoTaskCount', {
|
||||||
|
count: completedIds.length,
|
||||||
|
}, { pluralValue: completedIds.length });
|
||||||
|
const completedLink = renderMessageLink(
|
||||||
|
replyMessage,
|
||||||
|
renderTextWithEntities({
|
||||||
|
text: completedText,
|
||||||
|
asPreview: true,
|
||||||
|
}),
|
||||||
|
asPreview,
|
||||||
|
{ noEllipsis: true },
|
||||||
|
);
|
||||||
|
return translateWithYou(lang, 'MessageActionTodoCompletionsAsDone', isOutgoing, {
|
||||||
|
peer: senderLink,
|
||||||
|
task: completedLink,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const incompletedText = lang('MessageActionTodoTaskCount', {
|
||||||
|
count: incompletedIds.length,
|
||||||
|
}, { pluralValue: incompletedIds.length });
|
||||||
|
const incompletedLink = renderMessageLink(
|
||||||
|
replyMessage,
|
||||||
|
renderTextWithEntities({
|
||||||
|
text: incompletedText,
|
||||||
|
asPreview: true,
|
||||||
|
}),
|
||||||
|
asPreview,
|
||||||
|
{ noEllipsis: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
return translateWithYou(lang, 'MessageActionTodoCompletionsAsNotDone', isOutgoing, {
|
||||||
|
peer: senderLink,
|
||||||
|
task: incompletedLink,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'todoAppendTasks': {
|
||||||
|
const { items } = action;
|
||||||
|
const { todo } = replyMessage ? getMessageContent(replyMessage) : {};
|
||||||
|
|
||||||
|
const listTitle = todo?.todo.title.text || '';
|
||||||
|
const listLink = renderMessageLink(
|
||||||
|
replyMessage,
|
||||||
|
renderTextWithEntities({
|
||||||
|
text: listTitle,
|
||||||
|
asPreview: true,
|
||||||
|
}),
|
||||||
|
asPreview,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (items.length === 1) {
|
||||||
|
const taskTitle = items[0].title;
|
||||||
|
const taskLink = renderMessageLink(
|
||||||
|
replyMessage,
|
||||||
|
renderTextWithEntities({
|
||||||
|
text: taskTitle.text,
|
||||||
|
entities: taskTitle.entities,
|
||||||
|
asPreview: true,
|
||||||
|
}),
|
||||||
|
asPreview,
|
||||||
|
);
|
||||||
|
|
||||||
|
return translateWithYou(lang, 'MessageActionAppendTodo', isOutgoing, {
|
||||||
|
peer: senderLink,
|
||||||
|
task: taskLink,
|
||||||
|
list: listLink,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const tasksText = lang('MessageActionTodoTaskCount', {
|
||||||
|
count: items.length,
|
||||||
|
}, { pluralValue: items.length });
|
||||||
|
const tasksLink = renderMessageLink(
|
||||||
|
replyMessage,
|
||||||
|
renderTextWithEntities({
|
||||||
|
text: tasksText,
|
||||||
|
asPreview: true,
|
||||||
|
}),
|
||||||
|
asPreview,
|
||||||
|
{ noEllipsis: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
return translateWithYou(lang, 'MessageActionAppendTodoMultiple', isOutgoing, {
|
||||||
|
peer: senderLink,
|
||||||
|
tasks: tasksLink,
|
||||||
|
list: listLink,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
case 'phoneCall': // Rendered as a regular message, but considered an action for the summary
|
case 'phoneCall': // Rendered as a regular message, but considered an action for the summary
|
||||||
return lang(getCallMessageKey(action, isOutgoing));
|
return lang(getCallMessageKey(action, isOutgoing));
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -25,6 +25,9 @@ import type {
|
|||||||
} from '../../../types';
|
} from '../../../types';
|
||||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TODO_ITEMS_LIMIT,
|
||||||
|
} from '../../../config';
|
||||||
import { PREVIEW_AVATAR_COUNT, SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
|
import { PREVIEW_AVATAR_COUNT, SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
|
||||||
import {
|
import {
|
||||||
areReactionsEmpty,
|
areReactionsEmpty,
|
||||||
@ -79,6 +82,7 @@ import { getSelectionAsFormattedText } from './helpers/getSelectionAsFormattedTe
|
|||||||
import { isSelectionRangeInsideMessage } from './helpers/isSelectionRangeInsideMessage';
|
import { isSelectionRangeInsideMessage } from './helpers/isSelectionRangeInsideMessage';
|
||||||
|
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
import useSchedule from '../../../hooks/useSchedule';
|
import useSchedule from '../../../hooks/useSchedule';
|
||||||
@ -125,6 +129,7 @@ type StateProps = {
|
|||||||
canDelete?: boolean;
|
canDelete?: boolean;
|
||||||
canReport?: boolean;
|
canReport?: boolean;
|
||||||
canEdit?: boolean;
|
canEdit?: boolean;
|
||||||
|
canAppendTodoList?: boolean;
|
||||||
canForward?: boolean;
|
canForward?: boolean;
|
||||||
canFaveSticker?: boolean;
|
canFaveSticker?: boolean;
|
||||||
canUnfaveSticker?: boolean;
|
canUnfaveSticker?: boolean;
|
||||||
@ -192,6 +197,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
canReport,
|
canReport,
|
||||||
canShowReactionList,
|
canShowReactionList,
|
||||||
canEdit,
|
canEdit,
|
||||||
|
canAppendTodoList,
|
||||||
enabledReactions,
|
enabledReactions,
|
||||||
reactionsLimit,
|
reactionsLimit,
|
||||||
isPrivate,
|
isPrivate,
|
||||||
@ -265,9 +271,12 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
addLocalPaidReaction,
|
addLocalPaidReaction,
|
||||||
openPaidReactionModal,
|
openPaidReactionModal,
|
||||||
reportMessages,
|
reportMessages,
|
||||||
|
openTodoListModal,
|
||||||
|
showNotification,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const lang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
|
const lang = useLang();
|
||||||
const { ref: containerRef } = useShowTransition({
|
const { ref: containerRef } = useShowTransition({
|
||||||
isOpen,
|
isOpen,
|
||||||
onCloseAnimationEnd,
|
onCloseAnimationEnd,
|
||||||
@ -435,7 +444,34 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleEdit = useLastCallback(() => {
|
const handleEdit = useLastCallback(() => {
|
||||||
setEditingId({ messageId: message.id });
|
if (message.content.todo) {
|
||||||
|
openTodoListModal({
|
||||||
|
chatId: message.chatId,
|
||||||
|
messageId: message.id,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setEditingId({ messageId: message.id });
|
||||||
|
}
|
||||||
|
closeMenu();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleAppendTodoList = useLastCallback(() => {
|
||||||
|
if (!isCurrentUserPremium) {
|
||||||
|
showNotification({
|
||||||
|
message: lang('SubscribeToTelegramPremiumForAppendToDo'),
|
||||||
|
action: {
|
||||||
|
action: 'openPremiumModal',
|
||||||
|
payload: { initialSection: 'todo' },
|
||||||
|
},
|
||||||
|
actionText: oldLang('PremiumMore'),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
openTodoListModal({
|
||||||
|
chatId: message.chatId,
|
||||||
|
messageId: message.id,
|
||||||
|
isAddTaskMode: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
closeMenu();
|
closeMenu();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -667,6 +703,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
repliesThreadInfo={repliesThreadInfo}
|
repliesThreadInfo={repliesThreadInfo}
|
||||||
canUnpin={canUnpin}
|
canUnpin={canUnpin}
|
||||||
canEdit={canEdit}
|
canEdit={canEdit}
|
||||||
|
canAppendTodoList={canAppendTodoList}
|
||||||
canForward={canForward}
|
canForward={canForward}
|
||||||
canFaveSticker={canFaveSticker}
|
canFaveSticker={canFaveSticker}
|
||||||
canUnfaveSticker={canUnfaveSticker}
|
canUnfaveSticker={canUnfaveSticker}
|
||||||
@ -695,6 +732,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
onOpenThread={handleOpenThread}
|
onOpenThread={handleOpenThread}
|
||||||
onReply={handleReply}
|
onReply={handleReply}
|
||||||
onEdit={handleEdit}
|
onEdit={handleEdit}
|
||||||
|
onAppendTodoList={handleAppendTodoList}
|
||||||
onPin={handlePin}
|
onPin={handlePin}
|
||||||
onUnpin={handleUnpin}
|
onUnpin={handleUnpin}
|
||||||
onForward={handleForward}
|
onForward={handleForward}
|
||||||
@ -734,8 +772,8 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={isClosePollDialogOpen}
|
isOpen={isClosePollDialogOpen}
|
||||||
onClose={closeClosePollDialog}
|
onClose={closeClosePollDialog}
|
||||||
text={lang('lng_polls_stop_warning')}
|
text={oldLang('lng_polls_stop_warning')}
|
||||||
confirmLabel={lang('lng_polls_stop_sure')}
|
confirmLabel={oldLang('lng_polls_stop_sure')}
|
||||||
confirmHandler={handlePollClose}
|
confirmHandler={handlePollClose}
|
||||||
/>
|
/>
|
||||||
{canReschedule && calendar}
|
{canReschedule && calendar}
|
||||||
@ -855,6 +893,9 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const canGift = selectCanGift(global, message.chatId);
|
const canGift = selectCanGift(global, message.chatId);
|
||||||
|
|
||||||
const savedDialogId = selectSavedDialogIdFromMessage(global, message);
|
const savedDialogId = selectSavedDialogIdFromMessage(global, message);
|
||||||
|
const todoItemsMax = global.appConfig?.todoItemsMax || TODO_ITEMS_LIMIT;
|
||||||
|
const canAppendTodoList = message.content.todo?.todo.othersCanAppend
|
||||||
|
&& message.content.todo?.todo.items?.length < todoItemsMax;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
threadId,
|
threadId,
|
||||||
@ -871,6 +912,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
canUnpin: !isScheduled && canUnpin,
|
canUnpin: !isScheduled && canUnpin,
|
||||||
canDelete,
|
canDelete,
|
||||||
canEdit: !isPinned && canEdit,
|
canEdit: !isPinned && canEdit,
|
||||||
|
canAppendTodoList,
|
||||||
canForward: !isScheduled && canForward,
|
canForward: !isScheduled && canForward,
|
||||||
canFaveSticker: !isScheduled && canFaveSticker,
|
canFaveSticker: !isScheduled && canFaveSticker,
|
||||||
canUnfaveSticker: !isScheduled && canUnfaveSticker,
|
canUnfaveSticker: !isScheduled && canUnfaveSticker,
|
||||||
|
|||||||
@ -189,6 +189,7 @@ import RoundVideo from './RoundVideo';
|
|||||||
import Sticker from './Sticker';
|
import Sticker from './Sticker';
|
||||||
import Story from './Story';
|
import Story from './Story';
|
||||||
import StoryMention from './StoryMention';
|
import StoryMention from './StoryMention';
|
||||||
|
import TodoList from './TodoList';
|
||||||
import Video from './Video';
|
import Video from './Video';
|
||||||
import WebPage from './WebPage';
|
import WebPage from './WebPage';
|
||||||
|
|
||||||
@ -520,7 +521,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
voice, document, sticker, contact,
|
voice, document, sticker, contact,
|
||||||
webPage, invoice, location,
|
webPage, invoice, location,
|
||||||
action, game, storyData, giveaway,
|
action, game, storyData, giveaway,
|
||||||
giveawayResults,
|
giveawayResults, todo,
|
||||||
} = getMessageContent(message);
|
} = getMessageContent(message);
|
||||||
|
|
||||||
const messageReplyInfo = getMessageReplyInfo(message);
|
const messageReplyInfo = getMessageReplyInfo(message);
|
||||||
@ -1241,6 +1242,9 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
{poll && (
|
{poll && (
|
||||||
<Poll message={message} poll={poll} onSendVote={handleVoteSend} />
|
<Poll message={message} poll={poll} onSendVote={handleVoteSend} />
|
||||||
)}
|
)}
|
||||||
|
{todo && (
|
||||||
|
<TodoList message={message} todoList={todo} />
|
||||||
|
)}
|
||||||
{(giveaway || giveawayResults) && (
|
{(giveaway || giveawayResults) && (
|
||||||
<Giveaway message={message} />
|
<Giveaway message={message} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import { getMessageCopyOptions } from './helpers/copyOptions';
|
|||||||
|
|
||||||
import useAppLayout from '../../../hooks/useAppLayout';
|
import useAppLayout from '../../../hooks/useAppLayout';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
@ -73,6 +74,7 @@ type OwnProps = {
|
|||||||
canShowReactionList?: boolean;
|
canShowReactionList?: boolean;
|
||||||
canBuyPremium?: boolean;
|
canBuyPremium?: boolean;
|
||||||
canEdit?: boolean;
|
canEdit?: boolean;
|
||||||
|
canAppendTodoList?: boolean;
|
||||||
canForward?: boolean;
|
canForward?: boolean;
|
||||||
canFaveSticker?: boolean;
|
canFaveSticker?: boolean;
|
||||||
canUnfaveSticker?: boolean;
|
canUnfaveSticker?: boolean;
|
||||||
@ -101,6 +103,7 @@ type OwnProps = {
|
|||||||
onReply?: NoneToVoidFunction;
|
onReply?: NoneToVoidFunction;
|
||||||
onOpenThread?: VoidFunction;
|
onOpenThread?: VoidFunction;
|
||||||
onEdit?: NoneToVoidFunction;
|
onEdit?: NoneToVoidFunction;
|
||||||
|
onAppendTodoList?: NoneToVoidFunction;
|
||||||
onPin?: NoneToVoidFunction;
|
onPin?: NoneToVoidFunction;
|
||||||
onUnpin?: NoneToVoidFunction;
|
onUnpin?: NoneToVoidFunction;
|
||||||
onForward?: NoneToVoidFunction;
|
onForward?: NoneToVoidFunction;
|
||||||
@ -159,6 +162,7 @@ const MessageContextMenu: FC<OwnProps> = ({
|
|||||||
canReply,
|
canReply,
|
||||||
canQuote,
|
canQuote,
|
||||||
canEdit,
|
canEdit,
|
||||||
|
canAppendTodoList,
|
||||||
noReplies,
|
noReplies,
|
||||||
canPin,
|
canPin,
|
||||||
canUnpin,
|
canUnpin,
|
||||||
@ -192,6 +196,7 @@ const MessageContextMenu: FC<OwnProps> = ({
|
|||||||
onReply,
|
onReply,
|
||||||
onOpenThread,
|
onOpenThread,
|
||||||
onEdit,
|
onEdit,
|
||||||
|
onAppendTodoList,
|
||||||
onPin,
|
onPin,
|
||||||
onUnpin,
|
onUnpin,
|
||||||
onForward,
|
onForward,
|
||||||
@ -228,7 +233,8 @@ const MessageContextMenu: FC<OwnProps> = ({
|
|||||||
} = getActions();
|
} = getActions();
|
||||||
const menuRef = useRef<HTMLDivElement>();
|
const menuRef = useRef<HTMLDivElement>();
|
||||||
const scrollableRef = useRef<HTMLDivElement>();
|
const scrollableRef = useRef<HTMLDivElement>();
|
||||||
const lang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
|
const lang = useLang();
|
||||||
const noReactions = !isPrivate && !enabledReactions;
|
const noReactions = !isPrivate && !enabledReactions;
|
||||||
const areReactionsPossible = message.areReactionsPossible;
|
const areReactionsPossible = message.areReactionsPossible;
|
||||||
const withReactions = (canShowReactionList && !noReactions) || areReactionsPossible;
|
const withReactions = (canShowReactionList && !noReactions) || areReactionsPossible;
|
||||||
@ -248,7 +254,7 @@ const MessageContextMenu: FC<OwnProps> = ({
|
|||||||
|
|
||||||
const handleAfterCopy = useLastCallback(() => {
|
const handleAfterCopy = useLastCallback(() => {
|
||||||
showNotification({
|
showNotification({
|
||||||
message: lang('Share.Link.Copied'),
|
message: oldLang('Share.Link.Copied'),
|
||||||
});
|
});
|
||||||
onClose();
|
onClose();
|
||||||
});
|
});
|
||||||
@ -395,40 +401,49 @@ const MessageContextMenu: FC<OwnProps> = ({
|
|||||||
'MessageContextMenu_items scrollable-content custom-scroll',
|
'MessageContextMenu_items scrollable-content custom-scroll',
|
||||||
areItemsHidden && 'MessageContextMenu_items-hidden',
|
areItemsHidden && 'MessageContextMenu_items-hidden',
|
||||||
)}
|
)}
|
||||||
dir={lang.isRtl ? 'rtl' : undefined}
|
dir={oldLang.isRtl ? 'rtl' : undefined}
|
||||||
>
|
>
|
||||||
{shouldShowGiftButton
|
{shouldShowGiftButton
|
||||||
&& (
|
&& (
|
||||||
<MenuItem icon="gift" onClick={handleGiftClick}>
|
<MenuItem icon="gift" onClick={handleGiftClick}>
|
||||||
{message?.isOutgoing ? lang('SendAnotherGift')
|
{message?.isOutgoing ? oldLang('SendAnotherGift')
|
||||||
: lang('Conversation.ContextMenuSendGiftTo', userFullName)}
|
: oldLang('Conversation.ContextMenuSendGiftTo', userFullName)}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
{canSendNow && <MenuItem icon="send-outline" onClick={onSend}>{lang('MessageScheduleSend')}</MenuItem>}
|
{canSendNow && <MenuItem icon="send-outline" onClick={onSend}>{oldLang('MessageScheduleSend')}</MenuItem>}
|
||||||
{canReschedule && (
|
{canReschedule && (
|
||||||
<MenuItem icon="schedule" onClick={onReschedule}>{lang('MessageScheduleEditTime')}</MenuItem>
|
<MenuItem icon="schedule" onClick={onReschedule}>{oldLang('MessageScheduleEditTime')}</MenuItem>
|
||||||
)}
|
)}
|
||||||
{canReply && (
|
{canReply && (
|
||||||
<MenuItem icon="reply" onClick={onReply}>
|
<MenuItem icon="reply" onClick={onReply}>
|
||||||
{lang(canQuote ? 'lng_context_quote_and_reply' : 'Reply')}
|
{oldLang(canQuote ? 'lng_context_quote_and_reply' : 'Reply')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
{!noReplies && Boolean(repliesThreadInfo?.messagesCount) && (
|
{!noReplies && Boolean(repliesThreadInfo?.messagesCount) && (
|
||||||
<MenuItem icon="replies" onClick={onOpenThread}>
|
<MenuItem icon="replies" onClick={onOpenThread}>
|
||||||
{lang('Conversation.ContextViewReplies', repliesThreadInfo.messagesCount, 'i')}
|
{oldLang('Conversation.ContextViewReplies', repliesThreadInfo.messagesCount, 'i')}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
{canEdit && <MenuItem icon="edit" onClick={onEdit}>{oldLang('Edit')}</MenuItem>}
|
||||||
|
{canAppendTodoList && (
|
||||||
|
<MenuItem icon="add" onClick={onAppendTodoList}>
|
||||||
|
{lang('MenuButtonAppendTodoList')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
{canEdit && <MenuItem icon="edit" onClick={onEdit}>{lang('Edit')}</MenuItem>}
|
|
||||||
{canFaveSticker && (
|
{canFaveSticker && (
|
||||||
<MenuItem icon="favorite" onClick={onFaveSticker}>{lang('AddToFavorites')}</MenuItem>
|
<MenuItem icon="favorite" onClick={onFaveSticker}>{oldLang('AddToFavorites')}</MenuItem>
|
||||||
)}
|
)}
|
||||||
{canUnfaveSticker && (
|
{canUnfaveSticker && (
|
||||||
<MenuItem icon="favorite" onClick={onUnfaveSticker}>{lang('Stickers.RemoveFromFavorites')}</MenuItem>
|
<MenuItem icon="favorite" onClick={onUnfaveSticker}>{oldLang('Stickers.RemoveFromFavorites')}</MenuItem>
|
||||||
|
)}
|
||||||
|
{canTranslate && <MenuItem icon="language" onClick={onTranslate}>{oldLang('TranslateMessage')}</MenuItem>}
|
||||||
|
{canShowOriginal && (
|
||||||
|
<MenuItem icon="language" onClick={onShowOriginal}>
|
||||||
|
{oldLang('ShowOriginalButton')}
|
||||||
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
{canTranslate && <MenuItem icon="language" onClick={onTranslate}>{lang('TranslateMessage')}</MenuItem>}
|
|
||||||
{canShowOriginal && <MenuItem icon="language" onClick={onShowOriginal}>{lang('ShowOriginalButton')}</MenuItem>}
|
|
||||||
{canSelectLanguage && (
|
{canSelectLanguage && (
|
||||||
<MenuItem icon="web" onClick={onSelectLanguage}>{lang('lng_settings_change_lang')}</MenuItem>
|
<MenuItem icon="web" onClick={onSelectLanguage}>{oldLang('lng_settings_change_lang')}</MenuItem>
|
||||||
)}
|
)}
|
||||||
{copyOptions.map((option) => (
|
{copyOptions.map((option) => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@ -437,23 +452,23 @@ const MessageContextMenu: FC<OwnProps> = ({
|
|||||||
onClick={option.handler}
|
onClick={option.handler}
|
||||||
withPreventDefaultOnMouseDown
|
withPreventDefaultOnMouseDown
|
||||||
>
|
>
|
||||||
{lang(option.label)}
|
{oldLang(option.label)}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
{canPin && <MenuItem icon="pin" onClick={onPin}>{lang('DialogPin')}</MenuItem>}
|
{canPin && <MenuItem icon="pin" onClick={onPin}>{oldLang('DialogPin')}</MenuItem>}
|
||||||
{canUnpin && <MenuItem icon="unpin" onClick={onUnpin}>{lang('DialogUnpin')}</MenuItem>}
|
{canUnpin && <MenuItem icon="unpin" onClick={onUnpin}>{oldLang('DialogUnpin')}</MenuItem>}
|
||||||
{canSaveGif && <MenuItem icon="gifs" onClick={onSaveGif}>{lang('lng_context_save_gif')}</MenuItem>}
|
{canSaveGif && <MenuItem icon="gifs" onClick={onSaveGif}>{oldLang('lng_context_save_gif')}</MenuItem>}
|
||||||
{canRevote && <MenuItem icon="revote" onClick={onCancelVote}>{lang('lng_polls_retract')}</MenuItem>}
|
{canRevote && <MenuItem icon="revote" onClick={onCancelVote}>{oldLang('lng_polls_retract')}</MenuItem>}
|
||||||
{canClosePoll && <MenuItem icon="stop" onClick={onClosePoll}>{lang('lng_polls_stop')}</MenuItem>}
|
{canClosePoll && <MenuItem icon="stop" onClick={onClosePoll}>{oldLang('lng_polls_stop')}</MenuItem>}
|
||||||
{canDownload && (
|
{canDownload && (
|
||||||
<MenuItem icon="download" onClick={onDownload}>
|
<MenuItem icon="download" onClick={onDownload}>
|
||||||
{isDownloading ? lang('lng_context_cancel_download') : lang('lng_media_download')}
|
{isDownloading ? oldLang('lng_context_cancel_download') : oldLang('lng_media_download')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
{canForward && <MenuItem icon="forward" onClick={onForward}>{lang('Forward')}</MenuItem>}
|
{canForward && <MenuItem icon="forward" onClick={onForward}>{oldLang('Forward')}</MenuItem>}
|
||||||
{canSelect && <MenuItem icon="select" onClick={onSelect}>{lang('Common.Select')}</MenuItem>}
|
{canSelect && <MenuItem icon="select" onClick={onSelect}>{oldLang('Common.Select')}</MenuItem>}
|
||||||
{canReport && <MenuItem icon="flag" onClick={onReport}>{lang('lng_context_report_msg')}</MenuItem>}
|
{canReport && <MenuItem icon="flag" onClick={onReport}>{oldLang('lng_context_report_msg')}</MenuItem>}
|
||||||
{canDelete && <MenuItem destructive icon="delete" onClick={onDelete}>{lang('Delete')}</MenuItem>}
|
{canDelete && <MenuItem destructive icon="delete" onClick={onDelete}>{oldLang('Delete')}</MenuItem>}
|
||||||
{hasCustomEmoji && (
|
{hasCustomEmoji && (
|
||||||
<>
|
<>
|
||||||
<MenuSeparator size="thick" />
|
<MenuSeparator size="thick" />
|
||||||
@ -465,12 +480,14 @@ const MessageContextMenu: FC<OwnProps> = ({
|
|||||||
)}
|
)}
|
||||||
{customEmojiSets && customEmojiSets.length === 1 && (
|
{customEmojiSets && customEmojiSets.length === 1 && (
|
||||||
<MenuItem withWrap onClick={handleOpenCustomEmojiSets} className="menu-custom-emoji-sets">
|
<MenuItem withWrap onClick={handleOpenCustomEmojiSets} className="menu-custom-emoji-sets">
|
||||||
{renderText(lang('MessageContainsEmojiPack', customEmojiSets[0].title), ['simple_markdown', 'emoji'])}
|
{renderText(
|
||||||
|
oldLang('MessageContainsEmojiPack', customEmojiSets[0].title), ['simple_markdown', 'emoji'],
|
||||||
|
)}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
{customEmojiSets && customEmojiSets.length > 1 && (
|
{customEmojiSets && customEmojiSets.length > 1 && (
|
||||||
<MenuItem withWrap onClick={handleOpenCustomEmojiSets} className="menu-custom-emoji-sets">
|
<MenuItem withWrap onClick={handleOpenCustomEmojiSets} className="menu-custom-emoji-sets">
|
||||||
{renderText(lang('MessageContainsEmojiPacks', customEmojiSets.length), ['simple_markdown'])}
|
{renderText(oldLang('MessageContainsEmojiPacks', customEmojiSets.length), ['simple_markdown'])}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
@ -484,14 +501,14 @@ const MessageContextMenu: FC<OwnProps> = ({
|
|||||||
disabled={!canShowReactionsCount && !seenByDatesCount}
|
disabled={!canShowReactionsCount && !seenByDatesCount}
|
||||||
>
|
>
|
||||||
<span className="MessageContextMenu--seen-by-label-wrapper">
|
<span className="MessageContextMenu--seen-by-label-wrapper">
|
||||||
<span className="MessageContextMenu--seen-by-label" dir={lang.isRtl ? 'rtl' : undefined}>
|
<span className="MessageContextMenu--seen-by-label" dir={oldLang.isRtl ? 'rtl' : undefined}>
|
||||||
{canShowReactionsCount && message.reactors?.count ? (
|
{canShowReactionsCount && message.reactors?.count ? (
|
||||||
canShowSeenBy && seenByDatesCount
|
canShowSeenBy && seenByDatesCount
|
||||||
? lang(
|
? oldLang(
|
||||||
'Chat.OutgoingContextMixedReactionCount',
|
'Chat.OutgoingContextMixedReactionCount',
|
||||||
[message.reactors.count, seenByDatesCount],
|
[message.reactors.count, seenByDatesCount],
|
||||||
)
|
)
|
||||||
: lang('Chat.ContextReactionCount', message.reactors.count, 'i')
|
: oldLang('Chat.ContextReactionCount', message.reactors.count, 'i')
|
||||||
) : (
|
) : (
|
||||||
seenByDatesCount === 1 && seenByRecentPeers
|
seenByDatesCount === 1 && seenByRecentPeers
|
||||||
? renderText(
|
? renderText(
|
||||||
@ -500,8 +517,8 @@ const MessageContextMenu: FC<OwnProps> = ({
|
|||||||
: (seenByRecentPeers[0] as ApiChat).title,
|
: (seenByRecentPeers[0] as ApiChat).title,
|
||||||
) : (
|
) : (
|
||||||
seenByDatesCount
|
seenByDatesCount
|
||||||
? lang('Conversation.ContextMenuSeen', seenByDatesCount, 'i')
|
? oldLang('Conversation.ContextMenuSeen', seenByDatesCount, 'i')
|
||||||
: lang('Conversation.ContextMenuNoViews')
|
: oldLang('Conversation.ContextMenuNoViews')
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
141
src/components/middle/message/TodoList.scss
Normal file
141
src/components/middle/message/TodoList.scss
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
.todo-list {
|
||||||
|
min-width: 15rem;
|
||||||
|
text-align: initial;
|
||||||
|
|
||||||
|
.todo-list-readonly-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-readonly-item-checkbox {
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
margin-inline-end: 0.5rem;
|
||||||
|
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.readonly-item-label {
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-item-bullet-point {
|
||||||
|
width: 0.25rem;
|
||||||
|
height: 0.25rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.completed-label {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Checkbox {
|
||||||
|
min-height: 2.5rem;
|
||||||
|
padding-bottom: 1.25rem;
|
||||||
|
padding-left: 2.5rem;
|
||||||
|
|
||||||
|
&.withSubLabel {
|
||||||
|
padding-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
opacity: 1 !important;
|
||||||
|
|
||||||
|
.Checkbox-main {
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Checkbox-main {
|
||||||
|
padding: 0 !important;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
--color-borders-input: var(--secondary-color);
|
||||||
|
|
||||||
|
background-color: var(--background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
outline: 1px solid var(--background-color);
|
||||||
|
|
||||||
|
.theme-dark & {
|
||||||
|
background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEzLjkuOEw1LjggOC45IDIuMSA1LjJjLS40LS40LTEuMS0uNC0xLjYgMC0uNC40LS40IDEuMSAwIDEuNkw1IDExLjJjLjQuNCAxLjEuNCAxLjYgMGw4LjktOC45Yy40LS40LjQtMS4xIDAtMS42LS41LS40LTEuMi0uNC0xLjYuMXoiIGZpbGw9IiM3NjZhYzgiLz48L3N2Zz4=);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-avatar,
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
top: 0.6875rem;
|
||||||
|
left: 0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
left: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
line-height: 1.3125rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked ~ .Checkbox-main {
|
||||||
|
&::before {
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Spinner {
|
||||||
|
top: 0.6875rem;
|
||||||
|
left: 0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.loading {
|
||||||
|
.Spinner {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-list-title {
|
||||||
|
margin: 0.125rem 0;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
line-height: 1.25rem;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-type,
|
||||||
|
.completed-tasks-count {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--secondary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-type {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.completed-tasks-count {
|
||||||
|
margin: 0 0 1.125rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
min-width: 50vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
197
src/components/middle/message/TodoList.tsx
Normal file
197
src/components/middle/message/TodoList.tsx
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
import {
|
||||||
|
memo, useEffect, useMemo, useState,
|
||||||
|
} from '../../../lib/teact/teact';
|
||||||
|
import { getActions, getGlobal, withGlobal } from '../../../global';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ApiMediaTodo,
|
||||||
|
ApiMessage,
|
||||||
|
ApiPeer,
|
||||||
|
} from '../../../api/types';
|
||||||
|
|
||||||
|
import { getPeerFullTitle, getPeerTitle } from '../../../global/helpers/peers';
|
||||||
|
import { selectIsCurrentUserPremium, selectSender, selectUser } from '../../../global/selectors';
|
||||||
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
|
||||||
|
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import AnimatedCounter from '../../common/AnimatedCounter';
|
||||||
|
import Icon from '../../common/icons/Icon';
|
||||||
|
import CheckboxGroup from '../../ui/CheckboxGroup';
|
||||||
|
|
||||||
|
import './TodoList.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
message: ApiMessage;
|
||||||
|
todoList: ApiMediaTodo;
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
sender?: ApiPeer;
|
||||||
|
isCurrentUserPremium: boolean;
|
||||||
|
isSynced?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TodoList = ({
|
||||||
|
message,
|
||||||
|
todoList,
|
||||||
|
sender,
|
||||||
|
isCurrentUserPremium,
|
||||||
|
isSynced,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
|
const { toggleTodoCompleted, showNotification } = getActions();
|
||||||
|
const { todo, completions } = todoList;
|
||||||
|
const { title, items, othersCanComplete } = todo;
|
||||||
|
const [completedTasks, setCompletedTasks] = useState<string[]>([]);
|
||||||
|
const completedTasksSet = useMemo(() => new Set(completedTasks), [completedTasks]);
|
||||||
|
|
||||||
|
const canToggle = !message.isScheduled && isCurrentUserPremium && isSynced;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const completedIds = completions?.map((c) => c.itemId.toString()) || [];
|
||||||
|
setCompletedTasks(completedIds);
|
||||||
|
}, [completions]);
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
const handleTaskLabelClick = useLastCallback((e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (!isCurrentUserPremium) {
|
||||||
|
showNotification({
|
||||||
|
message: lang('SubscribeToTelegramPremiumForToggleTask'),
|
||||||
|
action: {
|
||||||
|
action: 'openPremiumModal',
|
||||||
|
payload: { initialSection: 'todo' },
|
||||||
|
},
|
||||||
|
actionText: lang('PremiumMore'),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleTaskToggle = useLastCallback((newCompletedTasks: string[]) => {
|
||||||
|
const newCompletedId = newCompletedTasks.find((id) => !completedTasksSet.has(id));
|
||||||
|
const newIncompletedId = Array.from(completedTasksSet).find((id) => !newCompletedTasks.includes(id));
|
||||||
|
|
||||||
|
toggleTodoCompleted({
|
||||||
|
chatId: message.chatId,
|
||||||
|
messageId: message.id,
|
||||||
|
completedIds: newCompletedId ? [Number(newCompletedId)] : [],
|
||||||
|
incompletedIds: newIncompletedId ? [Number(newIncompletedId)] : [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const isReadOnly = Boolean(message.forwardInfo) || (!othersCanComplete && !message.isOutgoing);
|
||||||
|
const isOutgoing = message.isOutgoing;
|
||||||
|
|
||||||
|
const tasks = useMemo(() => items.map((task) => {
|
||||||
|
const user = !othersCanComplete ? undefined : selectUser(getGlobal(),
|
||||||
|
completions?.find((c) => c.itemId === task.id)?.completedBy || '');
|
||||||
|
const subLabel = user ? getPeerFullTitle(lang, user) : undefined;
|
||||||
|
return {
|
||||||
|
label: renderTextWithEntities(task.title),
|
||||||
|
value: task.id.toString(),
|
||||||
|
user,
|
||||||
|
subLabel,
|
||||||
|
};
|
||||||
|
}), [items, othersCanComplete, completions, lang]);
|
||||||
|
|
||||||
|
const renderCheckBoxGroup = () => {
|
||||||
|
return (
|
||||||
|
<CheckboxGroup
|
||||||
|
options={tasks}
|
||||||
|
selected={completedTasks}
|
||||||
|
onChange={handleTaskToggle}
|
||||||
|
onClickLabel={!isCurrentUserPremium ? handleTaskLabelClick : undefined}
|
||||||
|
disabled={!canToggle}
|
||||||
|
isRound
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderReadOnlyTodoList = () => {
|
||||||
|
return (
|
||||||
|
<div className="todo-list-items">
|
||||||
|
{tasks.map((task) => (
|
||||||
|
<div
|
||||||
|
key={task.value}
|
||||||
|
className="todo-list-readonly-item"
|
||||||
|
>
|
||||||
|
<div className="todo-readonly-item-checkbox">
|
||||||
|
{completedTasksSet.has(task.value)
|
||||||
|
? <Icon name="check" />
|
||||||
|
: <div className="todo-item-bullet-point" />}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={buildClassName(
|
||||||
|
'readonly-item-label',
|
||||||
|
completedTasksSet.has(task.value) && 'completed-label',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{task.label}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTodoListType = () => {
|
||||||
|
if (message.forwardInfo) {
|
||||||
|
return lang('TitleToDoList');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (othersCanComplete) {
|
||||||
|
return lang('TitleGroupToDoList');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOutgoing) {
|
||||||
|
return lang('TitleYourToDoList');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sender) {
|
||||||
|
return lang('TitleUserToDoList', { peer: getPeerTitle(lang, sender) }, { withNodes: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return lang('TitleToDoList');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="todo-list" dir={lang.isRtl ? 'auto' : 'ltr'}>
|
||||||
|
<div className="todo-list-header">
|
||||||
|
<div className="todo-list-title">
|
||||||
|
{renderTextWithEntities(title)}
|
||||||
|
</div>
|
||||||
|
<div className="list-type">
|
||||||
|
{renderTodoListType()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="todo-list-items">
|
||||||
|
{isReadOnly ? renderReadOnlyTodoList() : renderCheckBoxGroup()}
|
||||||
|
</div>
|
||||||
|
<div className="completed-tasks-count">
|
||||||
|
<AnimatedCounter text={
|
||||||
|
lang('DescriptionCompletedToDoTasks', {
|
||||||
|
number: completedTasks.length,
|
||||||
|
count: tasks.length,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>((global, { message }): StateProps => {
|
||||||
|
const sender = selectSender(global, message);
|
||||||
|
return {
|
||||||
|
sender,
|
||||||
|
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
||||||
|
isSynced: global.isSynced,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(TodoList));
|
||||||
@ -103,12 +103,21 @@ export function renderPeerLink(peerId: string | undefined, text: string, asPrevi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderMessageLink(targetMessage: ApiMessage, text: TeactNode, asPreview?: boolean) {
|
export function renderMessageLink(
|
||||||
if (asPreview) return text;
|
targetMessage: ApiMessage | undefined,
|
||||||
|
text: TeactNode,
|
||||||
|
asPreview: boolean | undefined,
|
||||||
|
params?: {
|
||||||
|
noEllipsis?: boolean;
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const { noEllipsis } = params || {};
|
||||||
|
|
||||||
|
if (asPreview || !targetMessage) return text;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
className={styles.messageLink}
|
className={buildClassName(styles.messageLink, noEllipsis && styles.noEllipsis)}
|
||||||
|
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
getActions().focusMessage({ chatId: targetMessage.chatId, messageId: targetMessage.id });
|
getActions().focusMessage({ chatId: targetMessage.chatId, messageId: targetMessage.id });
|
||||||
|
|||||||
@ -24,7 +24,7 @@ type StateProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ChatRefundModal = ({ modal, user }: OwnProps & StateProps) => {
|
const ChatRefundModal = ({ modal, user }: OwnProps & StateProps) => {
|
||||||
const { closeChatRefundModal, addNoPaidMessagesException } = getActions();
|
const { closeChatRefundModal, toggleNoPaidMessagesException } = getActions();
|
||||||
|
|
||||||
const [shouldRefundStars, setShouldRefundStars] = useState(false);
|
const [shouldRefundStars, setShouldRefundStars] = useState(false);
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ const ChatRefundModal = ({ modal, user }: OwnProps & StateProps) => {
|
|||||||
const handleConfirmRemoveFee = useLastCallback(() => {
|
const handleConfirmRemoveFee = useLastCallback(() => {
|
||||||
closeChatRefundModal();
|
closeChatRefundModal();
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
addNoPaidMessagesException({ userId, shouldRefundCharged: shouldRefundStars });
|
toggleNoPaidMessagesException ({ userId, shouldRefundCharged: shouldRefundStars });
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -27,6 +27,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
transition: opacity 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
&.round {
|
&.round {
|
||||||
.Checkbox-main {
|
.Checkbox-main {
|
||||||
&::before, &::after {
|
&::before, &::after {
|
||||||
@ -101,11 +111,6 @@
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
content: "";
|
content: "";
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 1.125rem; // 1 + ((1.5 - 1.25) / 2)
|
|
||||||
transform: translateY(-50%);
|
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
width: 1.25rem;
|
width: 1.25rem;
|
||||||
@ -127,6 +132,15 @@
|
|||||||
transition: opacity 0.1s ease;
|
transition: opacity 0.1s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-avatar,
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 1.125rem; // 1 + ((1.5 - 1.25) / 2)
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
unicode-bidi: plaintext;
|
unicode-bidi: plaintext;
|
||||||
line-height: 1.25rem;
|
line-height: 1.25rem;
|
||||||
@ -202,6 +216,11 @@
|
|||||||
&::after {
|
&::after {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
.user-avatar {
|
||||||
|
&.user-avatar-visible {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&[dir="rtl"] {
|
&[dir="rtl"] {
|
||||||
@ -218,6 +237,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.Checkbox-main {
|
.Checkbox-main {
|
||||||
|
.user-avatar,
|
||||||
&::before,
|
&::before,
|
||||||
&::after {
|
&::after {
|
||||||
right: 1rem;
|
right: 1rem;
|
||||||
|
|||||||
@ -7,15 +7,19 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { ApiUser } from '../../api/types';
|
||||||
import type { IconName } from '../../types/icons';
|
import type { IconName } from '../../types/icons';
|
||||||
import type { IRadioOption } from './CheckboxGroup';
|
import type { IRadioOption } from './CheckboxGroup';
|
||||||
|
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
import { REM } from '../common/helpers/mediaDimensions';
|
||||||
import renderText from '../common/helpers/renderText';
|
import renderText from '../common/helpers/renderText';
|
||||||
|
|
||||||
|
import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
|
||||||
import useLastCallback from '../../hooks/useLastCallback';
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../hooks/useOldLang';
|
import useOldLang from '../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import Avatar from '../common/Avatar';
|
||||||
import Icon from '../common/icons/Icon';
|
import Icon from '../common/icons/Icon';
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
import Spinner from './Spinner';
|
import Spinner from './Spinner';
|
||||||
@ -26,6 +30,7 @@ type OwnProps = {
|
|||||||
id?: string;
|
id?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
|
user?: ApiUser;
|
||||||
label?: TeactNode;
|
label?: TeactNode;
|
||||||
labelText?: TeactNode;
|
labelText?: TeactNode;
|
||||||
subLabel?: string;
|
subLabel?: string;
|
||||||
@ -50,12 +55,14 @@ type OwnProps = {
|
|||||||
leftElement?: TeactNode;
|
leftElement?: TeactNode;
|
||||||
values?: string[];
|
values?: string[];
|
||||||
};
|
};
|
||||||
|
const AVATAR_SIZE = 1.25 * REM;
|
||||||
|
|
||||||
const Checkbox: FC<OwnProps> = ({
|
const Checkbox: FC<OwnProps> = ({
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
label,
|
label,
|
||||||
|
user,
|
||||||
labelText,
|
labelText,
|
||||||
subLabel,
|
subLabel,
|
||||||
checked,
|
checked,
|
||||||
@ -81,6 +88,7 @@ const Checkbox: FC<OwnProps> = ({
|
|||||||
const lang = useOldLang();
|
const lang = useOldLang();
|
||||||
const labelRef = useRef<HTMLLabelElement>();
|
const labelRef = useRef<HTMLLabelElement>();
|
||||||
const [showNested, setShowNested] = useState(false);
|
const [showNested, setShowNested] = useState(false);
|
||||||
|
const renderingUser = useCurrentOrPrev(user, true);
|
||||||
|
|
||||||
const handleChange = useLastCallback((event: ChangeEvent<HTMLInputElement>) => {
|
const handleChange = useLastCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
@ -151,6 +159,14 @@ const Checkbox: FC<OwnProps> = ({
|
|||||||
Boolean(leftElement) && 'Nested-avatar-list',
|
Boolean(leftElement) && 'Nested-avatar-list',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
<div className={buildClassName('user-avatar', renderingUser && 'user-avatar-visible')}>
|
||||||
|
{renderingUser && (
|
||||||
|
<Avatar
|
||||||
|
peer={renderingUser}
|
||||||
|
size={AVATAR_SIZE}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<span className="label" dir="auto">
|
<span className="label" dir="auto">
|
||||||
{leftElement}
|
{leftElement}
|
||||||
{typeof label === 'string' ? renderText(label) : label}
|
{typeof label === 'string' ? renderText(label) : label}
|
||||||
|
|||||||
@ -28,6 +28,7 @@ type OwnProps = {
|
|||||||
loadingOptions?: string[];
|
loadingOptions?: string[];
|
||||||
isRound?: boolean;
|
isRound?: boolean;
|
||||||
onChange: (value: string[]) => void;
|
onChange: (value: string[]) => void;
|
||||||
|
onClickLabel?: (e: React.MouseEvent, value?: string) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ const CheckboxGroup: FC<OwnProps> = ({
|
|||||||
loadingOptions,
|
loadingOptions,
|
||||||
isRound,
|
isRound,
|
||||||
onChange,
|
onChange,
|
||||||
|
onClickLabel,
|
||||||
className,
|
className,
|
||||||
}) => {
|
}) => {
|
||||||
const handleChange = useLastCallback((event: ChangeEvent<HTMLInputElement>, nestedOptionList?: IRadioOption) => {
|
const handleChange = useLastCallback((event: ChangeEvent<HTMLInputElement>, nestedOptionList?: IRadioOption) => {
|
||||||
@ -87,10 +89,12 @@ const CheckboxGroup: FC<OwnProps> = ({
|
|||||||
label={option.label}
|
label={option.label}
|
||||||
subLabel={option.subLabel}
|
subLabel={option.subLabel}
|
||||||
value={option.value}
|
value={option.value}
|
||||||
|
user={option.user}
|
||||||
checked={selected?.indexOf(option.value) !== -1}
|
checked={selected?.indexOf(option.value) !== -1}
|
||||||
disabled={option.disabled || disabled}
|
disabled={option.disabled || disabled}
|
||||||
isLoading={loadingOptions ? loadingOptions.indexOf(option.value) !== -1 : undefined}
|
isLoading={loadingOptions ? loadingOptions.indexOf(option.value) !== -1 : undefined}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
onClickLabel={onClickLabel}
|
||||||
nestedCheckbox={nestedCheckbox}
|
nestedCheckbox={nestedCheckbox}
|
||||||
nestedCheckboxCount={getCheckedNestedCount(option.nestedOptions ?? [])}
|
nestedCheckboxCount={getCheckedNestedCount(option.nestedOptions ?? [])}
|
||||||
nestedOptionList={option}
|
nestedOptionList={option}
|
||||||
|
|||||||
@ -106,6 +106,9 @@ export const STORY_LIST_LIMIT = 100;
|
|||||||
export const API_GENERAL_ID_LIMIT = 100;
|
export const API_GENERAL_ID_LIMIT = 100;
|
||||||
export const STATISTICS_PUBLIC_FORWARDS_LIMIT = 50;
|
export const STATISTICS_PUBLIC_FORWARDS_LIMIT = 50;
|
||||||
export const RESALE_GIFTS_LIMIT = 50;
|
export const RESALE_GIFTS_LIMIT = 50;
|
||||||
|
export const TODO_ITEMS_LIMIT = 30;
|
||||||
|
export const TODO_TITLE_LENGTH_LIMIT = 32;
|
||||||
|
export const TODO_ITEM_LENGTH_LIMIT = 64;
|
||||||
|
|
||||||
export const STORY_VIEWS_MIN_SEARCH = 15;
|
export const STORY_VIEWS_MIN_SEARCH = 15;
|
||||||
export const STORY_MIN_REACTIONS_SORT = 10;
|
export const STORY_MIN_REACTIONS_SORT = 10;
|
||||||
@ -429,6 +432,7 @@ export const PREMIUM_FEATURE_SECTIONS = [
|
|||||||
'last_seen',
|
'last_seen',
|
||||||
'message_privacy',
|
'message_privacy',
|
||||||
'effects',
|
'effects',
|
||||||
|
'todo',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const PREMIUM_BOTTOM_VIDEOS: ApiPremiumSection[] = [
|
export const PREMIUM_BOTTOM_VIDEOS: ApiPremiumSection[] = [
|
||||||
@ -444,6 +448,7 @@ export const PREMIUM_BOTTOM_VIDEOS: ApiPremiumSection[] = [
|
|||||||
'last_seen',
|
'last_seen',
|
||||||
'message_privacy',
|
'message_privacy',
|
||||||
'effects',
|
'effects',
|
||||||
|
'todo',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const PREMIUM_LIMITS_ORDER: ApiLimitTypeForPromo[] = [
|
export const PREMIUM_LIMITS_ORDER: ApiLimitTypeForPromo[] = [
|
||||||
|
|||||||
@ -53,6 +53,7 @@ import { getTranslationFn, type RegularLangFnParameters } from '../../../util/lo
|
|||||||
import { formatStarsAsText } from '../../../util/localization/format';
|
import { formatStarsAsText } from '../../../util/localization/format';
|
||||||
import { oldTranslate } from '../../../util/oldLangProvider';
|
import { oldTranslate } from '../../../util/oldLangProvider';
|
||||||
import { debounce, onTickEnd, rafPromise } from '../../../util/schedulers';
|
import { debounce, onTickEnd, rafPromise } from '../../../util/schedulers';
|
||||||
|
import { getServerTime } from '../../../util/serverTime';
|
||||||
import { callApi, cancelApiProgress } from '../../../api/gramjs';
|
import { callApi, cancelApiProgress } from '../../../api/gramjs';
|
||||||
import {
|
import {
|
||||||
getIsSavedDialog,
|
getIsSavedDialog,
|
||||||
@ -146,6 +147,7 @@ import {
|
|||||||
selectUserStatus,
|
selectUserStatus,
|
||||||
selectViewportIds,
|
selectViewportIds,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
|
import { updateWithLocalMedia } from '../apiUpdaters/messages';
|
||||||
import { deleteMessages } from '../apiUpdaters/messages';
|
import { deleteMessages } from '../apiUpdaters/messages';
|
||||||
|
|
||||||
const AUTOLOGIN_TOKEN_KEY = 'autologin_token';
|
const AUTOLOGIN_TOKEN_KEY = 'autologin_token';
|
||||||
@ -557,6 +559,24 @@ addActionHandler('editMessage', (global, actions, payload): ActionReturnType =>
|
|||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('editTodo', (global, actions, payload): ActionReturnType => {
|
||||||
|
const {
|
||||||
|
chatId, todo, messageId,
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
const message = selectChatMessage(global, chatId, messageId);
|
||||||
|
if (!chat || !message) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callApi('editTodo', {
|
||||||
|
chat,
|
||||||
|
message,
|
||||||
|
todo,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('cancelUploadMedia', (global, actions, payload): ActionReturnType => {
|
addActionHandler('cancelUploadMedia', (global, actions, payload): ActionReturnType => {
|
||||||
const { chatId, messageId } = payload;
|
const { chatId, messageId } = payload;
|
||||||
|
|
||||||
@ -1138,6 +1158,71 @@ addActionHandler('sendPollVote', (global, actions, payload): ActionReturnType =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('toggleTodoCompleted', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { chatId, messageId, completedIds, incompletedIds } = payload;
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
const message = selectChatMessage(global, chatId, messageId);
|
||||||
|
const currentUserId = global.currentUserId;
|
||||||
|
|
||||||
|
const currentTodo = message?.content.todo;
|
||||||
|
if (!currentTodo || !currentUserId || !chat) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentCompletions = currentTodo.completions || [];
|
||||||
|
const currentCompletionIds = currentCompletions.map((c) => c.itemId);
|
||||||
|
|
||||||
|
const newCompletions = [...currentCompletions];
|
||||||
|
const now = getServerTime();
|
||||||
|
|
||||||
|
completedIds.forEach((itemId) => {
|
||||||
|
if (!currentCompletionIds.includes(itemId)) {
|
||||||
|
newCompletions.push({
|
||||||
|
itemId,
|
||||||
|
completedBy: currentUserId,
|
||||||
|
completedAt: now,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const finalCompletions = newCompletions.filter((c) => !incompletedIds.includes(c.itemId));
|
||||||
|
|
||||||
|
const newContent = {
|
||||||
|
...message.content,
|
||||||
|
todo: {
|
||||||
|
...currentTodo,
|
||||||
|
completions: finalCompletions,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const messageUpdate: Partial<ApiMessage> = {
|
||||||
|
...message,
|
||||||
|
content: newContent,
|
||||||
|
};
|
||||||
|
|
||||||
|
global = updateWithLocalMedia(global, chatId, message.id, messageUpdate);
|
||||||
|
setGlobal(global);
|
||||||
|
|
||||||
|
callApi('toggleTodoCompleted', { chat, messageId: message.id, completedIds, incompletedIds });
|
||||||
|
});
|
||||||
|
addActionHandler('appendTodoList', (global, actions, payload): ActionReturnType => {
|
||||||
|
const {
|
||||||
|
chatId, items, messageId,
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
const message = selectChatMessage(global, chatId, messageId);
|
||||||
|
if (!chat || !message) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callApi('appendTodoList', {
|
||||||
|
chat,
|
||||||
|
message,
|
||||||
|
items,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('cancelPollVote', (global, actions, payload): ActionReturnType => {
|
addActionHandler('cancelPollVote', (global, actions, payload): ActionReturnType => {
|
||||||
const { chatId, messageId } = payload;
|
const { chatId, messageId } = payload;
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
|||||||
import * as langProvider from '../../../util/oldLangProvider';
|
import * as langProvider from '../../../util/oldLangProvider';
|
||||||
import { getStripeError } from '../../../util/payments/stripe';
|
import { getStripeError } from '../../../util/payments/stripe';
|
||||||
import { buildQueryString } from '../../../util/requestQuery';
|
import { buildQueryString } from '../../../util/requestQuery';
|
||||||
|
import { getServerTime } from '../../../util/serverTime';
|
||||||
import { extractCurrentThemeParams } from '../../../util/themeStyle';
|
import { extractCurrentThemeParams } from '../../../util/themeStyle';
|
||||||
import { callApi } from '../../../api/gramjs';
|
import { callApi } from '../../../api/gramjs';
|
||||||
import { isChatChannel, isChatSuperGroup } from '../../helpers';
|
import { isChatChannel, isChatSuperGroup } from '../../helpers';
|
||||||
@ -827,7 +828,7 @@ addActionHandler('applyBoost', async (global, actions, payload): Promise<void> =
|
|||||||
const oldMyBoosts = tabState.boostModal?.myBoosts;
|
const oldMyBoosts = tabState.boostModal?.myBoosts;
|
||||||
|
|
||||||
if (oldMyBoosts) {
|
if (oldMyBoosts) {
|
||||||
const unixNow = Math.floor(Date.now() / 1000);
|
const unixNow = getServerTime();
|
||||||
const newMyBoosts = oldMyBoosts.map((boost) => {
|
const newMyBoosts = oldMyBoosts.map((boost) => {
|
||||||
if (slots.includes(boost.slot)) {
|
if (slots.includes(boost.slot)) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -191,14 +191,14 @@ addActionHandler('loadCommonChats', async (global, actions, payload): Promise<vo
|
|||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('addNoPaidMessagesException', async (global, actions, payload): Promise<void> => {
|
addActionHandler('toggleNoPaidMessagesException', async (global, actions, payload): Promise<void> => {
|
||||||
const { userId, shouldRefundCharged } = payload;
|
const { userId, shouldRefundCharged } = payload;
|
||||||
const user = selectUser(global, userId);
|
const user = selectUser(global, userId);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await callApi('addNoPaidMessagesException',
|
const result = await callApi('toggleNoPaidMessagesException',
|
||||||
{ user, shouldRefundCharged });
|
{ user, shouldRefundCharged });
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -940,7 +940,7 @@ function updateReactions<T extends GlobalState>(
|
|||||||
return global;
|
return global;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateWithLocalMedia(
|
export function updateWithLocalMedia(
|
||||||
global: RequiredGlobalState,
|
global: RequiredGlobalState,
|
||||||
chatId: string,
|
chatId: string,
|
||||||
id: number,
|
id: number,
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import {
|
|||||||
isChatChannel,
|
isChatChannel,
|
||||||
} from '../../helpers';
|
} from '../../helpers';
|
||||||
import { getMessageSummaryText } from '../../helpers/messageSummary';
|
import { getMessageSummaryText } from '../../helpers/messageSummary';
|
||||||
|
import { addTabStateResetterAction } from '../../helpers/meta';
|
||||||
import { getPeerTitle } from '../../helpers/peers';
|
import { getPeerTitle } from '../../helpers/peers';
|
||||||
import { renderMessageSummaryHtml } from '../../helpers/renderMessageSummaryHtml';
|
import { renderMessageSummaryHtml } from '../../helpers/renderMessageSummaryHtml';
|
||||||
import { addActionHandler, getGlobal, setGlobal } from '../../index';
|
import { addActionHandler, getGlobal, setGlobal } from '../../index';
|
||||||
@ -762,6 +763,22 @@ addActionHandler('closePollModal', (global, actions, payload): ActionReturnType
|
|||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('openTodoListModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const {
|
||||||
|
chatId, messageId, isAddTaskMode, tabId = getCurrentTabId(),
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
return updateTabState(global, {
|
||||||
|
todoListModal: {
|
||||||
|
chatId,
|
||||||
|
messageId,
|
||||||
|
isAddTaskMode,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
|
addTabStateResetterAction('closeTodoListModal', 'todoListModal');
|
||||||
|
|
||||||
addActionHandler('checkVersionNotification', (global, actions): ActionReturnType => {
|
addActionHandler('checkVersionNotification', (global, actions): ActionReturnType => {
|
||||||
if (RELEASE_DATETIME && Date.now() > Number(RELEASE_DATETIME) + VERSION_NOTIFICATION_DURATION) {
|
if (RELEASE_DATETIME && Date.now() > Number(RELEASE_DATETIME) + VERSION_NOTIFICATION_DURATION) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -189,6 +189,7 @@ export interface IAllowedAttachmentOptions {
|
|||||||
canSendVoices: boolean;
|
canSendVoices: boolean;
|
||||||
canSendPlainText: boolean;
|
canSendPlainText: boolean;
|
||||||
canSendDocuments: boolean;
|
canSendDocuments: boolean;
|
||||||
|
canAttachToDoLists: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAllowedAttachmentOptions(
|
export function getAllowedAttachmentOptions(
|
||||||
@ -214,6 +215,7 @@ export function getAllowedAttachmentOptions(
|
|||||||
canSendVoices: false,
|
canSendVoices: false,
|
||||||
canSendPlainText: false,
|
canSendPlainText: false,
|
||||||
canSendDocuments: false,
|
canSendDocuments: false,
|
||||||
|
canAttachToDoLists: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,6 +226,7 @@ export function getAllowedAttachmentOptions(
|
|||||||
canAttachPolls: !isStoryReply && !chat.isMonoforum
|
canAttachPolls: !isStoryReply && !chat.isMonoforum
|
||||||
&& (isAdmin || !isUserRightBanned(chat, 'sendPolls', chatFullInfo))
|
&& (isAdmin || !isUserRightBanned(chat, 'sendPolls', chatFullInfo))
|
||||||
&& (!isUserId(chat.id) || isChatWithBot || isSavedMessages),
|
&& (!isUserId(chat.id) || isChatWithBot || isSavedMessages),
|
||||||
|
canAttachToDoLists: !isStoryReply && !chat.isMonoforum && !isChatChannel(chat),
|
||||||
canSendStickers: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendStickers', chatFullInfo),
|
canSendStickers: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendStickers', chatFullInfo),
|
||||||
canSendGifs: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendGifs', chatFullInfo),
|
canSendGifs: isAdmin || isStoryReply || !isUserRightBanned(chat, 'sendGifs', chatFullInfo),
|
||||||
canAttachEmbedLinks: !isStoryReply && (isAdmin || !isUserRightBanned(chat, 'embedLinks', chatFullInfo)),
|
canAttachEmbedLinks: !isStoryReply && (isAdmin || !isUserRightBanned(chat, 'embedLinks', chatFullInfo)),
|
||||||
|
|||||||
@ -51,6 +51,7 @@ export function hasMessageMedia(message: MediaContainer) {
|
|||||||
|| getMessageSticker(message)
|
|| getMessageSticker(message)
|
||||||
|| getMessageContact(message)
|
|| getMessageContact(message)
|
||||||
|| getMessagePollId(message)
|
|| getMessagePollId(message)
|
||||||
|
|| getMessageTodo(message)
|
||||||
|| getMessageAction(message)
|
|| getMessageAction(message)
|
||||||
|| getMessageAudio(message)
|
|| getMessageAudio(message)
|
||||||
|| getMessageVoice(message)
|
|| getMessageVoice(message)
|
||||||
@ -128,6 +129,10 @@ export function getMessagePollId(message: MediaContainer) {
|
|||||||
return message.content.pollId;
|
return message.content.pollId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMessageTodo(message: MediaContainer) {
|
||||||
|
return message.content.todo;
|
||||||
|
}
|
||||||
|
|
||||||
export function getMessageInvoice(message: MediaContainer) {
|
export function getMessageInvoice(message: MediaContainer) {
|
||||||
return message.content.invoice;
|
return message.content.invoice;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,6 +72,7 @@ export function getMessageSummaryEmoji(message: ApiMessage) {
|
|||||||
sticker,
|
sticker,
|
||||||
pollId,
|
pollId,
|
||||||
paidMedia,
|
paidMedia,
|
||||||
|
todo,
|
||||||
} = message.content;
|
} = message.content;
|
||||||
|
|
||||||
if (message.groupedId || photo || paidMedia) {
|
if (message.groupedId || photo || paidMedia) {
|
||||||
@ -102,6 +103,10 @@ export function getMessageSummaryEmoji(message: ApiMessage) {
|
|||||||
return '📊';
|
return '📊';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (todo) {
|
||||||
|
return '📝';
|
||||||
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,6 +148,7 @@ function getSummaryDescription(
|
|||||||
giveaway,
|
giveaway,
|
||||||
giveawayResults,
|
giveawayResults,
|
||||||
paidMedia,
|
paidMedia,
|
||||||
|
todo,
|
||||||
} = mediaContent;
|
} = mediaContent;
|
||||||
const { poll } = statefulContent || {};
|
const { poll } = statefulContent || {};
|
||||||
|
|
||||||
@ -239,6 +245,10 @@ function getSummaryDescription(
|
|||||||
summary = truncatedText || (message ? lang('ForwardedStory') : lang('Chat.ReplyStory'));
|
summary = truncatedText || (message ? lang('ForwardedStory') : lang('Chat.ReplyStory'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (todo) {
|
||||||
|
summary = lang('Chat.Todo.Message.Title');
|
||||||
|
}
|
||||||
|
|
||||||
return summary || CONTENT_NOT_SUPPORTED;
|
return summary || CONTENT_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -52,13 +52,14 @@ export function getMessageTranscription(message: ApiMessage) {
|
|||||||
|
|
||||||
export function hasMessageText(message: MediaContainer) {
|
export function hasMessageText(message: MediaContainer) {
|
||||||
const {
|
const {
|
||||||
action, text, sticker, photo, video, audio, voice, document, pollId, webPage, contact, invoice, location,
|
action, text, sticker, photo, video, audio, voice, document, pollId, todo,
|
||||||
game, storyData, giveaway, giveawayResults, paidMedia,
|
webPage, contact, invoice, location, game, storyData, giveaway, giveawayResults, paidMedia,
|
||||||
} = message.content;
|
} = message.content;
|
||||||
|
|
||||||
return Boolean(text) || !(
|
return Boolean(text) || !(
|
||||||
sticker || photo || video || audio || voice || document || contact || pollId || webPage || invoice || location
|
sticker || photo || video || audio || voice || document || contact || pollId || todo || webPage
|
||||||
|| game || storyData || giveaway || giveawayResults || paidMedia || action?.type === 'phoneCall'
|
|| invoice || location || game || storyData || giveaway || giveawayResults
|
||||||
|
|| paidMedia || action?.type === 'phoneCall'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import type {
|
|||||||
ApiMessage,
|
ApiMessage,
|
||||||
ApiMessageEntity,
|
ApiMessageEntity,
|
||||||
ApiMessageSearchContext,
|
ApiMessageSearchContext,
|
||||||
|
ApiNewMediaTodo,
|
||||||
ApiNotification,
|
ApiNotification,
|
||||||
ApiNotifyPeerType,
|
ApiNotifyPeerType,
|
||||||
ApiPaymentStatus,
|
ApiPaymentStatus,
|
||||||
@ -45,6 +46,7 @@ import type {
|
|||||||
ApiStickerSet,
|
ApiStickerSet,
|
||||||
ApiStickerSetInfo,
|
ApiStickerSetInfo,
|
||||||
ApiThemeParameters,
|
ApiThemeParameters,
|
||||||
|
ApiTodoItem,
|
||||||
ApiTypePrepaidGiveaway,
|
ApiTypePrepaidGiveaway,
|
||||||
ApiUpdate,
|
ApiUpdate,
|
||||||
ApiUser,
|
ApiUser,
|
||||||
@ -502,6 +504,11 @@ export interface ActionPayloads {
|
|||||||
attachments?: ApiAttachment[];
|
attachments?: ApiAttachment[];
|
||||||
entities?: ApiMessageEntity[];
|
entities?: ApiMessageEntity[];
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
|
editTodo: {
|
||||||
|
chatId: string;
|
||||||
|
todo: ApiNewMediaTodo;
|
||||||
|
messageId: number;
|
||||||
|
} & WithTabId;
|
||||||
deleteHistory: {
|
deleteHistory: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
shouldDeleteForAll?: boolean;
|
shouldDeleteForAll?: boolean;
|
||||||
@ -1380,6 +1387,17 @@ export interface ActionPayloads {
|
|||||||
messageId: number;
|
messageId: number;
|
||||||
options: string[];
|
options: string[];
|
||||||
};
|
};
|
||||||
|
toggleTodoCompleted: {
|
||||||
|
chatId: string;
|
||||||
|
messageId: number;
|
||||||
|
completedIds: number[];
|
||||||
|
incompletedIds: number[];
|
||||||
|
};
|
||||||
|
appendTodoList: {
|
||||||
|
chatId: string;
|
||||||
|
items: ApiTodoItem[];
|
||||||
|
messageId: number;
|
||||||
|
} & WithTabId;
|
||||||
cancelPollVote: {
|
cancelPollVote: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
messageId: number;
|
messageId: number;
|
||||||
@ -1779,7 +1797,7 @@ export interface ActionPayloads {
|
|||||||
isMuted?: boolean;
|
isMuted?: boolean;
|
||||||
shouldSharePhoneNumber?: boolean;
|
shouldSharePhoneNumber?: boolean;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
addNoPaidMessagesException: {
|
toggleNoPaidMessagesException: {
|
||||||
userId: string;
|
userId: string;
|
||||||
shouldRefundCharged: boolean;
|
shouldRefundCharged: boolean;
|
||||||
};
|
};
|
||||||
@ -2182,6 +2200,12 @@ export interface ActionPayloads {
|
|||||||
isQuiz?: boolean;
|
isQuiz?: boolean;
|
||||||
} & WithTabId) | undefined;
|
} & WithTabId) | undefined;
|
||||||
closePollModal: WithTabId | undefined;
|
closePollModal: WithTabId | undefined;
|
||||||
|
openTodoListModal: {
|
||||||
|
chatId: string;
|
||||||
|
messageId?: number;
|
||||||
|
isAddTaskMode?: boolean;
|
||||||
|
} & WithTabId;
|
||||||
|
closeTodoListModal: WithTabId | undefined;
|
||||||
requestConfetti: (ConfettiParams & WithTabId) | WithTabId;
|
requestConfetti: (ConfettiParams & WithTabId) | WithTabId;
|
||||||
requestWave: {
|
requestWave: {
|
||||||
startX: number;
|
startX: number;
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import type {
|
|||||||
ApiMessage,
|
ApiMessage,
|
||||||
ApiMissingInvitedUser,
|
ApiMissingInvitedUser,
|
||||||
ApiMyBoost,
|
ApiMyBoost,
|
||||||
|
ApiNewMediaTodo,
|
||||||
ApiNewPoll,
|
ApiNewPoll,
|
||||||
ApiNotification,
|
ApiNotification,
|
||||||
ApiPaymentFormRegular,
|
ApiPaymentFormRegular,
|
||||||
@ -141,6 +142,7 @@ export type TabState = {
|
|||||||
gif?: ApiVideo;
|
gif?: ApiVideo;
|
||||||
sticker?: ApiSticker;
|
sticker?: ApiSticker;
|
||||||
poll?: ApiNewPoll;
|
poll?: ApiNewPoll;
|
||||||
|
todo?: ApiNewMediaTodo;
|
||||||
isSilent?: boolean;
|
isSilent?: boolean;
|
||||||
sendGrouped?: boolean;
|
sendGrouped?: boolean;
|
||||||
sendCompressed?: boolean;
|
sendCompressed?: boolean;
|
||||||
@ -530,6 +532,12 @@ export type TabState = {
|
|||||||
isQuiz?: boolean;
|
isQuiz?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
todoListModal?: {
|
||||||
|
chatId: string;
|
||||||
|
messageId?: number;
|
||||||
|
isAddTaskMode?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
preparedMessageModal?: {
|
preparedMessageModal?: {
|
||||||
message: ApiPreparedInlineMessage;
|
message: ApiPreparedInlineMessage;
|
||||||
webAppKey: string;
|
webAppKey: string;
|
||||||
|
|||||||
@ -12,5 +12,5 @@ for (const tl of Object.values(Api)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LAYER = 204;
|
export const LAYER = 205;
|
||||||
export { tlobjects };
|
export { tlobjects };
|
||||||
|
|||||||
169
src/lib/gramjs/tl/api.d.ts
vendored
169
src/lib/gramjs/tl/api.d.ts
vendored
File diff suppressed because one or more lines are too long
@ -37,6 +37,7 @@ inputMediaDice#e66fbf7b emoticon:string = InputMedia;
|
|||||||
inputMediaStory#89fdd778 peer:InputPeer id:int = InputMedia;
|
inputMediaStory#89fdd778 peer:InputPeer id:int = InputMedia;
|
||||||
inputMediaWebPage#c21b8849 flags:# force_large_media:flags.0?true force_small_media:flags.1?true optional:flags.2?true url:string = InputMedia;
|
inputMediaWebPage#c21b8849 flags:# force_large_media:flags.0?true force_small_media:flags.1?true optional:flags.2?true url:string = InputMedia;
|
||||||
inputMediaPaidMedia#c4103386 flags:# stars_amount:long extended_media:Vector<InputMedia> payload:flags.0?string = InputMedia;
|
inputMediaPaidMedia#c4103386 flags:# stars_amount:long extended_media:Vector<InputMedia> payload:flags.0?string = InputMedia;
|
||||||
|
inputMediaTodo#9fc55fde todo:TodoList = InputMedia;
|
||||||
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
|
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
|
||||||
inputChatUploadedPhoto#bdcdaec0 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.3?VideoSize = InputChatPhoto;
|
inputChatUploadedPhoto#bdcdaec0 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.3?VideoSize = InputChatPhoto;
|
||||||
inputChatPhoto#8953ad37 id:InputPhoto = InputChatPhoto;
|
inputChatPhoto#8953ad37 id:InputPhoto = InputChatPhoto;
|
||||||
@ -83,7 +84,7 @@ chatForbidden#6592a1a7 id:long title:string = Chat;
|
|||||||
channel#fe685355 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true signature_profiles:flags2.12?true autotranslation:flags2.15?true broadcast_messages_allowed:flags2.16?true monoforum:flags2.17?true forum_tabs:flags2.19?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> stories_max_id:flags2.4?int color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int subscription_until_date:flags2.11?int bot_verification_icon:flags2.13?long send_paid_messages_stars:flags2.14?long linked_monoforum_id:flags2.18?long = Chat;
|
channel#fe685355 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true signature_profiles:flags2.12?true autotranslation:flags2.15?true broadcast_messages_allowed:flags2.16?true monoforum:flags2.17?true forum_tabs:flags2.19?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector<Username> stories_max_id:flags2.4?int color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int subscription_until_date:flags2.11?int bot_verification_icon:flags2.13?long send_paid_messages_stars:flags2.14?long linked_monoforum_id:flags2.18?long = Chat;
|
||||||
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
|
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
|
||||||
chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull;
|
chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull;
|
||||||
channelFull#52d6806b flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true paid_media_allowed:flags2.14?true can_view_stars_revenue:flags2.15?true paid_reactions_available:flags2.16?true stargifts_available:flags2.19?true paid_messages_available:flags2.20?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet bot_verification:flags2.17?BotVerification stargifts_count:flags2.18?int = ChatFull;
|
channelFull#e07429de flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true paid_media_allowed:flags2.14?true can_view_stars_revenue:flags2.15?true paid_reactions_available:flags2.16?true stargifts_available:flags2.19?true paid_messages_available:flags2.20?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet bot_verification:flags2.17?BotVerification stargifts_count:flags2.18?int send_paid_messages_stars:flags2.21?long = ChatFull;
|
||||||
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
|
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
|
||||||
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
|
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
|
||||||
chatParticipantAdmin#a0933f5b user_id:long inviter_id:long date:int = ChatParticipant;
|
chatParticipantAdmin#a0933f5b user_id:long inviter_id:long date:int = ChatParticipant;
|
||||||
@ -93,7 +94,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
|
|||||||
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
|
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
|
||||||
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
|
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
|
||||||
message#eabcdd4d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long = Message;
|
message#eabcdd4d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long = Message;
|
||||||
messageService#d3d28540 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true reactions_are_possible:flags.9?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction reactions:flags.20?MessageReactions ttl_period:flags.25?int = Message;
|
messageService#7a800e0a flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true reactions_are_possible:flags.9?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer saved_peer_id:flags.28?Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction reactions:flags.20?MessageReactions ttl_period:flags.25?int = Message;
|
||||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||||
messageMediaPhoto#695150d7 flags:# spoiler:flags.3?true photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
|
messageMediaPhoto#695150d7 flags:# spoiler:flags.3?true photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
|
||||||
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
|
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
|
||||||
@ -111,6 +112,7 @@ messageMediaStory#68cb6283 flags:# via_mention:flags.1?true peer:Peer id:int sto
|
|||||||
messageMediaGiveaway#aa073beb flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector<long> countries_iso2:flags.1?Vector<string> prize_description:flags.3?string quantity:int months:flags.4?int stars:flags.5?long until_date:int = MessageMedia;
|
messageMediaGiveaway#aa073beb flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector<long> countries_iso2:flags.1?Vector<string> prize_description:flags.3?string quantity:int months:flags.4?int stars:flags.5?long until_date:int = MessageMedia;
|
||||||
messageMediaGiveawayResults#ceaa3ea1 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector<long> months:flags.4?int stars:flags.5?long prize_description:flags.1?string until_date:int = MessageMedia;
|
messageMediaGiveawayResults#ceaa3ea1 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector<long> months:flags.4?int stars:flags.5?long prize_description:flags.1?string until_date:int = MessageMedia;
|
||||||
messageMediaPaidMedia#a8852491 stars_amount:long extended_media:Vector<MessageExtendedMedia> = MessageMedia;
|
messageMediaPaidMedia#a8852491 stars_amount:long extended_media:Vector<MessageExtendedMedia> = MessageMedia;
|
||||||
|
messageMediaToDo#8a53b014 flags:# todo:TodoList completions:flags.0?Vector<TodoCompletion> = MessageMedia;
|
||||||
messageActionEmpty#b6aef7b0 = MessageAction;
|
messageActionEmpty#b6aef7b0 = MessageAction;
|
||||||
messageActionChatCreate#bd47cbad title:string users:Vector<long> = MessageAction;
|
messageActionChatCreate#bd47cbad title:string users:Vector<long> = MessageAction;
|
||||||
messageActionChatEditTitle#b5a1ce5a title:string = MessageAction;
|
messageActionChatEditTitle#b5a1ce5a title:string = MessageAction;
|
||||||
@ -162,6 +164,8 @@ messageActionStarGiftUnique#2e3ae60e flags:# upgrade:flags.0?true transferred:fl
|
|||||||
messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction;
|
messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction;
|
||||||
messageActionPaidMessagesPrice#84b88578 flags:# broadcast_messages_allowed:flags.0?true stars:long = MessageAction;
|
messageActionPaidMessagesPrice#84b88578 flags:# broadcast_messages_allowed:flags.0?true stars:long = MessageAction;
|
||||||
messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction;
|
messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction;
|
||||||
|
messageActionTodoCompletions#cc7c5c89 completed:Vector<int> incompleted:Vector<int> = MessageAction;
|
||||||
|
messageActionTodoAppendTasks#c7edbc83 list:Vector<TodoItem> = MessageAction;
|
||||||
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
|
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
|
||||||
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
||||||
photoEmpty#2331b22d id:long = Photo;
|
photoEmpty#2331b22d id:long = Photo;
|
||||||
@ -1087,8 +1091,8 @@ botCommandScopePeerUser#a1321f3 peer:InputPeer user_id:InputUser = BotCommandSco
|
|||||||
account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordResult;
|
account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordResult;
|
||||||
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
|
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
|
||||||
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
|
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
|
||||||
sponsoredMessage#4d93a990 flags:# recommended:flags.5?true can_report:flags.12?true random_id:bytes url:string title:string message:string entities:flags.1?Vector<MessageEntity> photo:flags.6?Photo media:flags.14?MessageMedia color:flags.13?PeerColor button_text:string sponsor_info:flags.7?string additional_info:flags.8?string = SponsoredMessage;
|
sponsoredMessage#7dbf8673 flags:# recommended:flags.5?true can_report:flags.12?true random_id:bytes url:string title:string message:string entities:flags.1?Vector<MessageEntity> photo:flags.6?Photo media:flags.14?MessageMedia color:flags.13?PeerColor button_text:string sponsor_info:flags.7?string additional_info:flags.8?string min_display_duration:flags.15?int max_display_duration:flags.15?int = SponsoredMessage;
|
||||||
messages.sponsoredMessages#c9ee1d87 flags:# posts_between:flags.0?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
|
messages.sponsoredMessages#ffda656d flags:# posts_between:flags.0?int start_delay:flags.1?int between_delay:flags.2?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
|
||||||
messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages;
|
messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages;
|
||||||
searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int count:int = SearchResultsCalendarPeriod;
|
searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int count:int = SearchResultsCalendarPeriod;
|
||||||
messages.searchResultsCalendar#147ee23c flags:# inexact:flags.0?true count:int min_date:int min_msg_id:int offset_id_offset:flags.1?int periods:Vector<SearchResultsCalendarPeriod> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SearchResultsCalendar;
|
messages.searchResultsCalendar#147ee23c flags:# inexact:flags.0?true count:int min_date:int min_msg_id:int offset_id_offset:flags.1?int periods:Vector<SearchResultsCalendarPeriod> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SearchResultsCalendar;
|
||||||
@ -1283,7 +1287,7 @@ storyReactionPublicForward#bbab2643 message:Message = StoryReaction;
|
|||||||
storyReactionPublicRepost#cfcd0f13 peer_id:Peer story:StoryItem = StoryReaction;
|
storyReactionPublicRepost#cfcd0f13 peer_id:Peer story:StoryItem = StoryReaction;
|
||||||
stories.storyReactionsList#aa5f789c flags:# count:int reactions:Vector<StoryReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryReactionsList;
|
stories.storyReactionsList#aa5f789c flags:# count:int reactions:Vector<StoryReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryReactionsList;
|
||||||
savedDialog#bd87cb6c flags:# pinned:flags.2?true peer:Peer top_message:int = SavedDialog;
|
savedDialog#bd87cb6c flags:# pinned:flags.2?true peer:Peer top_message:int = SavedDialog;
|
||||||
monoForumDialog#64407ea7 flags:# unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_reactions_count:int draft:flags.1?DraftMessage = SavedDialog;
|
monoForumDialog#64407ea7 flags:# unread_mark:flags.3?true nopaid_messages_exception:flags.4?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_reactions_count:int draft:flags.1?DraftMessage = SavedDialog;
|
||||||
messages.savedDialogs#f83ae221 dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
|
messages.savedDialogs#f83ae221 dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
|
||||||
messages.savedDialogsSlice#44ba9dd9 count:int dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
|
messages.savedDialogsSlice#44ba9dd9 count:int dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
|
||||||
messages.savedDialogsNotModified#c01f6fe8 count:int = messages.SavedDialogs;
|
messages.savedDialogsNotModified#c01f6fe8 count:int = messages.SavedDialogs;
|
||||||
@ -1438,6 +1442,9 @@ starGiftAttributeCounter#2eb1b658 attribute:StarGiftAttributeId count:int = Star
|
|||||||
payments.resaleStarGifts#947a12df flags:# count:int gifts:Vector<StarGift> next_offset:flags.0?string attributes:flags.1?Vector<StarGiftAttribute> attributes_hash:flags.1?long chats:Vector<Chat> counters:flags.2?Vector<StarGiftAttributeCounter> users:Vector<User> = payments.ResaleStarGifts;
|
payments.resaleStarGifts#947a12df flags:# count:int gifts:Vector<StarGift> next_offset:flags.0?string attributes:flags.1?Vector<StarGiftAttribute> attributes_hash:flags.1?long chats:Vector<Chat> counters:flags.2?Vector<StarGiftAttributeCounter> users:Vector<User> = payments.ResaleStarGifts;
|
||||||
stories.canSendStoryCount#c387c04e count_remains:int = stories.CanSendStoryCount;
|
stories.canSendStoryCount#c387c04e count_remains:int = stories.CanSendStoryCount;
|
||||||
pendingSuggestion#e7e82e12 suggestion:string title:TextWithEntities description:TextWithEntities url:string = PendingSuggestion;
|
pendingSuggestion#e7e82e12 suggestion:string title:TextWithEntities description:TextWithEntities url:string = PendingSuggestion;
|
||||||
|
todoItem#cba9a52f id:int title:TextWithEntities = TodoItem;
|
||||||
|
todoList#49b92a26 flags:# others_can_append:flags.0?true others_can_complete:flags.1?true title:TextWithEntities list:Vector<TodoItem> = TodoList;
|
||||||
|
todoCompletion#4cc120b7 id:int completed_by:long date:int = TodoCompletion;
|
||||||
---functions---
|
---functions---
|
||||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||||
initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
|
initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
|
||||||
@ -1503,8 +1510,7 @@ account.toggleUsername#58d6b376 username:string active:Bool = Bool;
|
|||||||
account.resolveBusinessChatLink#5492e5ee slug:string = account.ResolvedBusinessChatLinks;
|
account.resolveBusinessChatLink#5492e5ee slug:string = account.ResolvedBusinessChatLinks;
|
||||||
account.toggleSponsoredMessages#b9d9a38d enabled:Bool = Bool;
|
account.toggleSponsoredMessages#b9d9a38d enabled:Bool = Bool;
|
||||||
account.getCollectibleEmojiStatuses#2e7b4543 hash:long = account.EmojiStatuses;
|
account.getCollectibleEmojiStatuses#2e7b4543 hash:long = account.EmojiStatuses;
|
||||||
account.addNoPaidMessagesException#6f688aa7 flags:# refund_charged:flags.0?true user_id:InputUser = Bool;
|
account.getPaidMessagesRevenue#19ba4a67 flags:# parent_peer:flags.0?InputPeer user_id:InputUser = account.PaidMessagesRevenue;
|
||||||
account.getPaidMessagesRevenue#f1266f38 user_id:InputUser = account.PaidMessagesRevenue;
|
|
||||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||||
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
||||||
contacts.getContacts#5dd69e12 hash:long = contacts.Contacts;
|
contacts.getContacts#5dd69e12 hash:long = contacts.Contacts;
|
||||||
@ -1663,9 +1669,11 @@ messages.getPaidReactionPrivacy#472455aa = Updates;
|
|||||||
messages.viewSponsoredMessage#269e3643 random_id:bytes = Bool;
|
messages.viewSponsoredMessage#269e3643 random_id:bytes = Bool;
|
||||||
messages.clickSponsoredMessage#8235057e flags:# media:flags.0?true fullscreen:flags.1?true random_id:bytes = Bool;
|
messages.clickSponsoredMessage#8235057e flags:# media:flags.0?true fullscreen:flags.1?true random_id:bytes = Bool;
|
||||||
messages.reportSponsoredMessage#12cbf0c4 random_id:bytes option:bytes = channels.SponsoredMessageReportResult;
|
messages.reportSponsoredMessage#12cbf0c4 random_id:bytes option:bytes = channels.SponsoredMessageReportResult;
|
||||||
messages.getSponsoredMessages#9bd2f439 peer:InputPeer = messages.SponsoredMessages;
|
messages.getSponsoredMessages#3d6ce850 flags:# peer:InputPeer msg_id:flags.0?int = messages.SponsoredMessages;
|
||||||
messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage;
|
messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage;
|
||||||
messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector<int> = Bool;
|
messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector<int> = Bool;
|
||||||
|
messages.toggleTodoCompleted#d3e03124 peer:InputPeer msg_id:int completed:Vector<int> incompleted:Vector<int> = Updates;
|
||||||
|
messages.appendTodoList#21a61057 peer:InputPeer msg_id:int list:Vector<TodoItem> = Updates;
|
||||||
updates.getState#edd4882a = updates.State;
|
updates.getState#edd4882a = updates.State;
|
||||||
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
|
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
|
||||||
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
||||||
|
|||||||
@ -61,7 +61,7 @@
|
|||||||
"account.resolveBusinessChatLink",
|
"account.resolveBusinessChatLink",
|
||||||
"account.toggleSponsoredMessages",
|
"account.toggleSponsoredMessages",
|
||||||
"account.getCollectibleEmojiStatuses",
|
"account.getCollectibleEmojiStatuses",
|
||||||
"account.addNoPaidMessagesException",
|
"account.toggleNoPaidMessagesException ",
|
||||||
"account.getPaidMessagesRevenue",
|
"account.getPaidMessagesRevenue",
|
||||||
"account.getAccountTTL",
|
"account.getAccountTTL",
|
||||||
"account.setAccountTTL",
|
"account.setAccountTTL",
|
||||||
@ -225,6 +225,8 @@
|
|||||||
"messages.getSponsoredMessages",
|
"messages.getSponsoredMessages",
|
||||||
"messages.reportMessagesDelivery",
|
"messages.reportMessagesDelivery",
|
||||||
"messages.getPreparedInlineMessage",
|
"messages.getPreparedInlineMessage",
|
||||||
|
"messages.toggleTodoCompleted",
|
||||||
|
"messages.appendTodoList",
|
||||||
"updates.getState",
|
"updates.getState",
|
||||||
"updates.getDifference",
|
"updates.getDifference",
|
||||||
"updates.getChannelDifference",
|
"updates.getChannelDifference",
|
||||||
|
|||||||
@ -46,6 +46,7 @@ inputMediaDice#e66fbf7b emoticon:string = InputMedia;
|
|||||||
inputMediaStory#89fdd778 peer:InputPeer id:int = InputMedia;
|
inputMediaStory#89fdd778 peer:InputPeer id:int = InputMedia;
|
||||||
inputMediaWebPage#c21b8849 flags:# force_large_media:flags.0?true force_small_media:flags.1?true optional:flags.2?true url:string = InputMedia;
|
inputMediaWebPage#c21b8849 flags:# force_large_media:flags.0?true force_small_media:flags.1?true optional:flags.2?true url:string = InputMedia;
|
||||||
inputMediaPaidMedia#c4103386 flags:# stars_amount:long extended_media:Vector<InputMedia> payload:flags.0?string = InputMedia;
|
inputMediaPaidMedia#c4103386 flags:# stars_amount:long extended_media:Vector<InputMedia> payload:flags.0?string = InputMedia;
|
||||||
|
inputMediaTodo#9fc55fde todo:TodoList = InputMedia;
|
||||||
|
|
||||||
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
|
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
|
||||||
inputChatUploadedPhoto#bdcdaec0 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.3?VideoSize = InputChatPhoto;
|
inputChatUploadedPhoto#bdcdaec0 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.3?VideoSize = InputChatPhoto;
|
||||||
@ -103,7 +104,7 @@ channel#fe685355 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.
|
|||||||
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
|
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
|
||||||
|
|
||||||
chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull;
|
chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull;
|
||||||
channelFull#52d6806b flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true paid_media_allowed:flags2.14?true can_view_stars_revenue:flags2.15?true paid_reactions_available:flags2.16?true stargifts_available:flags2.19?true paid_messages_available:flags2.20?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet bot_verification:flags2.17?BotVerification stargifts_count:flags2.18?int = ChatFull;
|
channelFull#e07429de flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true paid_media_allowed:flags2.14?true can_view_stars_revenue:flags2.15?true paid_reactions_available:flags2.16?true stargifts_available:flags2.19?true paid_messages_available:flags2.20?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet bot_verification:flags2.17?BotVerification stargifts_count:flags2.18?int send_paid_messages_stars:flags2.21?long = ChatFull;
|
||||||
|
|
||||||
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
|
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
|
||||||
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
|
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
|
||||||
@ -117,7 +118,7 @@ chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:f
|
|||||||
|
|
||||||
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
|
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
|
||||||
message#eabcdd4d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long = Message;
|
message#eabcdd4d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long = Message;
|
||||||
messageService#d3d28540 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true reactions_are_possible:flags.9?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction reactions:flags.20?MessageReactions ttl_period:flags.25?int = Message;
|
messageService#7a800e0a flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true reactions_are_possible:flags.9?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer saved_peer_id:flags.28?Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction reactions:flags.20?MessageReactions ttl_period:flags.25?int = Message;
|
||||||
|
|
||||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||||
messageMediaPhoto#695150d7 flags:# spoiler:flags.3?true photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
|
messageMediaPhoto#695150d7 flags:# spoiler:flags.3?true photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
|
||||||
@ -136,6 +137,7 @@ messageMediaStory#68cb6283 flags:# via_mention:flags.1?true peer:Peer id:int sto
|
|||||||
messageMediaGiveaway#aa073beb flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector<long> countries_iso2:flags.1?Vector<string> prize_description:flags.3?string quantity:int months:flags.4?int stars:flags.5?long until_date:int = MessageMedia;
|
messageMediaGiveaway#aa073beb flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector<long> countries_iso2:flags.1?Vector<string> prize_description:flags.3?string quantity:int months:flags.4?int stars:flags.5?long until_date:int = MessageMedia;
|
||||||
messageMediaGiveawayResults#ceaa3ea1 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector<long> months:flags.4?int stars:flags.5?long prize_description:flags.1?string until_date:int = MessageMedia;
|
messageMediaGiveawayResults#ceaa3ea1 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector<long> months:flags.4?int stars:flags.5?long prize_description:flags.1?string until_date:int = MessageMedia;
|
||||||
messageMediaPaidMedia#a8852491 stars_amount:long extended_media:Vector<MessageExtendedMedia> = MessageMedia;
|
messageMediaPaidMedia#a8852491 stars_amount:long extended_media:Vector<MessageExtendedMedia> = MessageMedia;
|
||||||
|
messageMediaToDo#8a53b014 flags:# todo:TodoList completions:flags.0?Vector<TodoCompletion> = MessageMedia;
|
||||||
|
|
||||||
messageActionEmpty#b6aef7b0 = MessageAction;
|
messageActionEmpty#b6aef7b0 = MessageAction;
|
||||||
messageActionChatCreate#bd47cbad title:string users:Vector<long> = MessageAction;
|
messageActionChatCreate#bd47cbad title:string users:Vector<long> = MessageAction;
|
||||||
@ -188,6 +190,8 @@ messageActionStarGiftUnique#2e3ae60e flags:# upgrade:flags.0?true transferred:fl
|
|||||||
messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction;
|
messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction;
|
||||||
messageActionPaidMessagesPrice#84b88578 flags:# broadcast_messages_allowed:flags.0?true stars:long = MessageAction;
|
messageActionPaidMessagesPrice#84b88578 flags:# broadcast_messages_allowed:flags.0?true stars:long = MessageAction;
|
||||||
messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction;
|
messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction;
|
||||||
|
messageActionTodoCompletions#cc7c5c89 completed:Vector<int> incompleted:Vector<int> = MessageAction;
|
||||||
|
messageActionTodoAppendTasks#c7edbc83 list:Vector<TodoItem> = MessageAction;
|
||||||
|
|
||||||
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
|
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
|
||||||
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
||||||
@ -1407,9 +1411,9 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR
|
|||||||
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
|
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
|
||||||
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
|
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
|
||||||
|
|
||||||
sponsoredMessage#4d93a990 flags:# recommended:flags.5?true can_report:flags.12?true random_id:bytes url:string title:string message:string entities:flags.1?Vector<MessageEntity> photo:flags.6?Photo media:flags.14?MessageMedia color:flags.13?PeerColor button_text:string sponsor_info:flags.7?string additional_info:flags.8?string = SponsoredMessage;
|
sponsoredMessage#7dbf8673 flags:# recommended:flags.5?true can_report:flags.12?true random_id:bytes url:string title:string message:string entities:flags.1?Vector<MessageEntity> photo:flags.6?Photo media:flags.14?MessageMedia color:flags.13?PeerColor button_text:string sponsor_info:flags.7?string additional_info:flags.8?string min_display_duration:flags.15?int max_display_duration:flags.15?int = SponsoredMessage;
|
||||||
|
|
||||||
messages.sponsoredMessages#c9ee1d87 flags:# posts_between:flags.0?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
|
messages.sponsoredMessages#ffda656d flags:# posts_between:flags.0?int start_delay:flags.1?int between_delay:flags.2?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
|
||||||
messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages;
|
messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages;
|
||||||
|
|
||||||
searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int count:int = SearchResultsCalendarPeriod;
|
searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int count:int = SearchResultsCalendarPeriod;
|
||||||
@ -1715,7 +1719,7 @@ storyReactionPublicRepost#cfcd0f13 peer_id:Peer story:StoryItem = StoryReaction;
|
|||||||
stories.storyReactionsList#aa5f789c flags:# count:int reactions:Vector<StoryReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryReactionsList;
|
stories.storyReactionsList#aa5f789c flags:# count:int reactions:Vector<StoryReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = stories.StoryReactionsList;
|
||||||
|
|
||||||
savedDialog#bd87cb6c flags:# pinned:flags.2?true peer:Peer top_message:int = SavedDialog;
|
savedDialog#bd87cb6c flags:# pinned:flags.2?true peer:Peer top_message:int = SavedDialog;
|
||||||
monoForumDialog#64407ea7 flags:# unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_reactions_count:int draft:flags.1?DraftMessage = SavedDialog;
|
monoForumDialog#64407ea7 flags:# unread_mark:flags.3?true nopaid_messages_exception:flags.4?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_reactions_count:int draft:flags.1?DraftMessage = SavedDialog;
|
||||||
|
|
||||||
messages.savedDialogs#f83ae221 dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
|
messages.savedDialogs#f83ae221 dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
|
||||||
messages.savedDialogsSlice#44ba9dd9 count:int dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
|
messages.savedDialogsSlice#44ba9dd9 count:int dialogs:Vector<SavedDialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SavedDialogs;
|
||||||
@ -1983,6 +1987,12 @@ stories.canSendStoryCount#c387c04e count_remains:int = stories.CanSendStoryCount
|
|||||||
|
|
||||||
pendingSuggestion#e7e82e12 suggestion:string title:TextWithEntities description:TextWithEntities url:string = PendingSuggestion;
|
pendingSuggestion#e7e82e12 suggestion:string title:TextWithEntities description:TextWithEntities url:string = PendingSuggestion;
|
||||||
|
|
||||||
|
todoItem#cba9a52f id:int title:TextWithEntities = TodoItem;
|
||||||
|
|
||||||
|
todoList#49b92a26 flags:# others_can_append:flags.0?true others_can_complete:flags.1?true title:TextWithEntities list:Vector<TodoItem> = TodoList;
|
||||||
|
|
||||||
|
todoCompletion#4cc120b7 id:int completed_by:long date:int = TodoCompletion;
|
||||||
|
|
||||||
---functions---
|
---functions---
|
||||||
|
|
||||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||||
@ -2134,8 +2144,8 @@ account.toggleSponsoredMessages#b9d9a38d enabled:Bool = Bool;
|
|||||||
account.getReactionsNotifySettings#6dd654c = ReactionsNotifySettings;
|
account.getReactionsNotifySettings#6dd654c = ReactionsNotifySettings;
|
||||||
account.setReactionsNotifySettings#316ce548 settings:ReactionsNotifySettings = ReactionsNotifySettings;
|
account.setReactionsNotifySettings#316ce548 settings:ReactionsNotifySettings = ReactionsNotifySettings;
|
||||||
account.getCollectibleEmojiStatuses#2e7b4543 hash:long = account.EmojiStatuses;
|
account.getCollectibleEmojiStatuses#2e7b4543 hash:long = account.EmojiStatuses;
|
||||||
account.addNoPaidMessagesException#6f688aa7 flags:# refund_charged:flags.0?true user_id:InputUser = Bool;
|
account.getPaidMessagesRevenue#19ba4a67 flags:# parent_peer:flags.0?InputPeer user_id:InputUser = account.PaidMessagesRevenue;
|
||||||
account.getPaidMessagesRevenue#f1266f38 user_id:InputUser = account.PaidMessagesRevenue;
|
account.toggleNoPaidMessagesException#fe2eda76 flags:# refund_charged:flags.0?true require_payment:flags.2?true parent_peer:flags.1?InputPeer user_id:InputUser = Bool;
|
||||||
|
|
||||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||||
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
||||||
@ -2390,13 +2400,15 @@ messages.getPaidReactionPrivacy#472455aa = Updates;
|
|||||||
messages.viewSponsoredMessage#269e3643 random_id:bytes = Bool;
|
messages.viewSponsoredMessage#269e3643 random_id:bytes = Bool;
|
||||||
messages.clickSponsoredMessage#8235057e flags:# media:flags.0?true fullscreen:flags.1?true random_id:bytes = Bool;
|
messages.clickSponsoredMessage#8235057e flags:# media:flags.0?true fullscreen:flags.1?true random_id:bytes = Bool;
|
||||||
messages.reportSponsoredMessage#12cbf0c4 random_id:bytes option:bytes = channels.SponsoredMessageReportResult;
|
messages.reportSponsoredMessage#12cbf0c4 random_id:bytes option:bytes = channels.SponsoredMessageReportResult;
|
||||||
messages.getSponsoredMessages#9bd2f439 peer:InputPeer = messages.SponsoredMessages;
|
messages.getSponsoredMessages#3d6ce850 flags:# peer:InputPeer msg_id:flags.0?int = messages.SponsoredMessages;
|
||||||
messages.savePreparedInlineMessage#f21f7f2f flags:# result:InputBotInlineResult user_id:InputUser peer_types:flags.0?Vector<InlineQueryPeerType> = messages.BotPreparedInlineMessage;
|
messages.savePreparedInlineMessage#f21f7f2f flags:# result:InputBotInlineResult user_id:InputUser peer_types:flags.0?Vector<InlineQueryPeerType> = messages.BotPreparedInlineMessage;
|
||||||
messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage;
|
messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage;
|
||||||
messages.searchStickers#29b1c66a flags:# emojis:flags.0?true q:string emoticon:string lang_code:Vector<string> offset:int limit:int hash:long = messages.FoundStickers;
|
messages.searchStickers#29b1c66a flags:# emojis:flags.0?true q:string emoticon:string lang_code:Vector<string> offset:int limit:int hash:long = messages.FoundStickers;
|
||||||
messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector<int> = Bool;
|
messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector<int> = Bool;
|
||||||
messages.getSavedDialogsByID#6f6f9c96 flags:# parent_peer:flags.1?InputPeer ids:Vector<InputPeer> = messages.SavedDialogs;
|
messages.getSavedDialogsByID#6f6f9c96 flags:# parent_peer:flags.1?InputPeer ids:Vector<InputPeer> = messages.SavedDialogs;
|
||||||
messages.readSavedHistory#ba4a3b5b parent_peer:InputPeer peer:InputPeer max_id:int = Bool;
|
messages.readSavedHistory#ba4a3b5b parent_peer:InputPeer peer:InputPeer max_id:int = Bool;
|
||||||
|
messages.toggleTodoCompleted#d3e03124 peer:InputPeer msg_id:int completed:Vector<int> incompleted:Vector<int> = Updates;
|
||||||
|
messages.appendTodoList#21a61057 peer:InputPeer msg_id:int list:Vector<TodoItem> = Updates;
|
||||||
|
|
||||||
updates.getState#edd4882a = updates.State;
|
updates.getState#edd4882a = updates.State;
|
||||||
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
|
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import type {
|
|||||||
ApiMediaFormat,
|
ApiMediaFormat,
|
||||||
ApiMessage,
|
ApiMessage,
|
||||||
ApiMessageEntity,
|
ApiMessageEntity,
|
||||||
|
ApiNewMediaTodo,
|
||||||
ApiNewPoll,
|
ApiNewPoll,
|
||||||
ApiPeer,
|
ApiPeer,
|
||||||
ApiPhoto,
|
ApiPhoto,
|
||||||
@ -730,6 +731,7 @@ export type SendMessageParams = {
|
|||||||
story?: ApiStory | ApiStorySkipped;
|
story?: ApiStory | ApiStorySkipped;
|
||||||
gif?: ApiVideo;
|
gif?: ApiVideo;
|
||||||
poll?: ApiNewPoll;
|
poll?: ApiNewPoll;
|
||||||
|
todo?: ApiNewMediaTodo;
|
||||||
contact?: ApiContact;
|
contact?: ApiContact;
|
||||||
isSilent?: boolean;
|
isSilent?: boolean;
|
||||||
scheduledAt?: number;
|
scheduledAt?: number;
|
||||||
|
|||||||
81
src/types/language.d.ts
vendored
81
src/types/language.d.ts
vendored
@ -50,6 +50,7 @@ export interface LangPair {
|
|||||||
'PremiumPreviewVoiceToTextDescription': undefined;
|
'PremiumPreviewVoiceToTextDescription': undefined;
|
||||||
'PremiumPreviewProfileBadgeDescription': undefined;
|
'PremiumPreviewProfileBadgeDescription': undefined;
|
||||||
'PremiumPreviewDownloadSpeedDescription': undefined;
|
'PremiumPreviewDownloadSpeedDescription': undefined;
|
||||||
|
'PremiumPreviewTodoDescription': undefined;
|
||||||
'PremiumPreviewUploadsDescription': undefined;
|
'PremiumPreviewUploadsDescription': undefined;
|
||||||
'PremiumPreviewAdvancedChatManagementDescription': undefined;
|
'PremiumPreviewAdvancedChatManagementDescription': undefined;
|
||||||
'PremiumPreviewAnimatedProfilesDescription': undefined;
|
'PremiumPreviewAnimatedProfilesDescription': undefined;
|
||||||
@ -1537,6 +1538,27 @@ export interface LangPair {
|
|||||||
'MonoforumComposerPlaceholder': undefined;
|
'MonoforumComposerPlaceholder': undefined;
|
||||||
'ChannelSendMessage': undefined;
|
'ChannelSendMessage': undefined;
|
||||||
'AutomaticTranslation': undefined;
|
'AutomaticTranslation': undefined;
|
||||||
|
'TitleNewToDoList': undefined;
|
||||||
|
'TitleEditToDoList': undefined;
|
||||||
|
'TitleAppendToDoList': undefined;
|
||||||
|
'InputTitle': undefined;
|
||||||
|
'TitleToDoList': undefined;
|
||||||
|
'TitleTask': undefined;
|
||||||
|
'TitleAddTask': undefined;
|
||||||
|
'AllowOthersAddTasks': undefined;
|
||||||
|
'AllowOthersMarkAsDone': undefined;
|
||||||
|
'AriaToDoCancel': undefined;
|
||||||
|
'TitleGroupToDoList': undefined;
|
||||||
|
'TitleYourToDoList': undefined;
|
||||||
|
'ToDoListNewTasks': undefined;
|
||||||
|
'MenuButtonAppendTodoList': undefined;
|
||||||
|
'PremiumMore': undefined;
|
||||||
|
'SubscribeToTelegramPremiumForToggleTask': undefined;
|
||||||
|
'SubscribeToTelegramPremiumForCreateToDo': undefined;
|
||||||
|
'SubscribeToTelegramPremiumForAppendToDo': undefined;
|
||||||
|
'ToDoListErrorChooseTitle': undefined;
|
||||||
|
'ToDoListErrorChooseTasks': undefined;
|
||||||
|
'PremiumPreviewTodo': undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LangPairWithVariables<V = LangVariable> {
|
export interface LangPairWithVariables<V = LangVariable> {
|
||||||
@ -2518,6 +2540,62 @@ export interface LangPairWithVariables<V = LangVariable> {
|
|||||||
'ComposerTitleForwardFrom': {
|
'ComposerTitleForwardFrom': {
|
||||||
'users': V;
|
'users': V;
|
||||||
};
|
};
|
||||||
|
'TitleUserToDoList': {
|
||||||
|
'peer': V;
|
||||||
|
};
|
||||||
|
'DescriptionCompletedToDoTasks': {
|
||||||
|
'number': V;
|
||||||
|
'count': V;
|
||||||
|
};
|
||||||
|
'MessageActionTodoCompletionsAsDone': {
|
||||||
|
'peer': V;
|
||||||
|
'task': V;
|
||||||
|
};
|
||||||
|
'MessageActionTodoCompletionsAsDoneYou': {
|
||||||
|
'task': V;
|
||||||
|
};
|
||||||
|
'MessageActionTodoCompletionsAsDoneMultiple': {
|
||||||
|
'peer': V;
|
||||||
|
'tasks': V;
|
||||||
|
};
|
||||||
|
'MessageActionTodoCompletionsAsDoneMultipleYou': {
|
||||||
|
'tasks': V;
|
||||||
|
};
|
||||||
|
'MessageActionTodoCompletionsAsNotDone': {
|
||||||
|
'peer': V;
|
||||||
|
'task': V;
|
||||||
|
};
|
||||||
|
'MessageActionTodoCompletionsAsNotDoneYou': {
|
||||||
|
'task': V;
|
||||||
|
};
|
||||||
|
'MessageActionTodoCompletionsAsNotDoneMultiple': {
|
||||||
|
'peer': V;
|
||||||
|
'tasks': V;
|
||||||
|
};
|
||||||
|
'MessageActionTodoCompletionsAsNotDoneMultipleYou': {
|
||||||
|
'tasks': V;
|
||||||
|
};
|
||||||
|
'MessageActionAppendTodo': {
|
||||||
|
'peer': V;
|
||||||
|
'task': V;
|
||||||
|
'list': V;
|
||||||
|
};
|
||||||
|
'MessageActionAppendTodoYou': {
|
||||||
|
'task': V;
|
||||||
|
'list': V;
|
||||||
|
};
|
||||||
|
'MessageActionAppendTodoMultiple': {
|
||||||
|
'peer': V;
|
||||||
|
'tasks': V;
|
||||||
|
'list': V;
|
||||||
|
};
|
||||||
|
'MessageActionAppendTodoMultipleYou': {
|
||||||
|
'tasks': V;
|
||||||
|
'list': V;
|
||||||
|
};
|
||||||
|
'HintTodoListTasksCount': {
|
||||||
|
'count': V;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LangPairPlural {
|
export interface LangPairPlural {
|
||||||
@ -2830,6 +2908,9 @@ export interface LangPairPluralWithVariables<V = LangVariable> {
|
|||||||
'GiftAttributeSymbolPlural': {
|
'GiftAttributeSymbolPlural': {
|
||||||
'count': V;
|
'count': V;
|
||||||
};
|
};
|
||||||
|
'MessageActionTodoTaskCount': {
|
||||||
|
'count': V;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export type RegularLangKey = keyof LangPair;
|
export type RegularLangKey = keyof LangPair;
|
||||||
export type RegularLangKeyWithVariables = keyof LangPairWithVariables;
|
export type RegularLangKeyWithVariables = keyof LangPairWithVariables;
|
||||||
|
|||||||
@ -1,3 +1,9 @@
|
|||||||
export default function generateUniqueId() {
|
export default function generateUniqueId() {
|
||||||
return Date.now().toString(36) + Math.random().toString(36).slice(2);
|
return Date.now().toString(36) + Math.random().toString(36).slice(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function generateUniqueNumberId() {
|
||||||
|
const timestamp = Date.now() % 100000000;
|
||||||
|
const random = Math.floor(Math.random() * 1000);
|
||||||
|
return timestamp + random;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user