Chat: Support unread poll votes (#6847)
This commit is contained in:
parent
741c6f532f
commit
86684cd1c1
@ -710,7 +710,7 @@ export function buildThreadReadState(
|
||||
const forumTopic = input instanceof GramJs.ForumTopic ? input : undefined;
|
||||
|
||||
const { unreadReactionsCount } = dialog || monoForumDialog || forumTopic || {};
|
||||
const { unreadMentionsCount } = dialog || forumTopic || {};
|
||||
const { unreadMentionsCount, unreadPollVotesCount } = dialog || forumTopic || {};
|
||||
const { unreadMark } = dialog || monoForumDialog || {};
|
||||
|
||||
return omitUndefined<ThreadReadState>({
|
||||
@ -718,6 +718,7 @@ export function buildThreadReadState(
|
||||
lastReadInboxMessageId: readInboxMaxId,
|
||||
lastReadOutboxMessageId: readOutboxMaxId,
|
||||
unreadReactionsCount,
|
||||
unreadPollVotesCount,
|
||||
unreadMentionsCount,
|
||||
hasUnreadMark: unreadMark,
|
||||
});
|
||||
|
||||
@ -45,6 +45,7 @@ import {
|
||||
MAX_INT_32,
|
||||
MENTION_UNREAD_SLICE,
|
||||
MESSAGE_ID_REQUIRED_ERROR,
|
||||
POLL_UNREAD_SLICE,
|
||||
REACTION_UNREAD_SLICE,
|
||||
SUPPORTED_PHOTO_CONTENT_TYPES,
|
||||
SUPPORTED_VIDEO_CONTENT_TYPES,
|
||||
@ -2298,6 +2299,27 @@ export async function readAllReactions({
|
||||
}
|
||||
}
|
||||
|
||||
export async function readAllPollVotes({
|
||||
chat,
|
||||
threadId,
|
||||
}: {
|
||||
chat: ApiChat;
|
||||
threadId?: ThreadId;
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.messages.ReadPollVotes({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
topMsgId: threadId ? Number(threadId) : undefined,
|
||||
}));
|
||||
|
||||
if (!result) return;
|
||||
|
||||
processAffectedHistory(chat, result);
|
||||
|
||||
if (result.offset) {
|
||||
await readAllPollVotes({ chat, threadId });
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchUnreadMentions({
|
||||
chat, threadId, offsetId, addOffset, maxId, minId,
|
||||
}: {
|
||||
@ -2376,6 +2398,45 @@ export async function fetchUnreadReactions({
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchUnreadPollVotes({
|
||||
chat, threadId, offsetId, addOffset, maxId, minId,
|
||||
}: {
|
||||
chat: ApiChat;
|
||||
threadId?: ThreadId;
|
||||
offsetId?: number;
|
||||
addOffset?: number;
|
||||
maxId?: number;
|
||||
minId?: number;
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.messages.GetUnreadPollVotes({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
topMsgId: threadId ? Number(threadId) : undefined,
|
||||
limit: POLL_UNREAD_SLICE,
|
||||
offsetId: offsetId ?? DEFAULT_PRIMITIVES.INT,
|
||||
addOffset: addOffset ?? DEFAULT_PRIMITIVES.INT,
|
||||
maxId: maxId ?? DEFAULT_PRIMITIVES.INT,
|
||||
minId: minId ?? DEFAULT_PRIMITIVES.INT,
|
||||
}));
|
||||
|
||||
if (
|
||||
!result
|
||||
|| result instanceof GramJs.messages.MessagesNotModified
|
||||
|| !result.messages
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const totalCount = 'count' in result ? result.count : result.messages.length;
|
||||
const messages = result.messages.map(buildApiMessage).filter(Boolean);
|
||||
const topics = result.topics.map(buildApiTopicWithState).filter(Boolean);
|
||||
|
||||
return {
|
||||
totalCount,
|
||||
messages,
|
||||
topics,
|
||||
};
|
||||
}
|
||||
|
||||
export async function transcribeAudio({
|
||||
chat, messageId,
|
||||
}: {
|
||||
|
||||
1
src/assets/font-icons/poll-badge.svg
Normal file
1
src/assets/font-icons/poll-badge.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 32 32"><path d="M16 28a3 3 0 0 1-3-3V7a3 3 0 1 1 6 0v18a3 3 0 0 1-3 3M25.325 28a3 3 0 0 1-3-3v-7.318a3 3 0 0 1 6 0V25a3 3 0 0 1-3 3M6.675 28a3 3 0 0 1-3-3V13.713a3 3 0 1 1 6 0V25a3 3 0 0 1-3 3"/></svg>
|
||||
|
After Width: | Height: | Size: 276 B |
@ -109,6 +109,7 @@
|
||||
"AnonymousPoll" = "Anonymous Poll";
|
||||
"AccDescrReactionMentionDown" = "Go to next unread reactions";
|
||||
"AccDescrMentionDown" = "Go to next mention";
|
||||
"AccDescrPollVoteDown" = "Go to next unread poll vote";
|
||||
"AccDescrPageDown" = "Go to bottom";
|
||||
"ChannelPrivate" = "Private Channel";
|
||||
"ChannelPrivateInfo" = "Private channels can only be joined via invite link.";
|
||||
|
||||
@ -90,6 +90,10 @@
|
||||
background-color: #ed504f;
|
||||
}
|
||||
|
||||
.poll {
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.round {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
|
||||
@ -69,6 +69,7 @@ const ChatBadge = ({
|
||||
|
||||
const {
|
||||
unreadMentionsCount: stateUnreadMentionsCount = 0,
|
||||
unreadPollVotesCount: stateUnreadPollVotesCount = 0,
|
||||
unreadReactionsCount: stateUnreadReactionsCount = 0,
|
||||
unreadCount: stateUnreadCount = 0,
|
||||
hasUnreadMark,
|
||||
@ -95,12 +96,29 @@ const ChatBadge = ({
|
||||
const topicsWithUnreadMentionsIds = useMemo(() => (
|
||||
isForum && listedTopicIds ? listedTopicIds.filter((tId) => topicsReadStates[tId]?.unreadMentionsCount) : undefined
|
||||
), [listedTopicIds, isForum, topicsReadStates]);
|
||||
const topicsWithUnreadPollVotesIds = useMemo(() => (
|
||||
isForum && listedTopicIds ? listedTopicIds.filter((tId) => topicsReadStates[tId]?.unreadPollVotesCount) : undefined
|
||||
), [listedTopicIds, isForum, topicsReadStates]);
|
||||
const topicsWithUnreadReactionsIds = useMemo(() => (
|
||||
isForum && listedTopicIds ? listedTopicIds.filter((tId) => topicsReadStates[tId]?.unreadReactionsCount) : undefined
|
||||
), [listedTopicIds, isForum, topicsReadStates]);
|
||||
const topicsWithStatefulUnreadIds = useMemo(() => {
|
||||
if (!isForum) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const allTopicIds = [
|
||||
...(topicsWithUnreadIds || []),
|
||||
...(topicsWithUnreadPollVotesIds || []),
|
||||
...(topicsWithUnreadReactionsIds || []),
|
||||
];
|
||||
|
||||
return allTopicIds.length ? [...new Set(allTopicIds)] : [];
|
||||
}, [isForum, topicsWithUnreadIds, topicsWithUnreadPollVotesIds, topicsWithUnreadReactionsIds]);
|
||||
|
||||
const unreadCount = isForum ? topicsWithUnreadIds?.length : stateUnreadCount;
|
||||
const unreadMentionsCount = isForum ? topicsWithUnreadMentionsIds?.length : stateUnreadMentionsCount;
|
||||
const unreadPollVotesCount = isForum ? topicsWithUnreadPollVotesIds?.length : stateUnreadPollVotesCount;
|
||||
const unreadReactionsCount = isForum ? topicsWithUnreadReactionsIds?.length : stateUnreadReactionsCount;
|
||||
|
||||
const shouldBeUnMuted = useMemo(() => {
|
||||
@ -108,17 +126,21 @@ const ChatBadge = ({
|
||||
return !isMuted || topic?.notifySettings.mutedUntil === 0;
|
||||
}
|
||||
|
||||
if (isMuted) {
|
||||
return topicsWithUnreadIds?.some((tId) => topicsById?.[tId]?.notifySettings.mutedUntil === 0);
|
||||
if (!topicsWithStatefulUnreadIds?.length) {
|
||||
return !isMuted;
|
||||
}
|
||||
|
||||
const isEveryUnreadMuted = topicsWithUnreadIds?.every((tId) => {
|
||||
if (isMuted) {
|
||||
return topicsWithStatefulUnreadIds.some((tId) => topicsById?.[tId]?.notifySettings.mutedUntil === 0);
|
||||
}
|
||||
|
||||
const isEveryUnreadMuted = topicsWithStatefulUnreadIds.every((tId) => {
|
||||
const mutedUntil = topicsById?.[tId]?.notifySettings.mutedUntil;
|
||||
return mutedUntil && mutedUntil > getServerTime();
|
||||
});
|
||||
|
||||
return !isEveryUnreadMuted;
|
||||
}, [isForum, isMuted, topicsWithUnreadIds, topicsById, topic?.notifySettings.mutedUntil]);
|
||||
}, [isForum, isMuted, topicsById, topic?.notifySettings.mutedUntil, topicsWithStatefulUnreadIds]);
|
||||
|
||||
const isUnread = Boolean((unreadCount || hasUnreadMark) && !isSavedDialog);
|
||||
|
||||
@ -127,7 +149,7 @@ const ChatBadge = ({
|
||||
[forceHidden],
|
||||
);
|
||||
const isShown = !resolvedForceHidden && Boolean(
|
||||
unreadCount || unreadMentionsCount || hasUnreadMark || isPinned || unreadReactionsCount
|
||||
unreadCount || unreadMentionsCount || unreadPollVotesCount || hasUnreadMark || isPinned || unreadReactionsCount
|
||||
|| isTopicUnopened || hasMiniApp,
|
||||
);
|
||||
|
||||
@ -152,6 +174,12 @@ const ChatBadge = ({
|
||||
</div>
|
||||
);
|
||||
|
||||
const unreadPollVotesElement = unreadPollVotesCount && (
|
||||
<div className={buildClassName(statefulClassName, styles.poll, styles.round)}>
|
||||
<Icon name="poll-badge" />
|
||||
</div>
|
||||
);
|
||||
|
||||
const unreadMentionsElement = unreadMentionsCount && (
|
||||
<div className={buildClassName(baseClassName, styles.mention, styles.round)}>
|
||||
<Icon name="mention" />
|
||||
@ -187,10 +215,16 @@ const ChatBadge = ({
|
||||
);
|
||||
|
||||
const visiblePinnedElement = !unreadCountElement && !unreadMentionsElement && !unreadReactionsElement
|
||||
&& !unreadPollVotesElement
|
||||
&& pinnedElement;
|
||||
|
||||
const elements = [
|
||||
unopenedTopicElement, unreadReactionsElement, unreadMentionsElement, unreadCountElement, visiblePinnedElement,
|
||||
unopenedTopicElement,
|
||||
unreadPollVotesElement,
|
||||
unreadReactionsElement,
|
||||
unreadMentionsElement,
|
||||
unreadCountElement,
|
||||
visiblePinnedElement,
|
||||
].filter(Boolean);
|
||||
|
||||
if (isSavedDialog) return pinnedElement;
|
||||
@ -204,7 +238,11 @@ const ChatBadge = ({
|
||||
|
||||
if (shouldShowOnlyMostImportant) {
|
||||
const importanceOrderedElements = [
|
||||
unreadMentionsElement, unreadCountElement, unreadReactionsElement, pinnedElement,
|
||||
unreadPollVotesElement,
|
||||
unreadReactionsElement,
|
||||
unreadMentionsElement,
|
||||
unreadCountElement,
|
||||
pinnedElement,
|
||||
].filter(Boolean);
|
||||
return importanceOrderedElements[0];
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
.root {
|
||||
--base-bottom-pos: 5.3125rem;
|
||||
--translate-y: 4.5rem;
|
||||
--unread-count-button-offset: 3.5rem;
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
@ -14,23 +15,6 @@
|
||||
|
||||
transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s ease;
|
||||
|
||||
.hidden {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.reactions {
|
||||
transition: 0.2s ease opacity, 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) transform;
|
||||
}
|
||||
|
||||
.transform-down {
|
||||
transform: translateY(3.5rem);
|
||||
}
|
||||
|
||||
.unread {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:global(body.no-page-transitions) & {
|
||||
--translate-y: 0 !important;
|
||||
|
||||
@ -52,7 +36,7 @@
|
||||
}
|
||||
|
||||
&.hide-scroll-down {
|
||||
--translate-y: 3.5rem;
|
||||
--translate-y: var(--unread-count-button-offset);
|
||||
|
||||
.unread {
|
||||
pointer-events: none;
|
||||
@ -75,3 +59,24 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.unreadCountButton {
|
||||
transition: opacity 0.2s ease, transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.transformDown {
|
||||
transform: translateY(var(--unread-count-button-offset));
|
||||
}
|
||||
|
||||
.transformDownDouble {
|
||||
transform: translateY(calc(var(--unread-count-button-offset) * 2));
|
||||
}
|
||||
|
||||
.unread {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import { memo, useEffect, useRef } from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { MessageListType, ThreadId, ThreadReadState } from '../../types';
|
||||
import type { IconName } from '../../types/icons';
|
||||
|
||||
import { selectChat, selectCurrentMessageList, selectCurrentMiddleSearch } from '../../global/selectors';
|
||||
import { selectThreadReadState } from '../../global/selectors/threads';
|
||||
@ -27,6 +28,16 @@ type StateProps = {
|
||||
shouldShowCount?: boolean;
|
||||
};
|
||||
|
||||
type UnreadCountButton = {
|
||||
icon: IconName;
|
||||
ariaLabelLang: string;
|
||||
unreadCount?: number;
|
||||
isHidden: boolean;
|
||||
hiddenUnreadCountButtonsBelow: number;
|
||||
onClick: VoidFunction;
|
||||
onReadAll: VoidFunction;
|
||||
};
|
||||
|
||||
const FloatingActionButtons = ({
|
||||
withScrollDown,
|
||||
canPost,
|
||||
@ -38,19 +49,40 @@ const FloatingActionButtons = ({
|
||||
shouldShowCount,
|
||||
}: OwnProps & StateProps) => {
|
||||
const {
|
||||
focusNextReply, focusNextReaction, focusNextMention, loadUnreadReactions,
|
||||
readAllMentions, readAllReactions, loadUnreadMentions, scrollMessageListToBottom,
|
||||
focusNextPollVote,
|
||||
focusNextReply,
|
||||
focusNextReaction,
|
||||
focusNextMention,
|
||||
loadUnreadPollVotes,
|
||||
loadUnreadReactions,
|
||||
readAllMentions,
|
||||
readAllPollVotes,
|
||||
readAllReactions,
|
||||
loadUnreadMentions,
|
||||
scrollMessageListToBottom,
|
||||
} = getActions();
|
||||
|
||||
const elementRef = useRef<HTMLDivElement>();
|
||||
|
||||
const {
|
||||
unreadReactionsCount, unreadMentionsCount, unreadCount, unreadReactions, unreadMentions,
|
||||
unreadPollVotesCount,
|
||||
unreadReactionsCount,
|
||||
unreadMentionsCount,
|
||||
unreadCount,
|
||||
unreadPollVotes,
|
||||
unreadReactions,
|
||||
unreadMentions,
|
||||
} = (shouldShowCount && threadReadState) || {};
|
||||
|
||||
const hasUnreadPollVotes = Boolean(unreadPollVotesCount);
|
||||
const hasUnreadReactions = Boolean(unreadReactionsCount);
|
||||
const hasUnreadMentions = Boolean(unreadMentionsCount);
|
||||
|
||||
const handleReadAllPollVotes = useLastCallback(() => {
|
||||
if (!chatId) return;
|
||||
readAllPollVotes({ chatId, threadId });
|
||||
});
|
||||
|
||||
const handleReadAllReactions = useLastCallback(() => {
|
||||
if (!chatId) return;
|
||||
readAllReactions({ chatId, threadId });
|
||||
@ -61,6 +93,12 @@ const FloatingActionButtons = ({
|
||||
readAllMentions({ chatId, threadId });
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (hasUnreadPollVotes && chatId && !unreadPollVotes?.length) {
|
||||
loadUnreadPollVotes({ chatId, threadId });
|
||||
}
|
||||
}, [chatId, threadId, hasUnreadPollVotes, unreadPollVotes?.length]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasUnreadReactions && chatId && !unreadReactions?.length) {
|
||||
loadUnreadReactions({ chatId, threadId });
|
||||
@ -90,42 +128,87 @@ const FloatingActionButtons = ({
|
||||
focusNextReaction({ chatId, threadId });
|
||||
});
|
||||
|
||||
const handleFocusNextPollVote = useLastCallback(() => {
|
||||
if (!chatId) return;
|
||||
focusNextPollVote({ chatId, threadId });
|
||||
});
|
||||
|
||||
const handleFocusNextMention = useLastCallback(() => {
|
||||
if (!chatId) return;
|
||||
focusNextMention({ chatId, threadId });
|
||||
});
|
||||
|
||||
const unreadCountButtonsConfig = [
|
||||
{
|
||||
icon: 'poll',
|
||||
ariaLabelLang: 'AccDescrPollVoteDown',
|
||||
unreadCount: unreadPollVotesCount,
|
||||
isHidden: !hasUnreadPollVotes,
|
||||
onClick: handleFocusNextPollVote,
|
||||
onReadAll: handleReadAllPollVotes,
|
||||
},
|
||||
{
|
||||
icon: 'heart-outline',
|
||||
ariaLabelLang: 'AccDescrReactionMentionDown',
|
||||
unreadCount: unreadReactionsCount,
|
||||
isHidden: !hasUnreadReactions,
|
||||
onClick: handleFocusNextReaction,
|
||||
onReadAll: handleReadAllReactions,
|
||||
},
|
||||
{
|
||||
icon: 'mention',
|
||||
ariaLabelLang: 'AccDescrMentionDown',
|
||||
unreadCount: unreadMentionsCount,
|
||||
isHidden: !hasUnreadMentions,
|
||||
onClick: handleFocusNextMention,
|
||||
onReadAll: handleReadAllMentions,
|
||||
},
|
||||
] satisfies Omit<UnreadCountButton, 'hiddenUnreadCountButtonsBelow'>[];
|
||||
|
||||
let hiddenUnreadCountButtonsBelow = 0;
|
||||
const unreadCountButtons = unreadCountButtonsConfig.reduceRight<UnreadCountButton[]>((result, button) => {
|
||||
result.unshift({
|
||||
...button,
|
||||
hiddenUnreadCountButtonsBelow,
|
||||
});
|
||||
|
||||
if (button.isHidden) {
|
||||
hiddenUnreadCountButtonsBelow++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}, []);
|
||||
|
||||
const hasUnreadCountButtons = unreadCountButtons.some((button) => !button.isHidden);
|
||||
|
||||
const buildUnreadCountButtonClassName = (button: UnreadCountButton) => buildClassName(
|
||||
styles.unreadCountButton,
|
||||
button.isHidden && styles.hidden,
|
||||
button.hiddenUnreadCountButtonsBelow === 1 && styles.transformDown,
|
||||
button.hiddenUnreadCountButtonsBelow === 2 && styles.transformDownDouble,
|
||||
);
|
||||
|
||||
const fabClassName = buildClassName(
|
||||
styles.root,
|
||||
(withScrollDown || hasUnreadReactions || hasUnreadMentions) && styles.revealed,
|
||||
(hasUnreadReactions || hasUnreadMentions) && !withScrollDown && styles.hideScrollDown,
|
||||
(withScrollDown || hasUnreadCountButtons) && styles.revealed,
|
||||
hasUnreadCountButtons && !withScrollDown && styles.hideScrollDown,
|
||||
!canPost && styles.noComposer,
|
||||
!withExtraShift && styles.noExtraShift,
|
||||
);
|
||||
|
||||
return (
|
||||
<div ref={elementRef} className={fabClassName}>
|
||||
<ScrollDownButton
|
||||
icon="heart-outline"
|
||||
ariaLabelLang="AccDescrReactionMentionDown"
|
||||
onClick={handleFocusNextReaction}
|
||||
onReadAll={handleReadAllReactions}
|
||||
unreadCount={unreadReactionsCount}
|
||||
className={buildClassName(
|
||||
styles.reactions,
|
||||
!hasUnreadReactions && styles.hidden,
|
||||
!hasUnreadMentions && styles.transformDown,
|
||||
)}
|
||||
/>
|
||||
|
||||
<ScrollDownButton
|
||||
icon="mention"
|
||||
ariaLabelLang="AccDescrMentionDown"
|
||||
onClick={handleFocusNextMention}
|
||||
onReadAll={handleReadAllMentions}
|
||||
unreadCount={unreadMentionsCount}
|
||||
className={!hasUnreadMentions && styles.hidden}
|
||||
/>
|
||||
{unreadCountButtons.map((button) => (
|
||||
<ScrollDownButton
|
||||
key={button.icon}
|
||||
icon={button.icon}
|
||||
ariaLabelLang={button.ariaLabelLang}
|
||||
onClick={button.onClick}
|
||||
onReadAll={button.onReadAll}
|
||||
unreadCount={button.unreadCount}
|
||||
className={buildUnreadCountButtonClassName(button)}
|
||||
/>
|
||||
))}
|
||||
|
||||
<ScrollDownButton
|
||||
icon="arrow-down"
|
||||
@ -142,7 +225,13 @@ export default memo(withGlobal<OwnProps>(
|
||||
(global): Complete<StateProps> => {
|
||||
const currentMessageList = selectCurrentMessageList(global);
|
||||
if (!currentMessageList) {
|
||||
return {} as Complete<StateProps>;
|
||||
return {
|
||||
chatId: undefined,
|
||||
messageListType: undefined,
|
||||
shouldShowCount: undefined,
|
||||
threadId: undefined,
|
||||
threadReadState: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const { chatId, threadId, type: messageListType } = currentMessageList;
|
||||
|
||||
@ -33,7 +33,7 @@ export default function useMessageObservers({
|
||||
onIntersectPinnedMessage: OnIntersectPinnedMessage | undefined;
|
||||
}) {
|
||||
const {
|
||||
markMessageListRead, markMentionsRead, animateUnreadReaction,
|
||||
markMessageListRead, markMentionsRead, markPollVotesRead, animateUnreadReaction,
|
||||
scheduleForViewsIncrement,
|
||||
} = getActions();
|
||||
|
||||
@ -61,6 +61,7 @@ export default function useMessageObservers({
|
||||
|
||||
let maxId = 0;
|
||||
const mentionIds: number[] = [];
|
||||
const pollVoteIds: number[] = [];
|
||||
const reactionIds: number[] = [];
|
||||
const scheduledToUpdateViews: number[] = [];
|
||||
|
||||
@ -91,6 +92,10 @@ export default function useMessageObservers({
|
||||
mentionIds.push(messageId);
|
||||
}
|
||||
|
||||
if (dataset.hasUnreadPollVote) {
|
||||
pollVoteIds.push(messageId);
|
||||
}
|
||||
|
||||
if (dataset.hasUnreadReaction) {
|
||||
reactionIds.push(messageId);
|
||||
}
|
||||
@ -113,6 +118,10 @@ export default function useMessageObservers({
|
||||
markMentionsRead({ chatId, messageIds: mentionIds });
|
||||
}
|
||||
|
||||
if (pollVoteIds.length) {
|
||||
markPollVotesRead({ chatId, messageIds: pollVoteIds });
|
||||
}
|
||||
|
||||
if (scheduledToUpdateViews.length) {
|
||||
scheduleForViewsIncrement({ chatId, ids: scheduledToUpdateViews });
|
||||
}
|
||||
|
||||
@ -102,6 +102,7 @@ type StateProps = {
|
||||
isCurrentUserPremium?: boolean;
|
||||
isInSelectMode?: boolean;
|
||||
hasUnreadReaction?: boolean;
|
||||
hasUnreadPollVote?: boolean;
|
||||
isResizingContainer?: boolean;
|
||||
scrollTargetPosition?: ScrollTargetPosition;
|
||||
isAccountFrozen?: boolean;
|
||||
@ -140,6 +141,7 @@ const ActionMessage = ({
|
||||
isCurrentUserPremium,
|
||||
isInSelectMode,
|
||||
hasUnreadReaction,
|
||||
hasUnreadPollVote,
|
||||
isResizingContainer,
|
||||
scrollTargetPosition,
|
||||
isAccountFrozen,
|
||||
@ -161,6 +163,7 @@ const ActionMessage = ({
|
||||
toggleChannelRecommendations,
|
||||
animateUnreadReaction,
|
||||
markMentionsRead,
|
||||
markPollVotesRead,
|
||||
focusMessage,
|
||||
openGiftOfferAcceptModal,
|
||||
declineStarGiftOffer,
|
||||
@ -335,10 +338,22 @@ const ActionMessage = ({
|
||||
animateUnreadReaction({ chatId, messageIds: [id] });
|
||||
}
|
||||
|
||||
if (hasUnreadPollVote) {
|
||||
markPollVotesRead({ chatId, messageIds: [id] });
|
||||
}
|
||||
|
||||
if (message.hasUnreadMention) {
|
||||
markMentionsRead({ chatId, messageIds: [id] });
|
||||
}
|
||||
}, [hasUnreadReaction, chatId, id, animateUnreadReaction, message.hasUnreadMention]);
|
||||
}, [
|
||||
hasUnreadReaction,
|
||||
hasUnreadPollVote,
|
||||
chatId,
|
||||
id,
|
||||
animateUnreadReaction,
|
||||
markPollVotesRead,
|
||||
message.hasUnreadMention,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (action.type !== 'giftPremium') return;
|
||||
@ -619,6 +634,7 @@ const ActionMessage = ({
|
||||
data-message-id={message.id}
|
||||
data-is-pinned={message.isPinned || undefined}
|
||||
data-has-unread-mention={message.hasUnreadMention || undefined}
|
||||
data-has-unread-poll-vote={hasUnreadPollVote || undefined}
|
||||
data-has-unread-reaction={hasUnreadReaction || undefined}
|
||||
onMouseDown={handleMouseDown}
|
||||
onContextMenu={handleContextMenu}
|
||||
@ -737,6 +753,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
const readState = selectThreadReadState(global, message.chatId, threadId);
|
||||
const hasUnreadReaction = readState?.unreadReactions?.includes(message.id);
|
||||
const hasUnreadPollVote = readState?.unreadPollVotes?.includes(message.id);
|
||||
const isAccountFrozen = selectIsCurrentUserFrozen(global);
|
||||
|
||||
return {
|
||||
@ -751,6 +768,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
isInSelectMode: selectIsInSelectMode(global),
|
||||
actionMessageBg: selectActionMessageBg(global),
|
||||
hasUnreadReaction,
|
||||
hasUnreadPollVote,
|
||||
isResizingContainer,
|
||||
scrollTargetPosition,
|
||||
isAccountFrozen,
|
||||
|
||||
@ -301,6 +301,7 @@ type StateProps = {
|
||||
defaultReaction?: ApiReaction;
|
||||
activeEmojiInteractions?: ActiveEmojiInteraction[];
|
||||
hasUnreadReaction?: boolean;
|
||||
hasUnreadPollVote?: boolean;
|
||||
isTranscribing?: boolean;
|
||||
transcribedText?: string;
|
||||
isPremium: boolean;
|
||||
@ -431,6 +432,7 @@ const Message = ({
|
||||
autoLoadFileMaxSizeMb,
|
||||
repliesThreadInfo,
|
||||
hasUnreadReaction,
|
||||
hasUnreadPollVote,
|
||||
memoFirstUnreadIdRef,
|
||||
senderChatMember,
|
||||
messageTopic,
|
||||
@ -478,6 +480,7 @@ const Message = ({
|
||||
animateUnreadReaction,
|
||||
focusMessage,
|
||||
markMentionsRead,
|
||||
markPollVotesRead,
|
||||
openThread,
|
||||
summarizeMessage,
|
||||
} = getActions();
|
||||
@ -963,6 +966,10 @@ const Message = ({
|
||||
animateUnreadReaction({ chatId, messageIds: [messageId] });
|
||||
}
|
||||
|
||||
if (hasUnreadPollVote) {
|
||||
markPollVotesRead({ chatId, messageIds: [messageId] });
|
||||
}
|
||||
|
||||
let unreadMentionIds: number[] = [];
|
||||
if (message.hasUnreadMention) {
|
||||
unreadMentionIds = [messageId];
|
||||
@ -975,7 +982,16 @@ const Message = ({
|
||||
if (unreadMentionIds.length) {
|
||||
markMentionsRead({ chatId, messageIds: unreadMentionIds });
|
||||
}
|
||||
}, [hasUnreadReaction, album, chatId, messageId, animateUnreadReaction, message.hasUnreadMention]);
|
||||
}, [
|
||||
hasUnreadReaction,
|
||||
hasUnreadPollVote,
|
||||
album,
|
||||
chatId,
|
||||
messageId,
|
||||
animateUnreadReaction,
|
||||
markPollVotesRead,
|
||||
message.hasUnreadMention,
|
||||
]);
|
||||
|
||||
const albumLayout = useMemo(() => {
|
||||
return isAlbum
|
||||
@ -1848,6 +1864,7 @@ const Message = ({
|
||||
data-last-message-id={album ? album.messages[album.messages.length - 1].id : undefined}
|
||||
data-album-main-id={album ? album.mainMessage.id : undefined}
|
||||
data-has-unread-mention={message.hasUnreadMention || undefined}
|
||||
data-has-unread-poll-vote={hasUnreadPollVote || undefined}
|
||||
data-has-unread-reaction={hasUnreadReaction || undefined}
|
||||
data-is-pinned={isPinned || undefined}
|
||||
data-should-update-views={message.viewsCount !== undefined}
|
||||
@ -2123,6 +2140,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
const readState = selectThreadReadState(global, chatId, threadId);
|
||||
const hasUnreadReaction = readState?.unreadReactions?.includes(message.id);
|
||||
const hasUnreadPollVote = readState?.unreadPollVotes?.includes(message.id);
|
||||
|
||||
const hasTopicChip = threadId === MAIN_THREAD_ID && chat?.isForum && !chat.isBotForum && isFirstInGroup;
|
||||
const messageTopic = selectTopicFromMessage(global, message);
|
||||
@ -2220,6 +2238,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
hasActiveReactions,
|
||||
activeEmojiInteractions,
|
||||
hasUnreadReaction,
|
||||
hasUnreadPollVote,
|
||||
isTranscribing: transcriptionId !== undefined && global.transcriptions[transcriptionId]?.isPending,
|
||||
transcribedText: transcriptionId !== undefined ? global.transcriptions[transcriptionId]?.text : undefined,
|
||||
isPremium,
|
||||
|
||||
@ -116,6 +116,7 @@ export const GLOBAL_SUGGESTED_CHANNELS_ID = 'global';
|
||||
// https://github.com/DrKLO/Telegram/blob/51e9947527/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java#L7781
|
||||
export const REACTION_UNREAD_SLICE = 100;
|
||||
export const MENTION_UNREAD_SLICE = 100;
|
||||
export const POLL_UNREAD_SLICE = 100;
|
||||
export const TOPICS_SLICE = 20;
|
||||
export const TOPICS_SLICE_SECOND_LOAD = 500;
|
||||
|
||||
|
||||
@ -1461,6 +1461,7 @@ addActionHandler('markChatMessagesRead', async (global, actions, payload): Promi
|
||||
await callApi('markMessageListRead', { chat, threadId: MAIN_THREAD_ID });
|
||||
actions.readAllMentions({ chatId: id });
|
||||
actions.readAllReactions({ chatId: id });
|
||||
actions.readAllPollVotes({ chatId: id });
|
||||
if (chatReadState?.hasUnreadMark) {
|
||||
actions.markChatRead({ id });
|
||||
}
|
||||
@ -1487,7 +1488,13 @@ addActionHandler('markChatMessagesRead', async (global, actions, payload): Promi
|
||||
global = updateTopicWithState(global, id, topicWithState);
|
||||
|
||||
const { readState } = topicWithState;
|
||||
if (readState && !readState.unreadCount && !readState.unreadMentionsCount && !readState.unreadReactionsCount) {
|
||||
if (
|
||||
readState
|
||||
&& !readState.unreadCount
|
||||
&& !readState.unreadMentionsCount
|
||||
&& !readState.unreadReactionsCount
|
||||
&& !readState.unreadPollVotesCount
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1530,6 +1537,7 @@ addActionHandler('markTopicRead', (global, actions, payload): ActionReturnType =
|
||||
});
|
||||
actions.readAllMentions({ chatId, threadId: topicId });
|
||||
actions.readAllReactions({ chatId, threadId: topicId });
|
||||
actions.readAllPollVotes({ chatId, threadId: topicId });
|
||||
|
||||
global = getGlobal();
|
||||
global = replaceThreadReadStateParam(global, chatId, topicId, 'lastReadInboxMessageId', lastTopicMessageId);
|
||||
|
||||
@ -81,12 +81,12 @@ import {
|
||||
} from '../../index';
|
||||
import {
|
||||
addChatMessagesById,
|
||||
addUnreadMentions,
|
||||
clearMessageSummary,
|
||||
deleteSponsoredMessage,
|
||||
removeOutlyingList,
|
||||
removeRequestedMessageTranslation,
|
||||
removeUnreadMentions,
|
||||
removeUnreadPollVotes,
|
||||
replaceSettings,
|
||||
replaceUserStatuses,
|
||||
safeReplacePinnedIds,
|
||||
@ -106,6 +106,7 @@ import {
|
||||
updateScheduledMessages,
|
||||
updateSponsoredMessage,
|
||||
updateTopicWithState,
|
||||
updateUnreadCounters,
|
||||
updateUploadByMessageKey,
|
||||
updateUserFullInfo,
|
||||
} from '../../reducers';
|
||||
@ -172,7 +173,6 @@ import {
|
||||
selectThreadReadState,
|
||||
} from '../../selectors/threads';
|
||||
import { deleteMessages, updateWithLocalMedia } from '../apiUpdaters/messages';
|
||||
|
||||
const AUTOLOGIN_TOKEN_KEY = 'autologin_token';
|
||||
|
||||
const uploadProgressCallbacks = new Map<MessageKey, ApiOnProgress>();
|
||||
@ -2287,19 +2287,45 @@ addActionHandler('loadUnreadMentions', async (global, actions, payload): Promise
|
||||
|
||||
const { messages, topics, totalCount } = result;
|
||||
|
||||
const byId = buildCollectionByKey(messages, 'id');
|
||||
const ids = Object.keys(byId).map(Number);
|
||||
|
||||
global = getGlobal();
|
||||
global = addChatMessagesById(global, chat.id, byId);
|
||||
topics.forEach((topicState) => {
|
||||
global = updateTopicWithState(global, chat.id, topicState);
|
||||
});
|
||||
global = addUnreadMentions({
|
||||
global = updateUnreadCounters({
|
||||
global,
|
||||
chatId,
|
||||
ids,
|
||||
threadId,
|
||||
messages,
|
||||
topics,
|
||||
totalCount,
|
||||
unreadCountKey: 'unreadMentionsCount',
|
||||
});
|
||||
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('loadUnreadPollVotes', async (global, actions, payload): Promise<void> => {
|
||||
const { chatId, threadId = MAIN_THREAD_ID, offsetId } = payload;
|
||||
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) return;
|
||||
|
||||
const result = await callApi('fetchUnreadPollVotes', {
|
||||
chat,
|
||||
threadId: threadId !== MAIN_THREAD_ID ? threadId : undefined,
|
||||
offsetId,
|
||||
});
|
||||
|
||||
if (!result) return;
|
||||
|
||||
const { messages, topics, totalCount } = result;
|
||||
|
||||
global = getGlobal();
|
||||
global = updateUnreadCounters({
|
||||
global,
|
||||
chatId,
|
||||
threadId,
|
||||
messages,
|
||||
topics,
|
||||
totalCount,
|
||||
unreadCountKey: 'unreadPollVotesCount',
|
||||
});
|
||||
|
||||
setGlobal(global);
|
||||
@ -2391,6 +2417,21 @@ addActionHandler('markMentionsRead', (global, actions, payload): ActionReturnTyp
|
||||
actions.markMessagesRead({ chatId, messageIds });
|
||||
});
|
||||
|
||||
addActionHandler('markPollVotesRead', (global, actions, payload): ActionReturnType => {
|
||||
const { chatId, messageIds } = payload;
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) return;
|
||||
|
||||
global = removeUnreadPollVotes({
|
||||
global,
|
||||
chatId,
|
||||
ids: messageIds,
|
||||
});
|
||||
setGlobal(global);
|
||||
|
||||
actions.markMessagesRead({ chatId, messageIds });
|
||||
});
|
||||
|
||||
addActionHandler('focusNextMention', async (global, actions, payload): Promise<void> => {
|
||||
const { chatId, threadId = MAIN_THREAD_ID, tabId = getCurrentTabId() } = payload;
|
||||
|
||||
@ -2407,6 +2448,28 @@ addActionHandler('focusNextMention', async (global, actions, payload): Promise<v
|
||||
actions.focusMessage({ chatId, messageId: readState.unreadMentions[0], tabId });
|
||||
});
|
||||
|
||||
addActionHandler('focusNextPollVote', async (global, actions, payload): Promise<void> => {
|
||||
const { chatId, threadId = MAIN_THREAD_ID, tabId = getCurrentTabId() } = payload;
|
||||
|
||||
let readState = selectThreadReadState(global, chatId, threadId);
|
||||
|
||||
if (!readState?.unreadPollVotes?.length) {
|
||||
await getPromiseActions().loadUnreadPollVotes({ chatId, threadId });
|
||||
|
||||
global = getGlobal();
|
||||
readState = selectThreadReadState(global, chatId, threadId);
|
||||
if (!readState?.unreadPollVotes?.length) return;
|
||||
}
|
||||
|
||||
actions.focusMessage({
|
||||
chatId,
|
||||
threadId,
|
||||
messageId: readState.unreadPollVotes[0],
|
||||
tabId,
|
||||
scrollTargetPosition: 'end',
|
||||
});
|
||||
});
|
||||
|
||||
addActionHandler('readAllMentions', (global, actions, payload): ActionReturnType => {
|
||||
const { chatId, threadId = MAIN_THREAD_ID } = payload;
|
||||
|
||||
@ -2422,6 +2485,21 @@ addActionHandler('readAllMentions', (global, actions, payload): ActionReturnType
|
||||
return global;
|
||||
});
|
||||
|
||||
addActionHandler('readAllPollVotes', (global, actions, payload): ActionReturnType => {
|
||||
const { chatId, threadId = MAIN_THREAD_ID } = payload;
|
||||
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) return undefined;
|
||||
|
||||
callApi('readAllPollVotes', { chat, threadId: threadId !== MAIN_THREAD_ID ? threadId : undefined });
|
||||
|
||||
global = updateThreadReadState(global, chatId, threadId, {
|
||||
unreadPollVotesCount: 0,
|
||||
unreadPollVotes: undefined,
|
||||
});
|
||||
return global;
|
||||
});
|
||||
|
||||
addActionHandler('openUrl', (global, actions, payload): ActionReturnType => {
|
||||
const {
|
||||
url, shouldSkipModal, ignoreDeepLinks, linkContext, tabId = getCurrentTabId(),
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import type { ApiError, ApiReaction, ApiReactionEmoji } from '../../../api/types';
|
||||
import type { ActionReturnType } from '../../types';
|
||||
import { ApiMediaFormat, MAIN_THREAD_ID } from '../../../api/types';
|
||||
import {
|
||||
type ApiError,
|
||||
ApiMediaFormat,
|
||||
type ApiReaction,
|
||||
type ApiReactionEmoji,
|
||||
MAIN_THREAD_ID,
|
||||
} from '../../../api/types';
|
||||
|
||||
import { GENERAL_REFETCH_INTERVAL } from '../../../config';
|
||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||
@ -21,13 +26,11 @@ import {
|
||||
} from '../../helpers';
|
||||
import { addActionHandler, getGlobal, getPromiseActions, setGlobal } from '../../index';
|
||||
import {
|
||||
addChatMessagesById,
|
||||
updateChatMessage,
|
||||
updateTopicWithState,
|
||||
updateUnreadCounters,
|
||||
} from '../../reducers';
|
||||
import {
|
||||
addMessageReaction,
|
||||
addUnreadReactions,
|
||||
removeUnreadReactions,
|
||||
subtractXForEmojiInteraction,
|
||||
} from '../../reducers/reactions';
|
||||
@ -490,19 +493,15 @@ addActionHandler('loadUnreadReactions', async (global, actions, payload): Promis
|
||||
|
||||
const { messages, topics, totalCount } = result;
|
||||
|
||||
const byId = buildCollectionByKey(messages, 'id');
|
||||
const ids = Object.keys(byId).map(Number);
|
||||
|
||||
global = getGlobal();
|
||||
global = addChatMessagesById(global, chat.id, byId);
|
||||
topics.forEach((topicState) => {
|
||||
global = updateTopicWithState(global, chat.id, topicState);
|
||||
});
|
||||
global = addUnreadReactions({
|
||||
global = updateUnreadCounters({
|
||||
global,
|
||||
chatId,
|
||||
ids,
|
||||
threadId,
|
||||
messages,
|
||||
topics,
|
||||
totalCount,
|
||||
unreadCountKey: 'unreadReactionsCount',
|
||||
});
|
||||
|
||||
setGlobal(global);
|
||||
|
||||
@ -8,12 +8,11 @@ import {
|
||||
import { ARCHIVED_FOLDER_ID } from '../../config';
|
||||
import { areDeepEqual } from '../../util/areDeepEqual';
|
||||
import {
|
||||
areSortedArraysEqual, buildCollectionByKey, omit, omitUndefined, pick, unique,
|
||||
areSortedArraysEqual, buildCollectionByKey, omit, omitUndefined, pick,
|
||||
} from '../../util/iteratees';
|
||||
import { groupMessageIdsByThreadId } from '../helpers';
|
||||
import { selectChatFullInfo } from '../selectors';
|
||||
import { selectThreadReadState } from '../selectors/threads';
|
||||
import { replaceThreadReadStateParam, updateThreadInfoLastMessageId } from './threads';
|
||||
import { updateThreadInfoLastMessageId } from './threads';
|
||||
import { addUnreadCount, removeUnreadCount } from './unreadCounters';
|
||||
|
||||
const DEFAULT_CHAT_LISTS: ChatListType[] = ['active', 'archived'];
|
||||
|
||||
@ -132,28 +131,13 @@ export function addUnreadMentions<T extends GlobalState>({
|
||||
ids: number[];
|
||||
totalCount?: number;
|
||||
}): T {
|
||||
const messageIdsByThreadId = groupMessageIdsByThreadId(global, chatId, ids, false);
|
||||
|
||||
for (const threadId in messageIdsByThreadId) {
|
||||
const messageIds = messageIdsByThreadId[threadId];
|
||||
if (totalCount !== undefined) { // Assume that when `totalCount` is passed, server returned full id list
|
||||
global = replaceThreadReadStateParam(global, chatId, threadId, 'unreadMentions', messageIds);
|
||||
global = replaceThreadReadStateParam(global, chatId, threadId, 'unreadMentionsCount', totalCount);
|
||||
continue;
|
||||
}
|
||||
|
||||
const readState = selectThreadReadState(global, chatId, threadId);
|
||||
const prevChatUnreadMentions = readState?.unreadMentions || [];
|
||||
const updatedUnreadMentions = unique([...prevChatUnreadMentions, ...messageIds]).sort((a, b) => b - a);
|
||||
global = replaceThreadReadStateParam(global, chatId, threadId, 'unreadMentions', updatedUnreadMentions);
|
||||
|
||||
const updatedUnreadMentionsCount = (readState?.unreadMentionsCount || 0)
|
||||
+ Math.max(0, updatedUnreadMentions.length - prevChatUnreadMentions.length);
|
||||
|
||||
global = replaceThreadReadStateParam(global, chatId, threadId, 'unreadMentionsCount', updatedUnreadMentionsCount);
|
||||
}
|
||||
|
||||
return global;
|
||||
return addUnreadCount({
|
||||
global,
|
||||
chatId,
|
||||
messageIds: ids,
|
||||
totalCount,
|
||||
unreadCountKey: 'unreadMentionsCount',
|
||||
});
|
||||
}
|
||||
|
||||
export function removeUnreadMentions<T extends GlobalState>({
|
||||
@ -163,24 +147,12 @@ export function removeUnreadMentions<T extends GlobalState>({
|
||||
chatId: string;
|
||||
ids: number[];
|
||||
}): T {
|
||||
const messageIdsByThreadId = groupMessageIdsByThreadId(global, chatId, ids, false);
|
||||
|
||||
for (const threadId in messageIdsByThreadId) {
|
||||
const messageIds = messageIdsByThreadId[threadId];
|
||||
const readState = selectThreadReadState(global, chatId, threadId);
|
||||
const prevChatUnreadMentions = (readState?.unreadMentions || []);
|
||||
const updatedUnreadMentions = prevChatUnreadMentions?.filter((id) => !messageIds.includes(id));
|
||||
|
||||
global = replaceThreadReadStateParam(global, chatId, threadId, 'unreadMentions', updatedUnreadMentions);
|
||||
|
||||
const removedCount = prevChatUnreadMentions.length - updatedUnreadMentions.length;
|
||||
if (removedCount && readState?.unreadMentionsCount) {
|
||||
const updatedUnreadMentionsCount = Math.max(readState.unreadMentionsCount - removedCount, 0);
|
||||
|
||||
global = replaceThreadReadStateParam(global, chatId, threadId, 'unreadMentionsCount', updatedUnreadMentionsCount);
|
||||
}
|
||||
}
|
||||
return global;
|
||||
return removeUnreadCount({
|
||||
global,
|
||||
chatId,
|
||||
messageIds: ids,
|
||||
unreadCountKey: 'unreadMentionsCount',
|
||||
});
|
||||
}
|
||||
|
||||
export function updateChat<T extends GlobalState>(
|
||||
|
||||
@ -2,6 +2,7 @@ export * from './chats';
|
||||
export * from './messages';
|
||||
export * from './symbols';
|
||||
export * from './users';
|
||||
export * from './unreadCounters';
|
||||
export * from './globalSearch';
|
||||
export * from './middleSearch';
|
||||
export * from './management';
|
||||
@ -13,5 +14,6 @@ export * from './statistics';
|
||||
export * from './stories';
|
||||
export * from './translations';
|
||||
export * from './peers';
|
||||
export * from './polls';
|
||||
export * from './topics';
|
||||
export * from './gifts';
|
||||
|
||||
@ -52,6 +52,7 @@ import {
|
||||
import { selectThreadIdFromMessage, selectThreadInfo, selectThreadLocalStateParam } from '../selectors/threads';
|
||||
import { removeUnreadMentions } from './chats';
|
||||
import { removeIdFromSearchResults } from './middleSearch';
|
||||
import { removeUnreadPollVotes } from './polls';
|
||||
import { removeUnreadReactions } from './reactions';
|
||||
import { updateTabState } from './tabs';
|
||||
import {
|
||||
@ -437,6 +438,7 @@ export function deleteChatMessages<T extends GlobalState>(
|
||||
|
||||
global = removeUnreadReactions({ global, chatId, ids: messageIds });
|
||||
global = removeUnreadMentions({ global, chatId, ids: messageIds });
|
||||
global = removeUnreadPollVotes({ global, chatId, ids: messageIds });
|
||||
|
||||
const newById = omit(byId, messageIds);
|
||||
global = replaceChatMessages(global, chatId, newById);
|
||||
|
||||
35
src/global/reducers/polls.ts
Normal file
35
src/global/reducers/polls.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import type { GlobalState } from '../types';
|
||||
|
||||
import { addUnreadCount, removeUnreadCount } from './unreadCounters';
|
||||
|
||||
export function addUnreadPollVotes<T extends GlobalState>({
|
||||
global, chatId, ids, totalCount,
|
||||
}: {
|
||||
global: T;
|
||||
chatId: string;
|
||||
ids: number[];
|
||||
totalCount?: number;
|
||||
}): T {
|
||||
return addUnreadCount({
|
||||
global,
|
||||
chatId,
|
||||
messageIds: ids,
|
||||
totalCount,
|
||||
unreadCountKey: 'unreadPollVotesCount',
|
||||
});
|
||||
}
|
||||
|
||||
export function removeUnreadPollVotes<T extends GlobalState>({
|
||||
global, chatId, ids,
|
||||
}: {
|
||||
global: T;
|
||||
chatId: string;
|
||||
ids: number[];
|
||||
}): T {
|
||||
return removeUnreadCount({
|
||||
global,
|
||||
chatId,
|
||||
messageIds: ids,
|
||||
unreadCountKey: 'unreadPollVotesCount',
|
||||
});
|
||||
}
|
||||
@ -2,17 +2,15 @@ import type { GlobalState } from '../types';
|
||||
import { type ApiMessage, type ApiReactionWithPaid } from '../../api/types';
|
||||
|
||||
import { MIN_SCREEN_WIDTH_FOR_STATIC_LEFT_COLUMN, MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN } from '../../config';
|
||||
import { unique } from '../../util/iteratees';
|
||||
import windowSize from '../../util/windowSize';
|
||||
import {
|
||||
MIN_LEFT_COLUMN_WIDTH,
|
||||
SIDE_COLUMN_MAX_WIDTH,
|
||||
} from '../../components/middle/helpers/calculateMiddleFooterTransforms';
|
||||
import { groupMessageIdsByThreadId, updateReactionCount } from '../helpers';
|
||||
import { updateReactionCount } from '../helpers';
|
||||
import { selectIsChatWithSelf, selectSendAs, selectTabState } from '../selectors';
|
||||
import { selectThreadReadState } from '../selectors/threads';
|
||||
import { updateChatMessage } from './messages';
|
||||
import { replaceThreadReadStateParam } from './threads';
|
||||
import { addUnreadCount, removeUnreadCount } from './unreadCounters';
|
||||
|
||||
import { getIsMobile } from '../../hooks/useAppLayout';
|
||||
|
||||
@ -85,29 +83,13 @@ export function addUnreadReactions<T extends GlobalState>({
|
||||
ids: number[];
|
||||
totalCount?: number;
|
||||
}): T {
|
||||
const messageIdsByThreadId = groupMessageIdsByThreadId(global, chatId, ids, false);
|
||||
|
||||
for (const threadId in messageIdsByThreadId) {
|
||||
const messageIds = messageIdsByThreadId[threadId];
|
||||
if (totalCount !== undefined) { // Assume that when `totalCount` is passed, server returned full id list
|
||||
global = replaceThreadReadStateParam(global, chatId, threadId, 'unreadReactions', messageIds);
|
||||
global = replaceThreadReadStateParam(global, chatId, threadId, 'unreadReactionsCount', totalCount);
|
||||
continue;
|
||||
}
|
||||
|
||||
const readState = selectThreadReadState(global, chatId, threadId);
|
||||
const prevChatUnreadReactions = readState?.unreadReactions || [];
|
||||
const updatedUnreadReactions = unique([...prevChatUnreadReactions, ...messageIds]).sort((a, b) => b - a);
|
||||
global = replaceThreadReadStateParam(global, chatId, threadId, 'unreadReactions', updatedUnreadReactions);
|
||||
|
||||
const delta = updatedUnreadReactions.length - prevChatUnreadReactions.length;
|
||||
if (delta > 0) {
|
||||
const unreadReactionsCount = (readState?.unreadReactionsCount || 0) + delta;
|
||||
global = replaceThreadReadStateParam(global, chatId, threadId, 'unreadReactionsCount', unreadReactionsCount);
|
||||
}
|
||||
}
|
||||
|
||||
return global;
|
||||
return addUnreadCount({
|
||||
global,
|
||||
chatId,
|
||||
messageIds: ids,
|
||||
totalCount,
|
||||
unreadCountKey: 'unreadReactionsCount',
|
||||
});
|
||||
}
|
||||
|
||||
export function removeUnreadReactions<T extends GlobalState>({
|
||||
@ -117,20 +99,10 @@ export function removeUnreadReactions<T extends GlobalState>({
|
||||
chatId: string;
|
||||
ids: number[];
|
||||
}): T {
|
||||
const messageIdsByThreadId = groupMessageIdsByThreadId(global, chatId, ids, false);
|
||||
|
||||
for (const threadId in messageIdsByThreadId) {
|
||||
const messageIds = messageIdsByThreadId[threadId];
|
||||
const readState = selectThreadReadState(global, chatId, threadId);
|
||||
const prevChatUnreadReactions = readState?.unreadReactions || [];
|
||||
const updatedUnreadReactions = prevChatUnreadReactions.filter((id) => !messageIds.includes(id));
|
||||
global = replaceThreadReadStateParam(global, chatId, threadId, 'unreadReactions', updatedUnreadReactions);
|
||||
|
||||
const delta = prevChatUnreadReactions.length - updatedUnreadReactions.length;
|
||||
if (delta > 0 && readState?.unreadReactionsCount) {
|
||||
const unreadReactionsCount = Math.max(readState.unreadReactionsCount - delta, 0);
|
||||
global = replaceThreadReadStateParam(global, chatId, threadId, 'unreadReactionsCount', unreadReactionsCount);
|
||||
}
|
||||
}
|
||||
return global;
|
||||
return removeUnreadCount({
|
||||
global,
|
||||
chatId,
|
||||
messageIds: ids,
|
||||
unreadCountKey: 'unreadReactionsCount',
|
||||
});
|
||||
}
|
||||
|
||||
170
src/global/reducers/unreadCounters.ts
Normal file
170
src/global/reducers/unreadCounters.ts
Normal file
@ -0,0 +1,170 @@
|
||||
import type { ApiMessage, ApiTopicWithState } from '../../api/types';
|
||||
import type { ThreadId, ThreadReadState } from '../../types';
|
||||
import type { GlobalState } from '../types';
|
||||
|
||||
import { buildCollectionByKey, unique } from '../../util/iteratees';
|
||||
import { groupMessageIdsByThreadId } from '../helpers';
|
||||
import { selectThreadReadState } from '../selectors/threads';
|
||||
import { addChatMessagesById } from './messages';
|
||||
import { replaceThreadReadStateParam } from './threads';
|
||||
import { updateTopicWithState } from './topics';
|
||||
|
||||
type UnreadCountIdsKey = 'unreadMentions' | 'unreadReactions' | 'unreadPollVotes';
|
||||
export type UnreadCountKey = 'unreadMentionsCount' | 'unreadReactionsCount' | 'unreadPollVotesCount';
|
||||
|
||||
type AddUnreadCountArgs<T extends GlobalState, TKey extends UnreadCountKey> = {
|
||||
global: T;
|
||||
chatId: string;
|
||||
messageIds: number[];
|
||||
totalCount?: number;
|
||||
unreadCountKey: TKey;
|
||||
};
|
||||
|
||||
type RemoveUnreadCountArgs<T extends GlobalState, TKey extends UnreadCountKey> = {
|
||||
global: T;
|
||||
chatId: string;
|
||||
messageIds: number[];
|
||||
unreadCountKey: TKey;
|
||||
};
|
||||
|
||||
type UpdateUnreadCountersArgs<T extends GlobalState, TKey extends UnreadCountKey> = {
|
||||
global: T;
|
||||
chatId: string;
|
||||
threadId: ThreadId;
|
||||
messages: ApiMessage[];
|
||||
topics: ApiTopicWithState[];
|
||||
totalCount: number;
|
||||
unreadCountKey: TKey;
|
||||
};
|
||||
|
||||
const unreadIdsKeyByCountKey = {
|
||||
unreadMentionsCount: 'unreadMentions',
|
||||
unreadReactionsCount: 'unreadReactions',
|
||||
unreadPollVotesCount: 'unreadPollVotes',
|
||||
} satisfies Record<UnreadCountKey, UnreadCountIdsKey>;
|
||||
|
||||
function getUnreadIdsKey<TKey extends UnreadCountKey>(unreadCountKey: TKey) {
|
||||
return unreadIdsKeyByCountKey[unreadCountKey];
|
||||
}
|
||||
|
||||
function replaceThreadUnreadIds<T extends GlobalState, TKey extends UnreadCountKey>(
|
||||
global: T,
|
||||
chatId: string,
|
||||
threadId: ThreadId,
|
||||
unreadCountKey: TKey,
|
||||
messageIds: number[] | undefined,
|
||||
) {
|
||||
return replaceThreadReadStateParam(global, chatId, threadId, getUnreadIdsKey(unreadCountKey), messageIds);
|
||||
}
|
||||
|
||||
function replaceThreadUnreadCount<T extends GlobalState, TKey extends UnreadCountKey>(
|
||||
global: T,
|
||||
chatId: string,
|
||||
threadId: ThreadId,
|
||||
unreadCountKey: TKey,
|
||||
count: number | undefined,
|
||||
) {
|
||||
return replaceThreadReadStateParam(global, chatId, threadId, unreadCountKey, count);
|
||||
}
|
||||
|
||||
function selectThreadUnreadIds<TKey extends UnreadCountKey>(
|
||||
readState: ThreadReadState | undefined,
|
||||
unreadCountKey: TKey,
|
||||
) {
|
||||
return readState?.[getUnreadIdsKey(unreadCountKey)];
|
||||
}
|
||||
|
||||
export function addUnreadCount<T extends GlobalState, TKey extends UnreadCountKey>({
|
||||
global,
|
||||
chatId,
|
||||
messageIds,
|
||||
totalCount,
|
||||
unreadCountKey,
|
||||
}: AddUnreadCountArgs<T, TKey>): T {
|
||||
const messageIdsByThreadId = groupMessageIdsByThreadId(global, chatId, messageIds, false);
|
||||
|
||||
for (const threadId in messageIdsByThreadId) {
|
||||
const threadMessageIds = messageIdsByThreadId[threadId];
|
||||
if (totalCount !== undefined) {
|
||||
global = replaceThreadUnreadIds(global, chatId, threadId, unreadCountKey, threadMessageIds);
|
||||
global = replaceThreadUnreadCount(global, chatId, threadId, unreadCountKey, totalCount);
|
||||
continue;
|
||||
}
|
||||
|
||||
const readState = selectThreadReadState(global, chatId, threadId);
|
||||
const previousIds = selectThreadUnreadIds(readState, unreadCountKey) || [];
|
||||
const updatedIds = unique([...previousIds, ...threadMessageIds]).sort((a, b) => b - a);
|
||||
|
||||
global = replaceThreadUnreadIds(global, chatId, threadId, unreadCountKey, updatedIds);
|
||||
|
||||
const delta = updatedIds.length - previousIds.length;
|
||||
if (delta > 0) {
|
||||
global = replaceThreadUnreadCount(
|
||||
global,
|
||||
chatId,
|
||||
threadId,
|
||||
unreadCountKey,
|
||||
(readState?.[unreadCountKey] || 0) + delta,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
export function removeUnreadCount<T extends GlobalState, TKey extends UnreadCountKey>({
|
||||
global,
|
||||
chatId,
|
||||
messageIds,
|
||||
unreadCountKey,
|
||||
}: RemoveUnreadCountArgs<T, TKey>): T {
|
||||
const messageIdsByThreadId = groupMessageIdsByThreadId(global, chatId, messageIds, false);
|
||||
|
||||
for (const threadId in messageIdsByThreadId) {
|
||||
const threadMessageIds = messageIdsByThreadId[threadId];
|
||||
const threadMessageIdSet = new Set(threadMessageIds);
|
||||
const readState = selectThreadReadState(global, chatId, threadId);
|
||||
const previousIds = selectThreadUnreadIds(readState, unreadCountKey) || [];
|
||||
const updatedIds = previousIds.filter((id) => !threadMessageIdSet.has(id));
|
||||
|
||||
global = replaceThreadUnreadIds(global, chatId, threadId, unreadCountKey, updatedIds);
|
||||
|
||||
const previousCount = readState?.[unreadCountKey];
|
||||
const delta = previousIds.length - updatedIds.length;
|
||||
|
||||
if (delta > 0 && previousCount) {
|
||||
global = replaceThreadUnreadCount(
|
||||
global,
|
||||
chatId,
|
||||
threadId,
|
||||
unreadCountKey,
|
||||
Math.max(previousCount - delta, 0),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
export function updateUnreadCounters<T extends GlobalState, TKey extends UnreadCountKey>({
|
||||
global,
|
||||
chatId,
|
||||
threadId,
|
||||
messages,
|
||||
topics,
|
||||
totalCount,
|
||||
unreadCountKey,
|
||||
}: UpdateUnreadCountersArgs<T, TKey>): T {
|
||||
const messagesById = buildCollectionByKey(messages, 'id');
|
||||
|
||||
global = addChatMessagesById(global, chatId, messagesById);
|
||||
topics.forEach((topicState) => {
|
||||
global = updateTopicWithState(global, chatId, topicState);
|
||||
});
|
||||
|
||||
const messageIds = Object.keys(messagesById).map(Number).sort((a, b) => b - a);
|
||||
global = replaceThreadUnreadIds(global, chatId, threadId, unreadCountKey, messageIds);
|
||||
global = replaceThreadUnreadCount(global, chatId, threadId, unreadCountKey, totalCount);
|
||||
|
||||
return global;
|
||||
}
|
||||
@ -1434,6 +1434,11 @@ export interface ActionPayloads {
|
||||
threadId?: ThreadId;
|
||||
offsetId?: number;
|
||||
};
|
||||
loadUnreadPollVotes: {
|
||||
chatId: string;
|
||||
threadId?: ThreadId;
|
||||
offsetId?: number;
|
||||
};
|
||||
scheduleForViewsIncrement: {
|
||||
chatId: string;
|
||||
ids: number[];
|
||||
@ -1468,6 +1473,10 @@ export interface ActionPayloads {
|
||||
chatId: string;
|
||||
threadId?: ThreadId;
|
||||
} & WithTabId;
|
||||
focusNextPollVote: {
|
||||
chatId: string;
|
||||
threadId?: ThreadId;
|
||||
} & WithTabId;
|
||||
readAllReactions: {
|
||||
chatId: string;
|
||||
threadId?: ThreadId;
|
||||
@ -1476,10 +1485,18 @@ export interface ActionPayloads {
|
||||
chatId: string;
|
||||
threadId?: ThreadId;
|
||||
};
|
||||
readAllPollVotes: {
|
||||
chatId: string;
|
||||
threadId?: ThreadId;
|
||||
};
|
||||
markMentionsRead: {
|
||||
chatId: string;
|
||||
messageIds: number[];
|
||||
};
|
||||
markPollVotesRead: {
|
||||
chatId: string;
|
||||
messageIds: number[];
|
||||
};
|
||||
copyMessageLink: {
|
||||
chatId: string;
|
||||
messageId: number;
|
||||
|
||||
@ -1812,6 +1812,8 @@ messages.editChatParticipantRank#a00f32b0 peer:InputPeer participant:InputPeer r
|
||||
messages.declineUrlAuth#35436bbc url:string = Bool;
|
||||
messages.checkUrlAuthMatchCode#c9a47b0b url:string match_code:string = Bool;
|
||||
messages.composeMessageWithAI#fd426afe flags:# proofread:flags.0?true emojify:flags.3?true text:TextWithEntities translate_to_lang:flags.1?string change_tone:flags.2?string = messages.ComposedMessageWithAI;
|
||||
messages.getUnreadPollVotes#43286cf2 flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readPollVotes#1720b4d8 flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
|
||||
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.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
||||
|
||||
@ -224,6 +224,8 @@
|
||||
"messages.clearRecentReactions",
|
||||
"messages.readMentions",
|
||||
"messages.getUnreadMentions",
|
||||
"messages.readPollVotes",
|
||||
"messages.getUnreadPollVotes",
|
||||
"messages.getSavedDialogs",
|
||||
"messages.getSavedHistory",
|
||||
"messages.deleteSavedHistory",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -24,330 +24,328 @@ $icons-map: (
|
||||
"add-user": "\f106",
|
||||
"add": "\f107",
|
||||
"admin": "\f108",
|
||||
"ai-edit": "\f109",
|
||||
"ai-fix": "\f10a",
|
||||
"ai": "\f10b",
|
||||
"allow-share": "\f10c",
|
||||
"allow-speak": "\f10d",
|
||||
"animals": "\f10e",
|
||||
"animations": "\f10f",
|
||||
"archive-filled": "\f110",
|
||||
"archive-from-main": "\f111",
|
||||
"archive-to-main": "\f112",
|
||||
"archive": "\f113",
|
||||
"arrow-down-circle": "\f114",
|
||||
"arrow-down": "\f115",
|
||||
"arrow-left": "\f116",
|
||||
"arrow-right": "\f117",
|
||||
"ask-support": "\f118",
|
||||
"attach": "\f119",
|
||||
"auction-drop": "\f11a",
|
||||
"auction-filled": "\f11b",
|
||||
"auction-next-round": "\f11c",
|
||||
"auction": "\f11d",
|
||||
"author-hidden": "\f11e",
|
||||
"avatar-archived-chats": "\f11f",
|
||||
"avatar-deleted-account": "\f120",
|
||||
"avatar-saved-messages": "\f121",
|
||||
"bold": "\f122",
|
||||
"boost-craft-chance": "\f123",
|
||||
"boost-outline": "\f124",
|
||||
"boost": "\f125",
|
||||
"boostcircle": "\f126",
|
||||
"boosts": "\f127",
|
||||
"bot-command": "\f128",
|
||||
"bot-commands-filled": "\f129",
|
||||
"bots": "\f12a",
|
||||
"brush": "\f12b",
|
||||
"bug": "\f12c",
|
||||
"calendar-filter": "\f12d",
|
||||
"calendar": "\f12e",
|
||||
"camera-add": "\f12f",
|
||||
"camera": "\f130",
|
||||
"car": "\f131",
|
||||
"card": "\f132",
|
||||
"cash-circle": "\f133",
|
||||
"channel-filled": "\f134",
|
||||
"channel": "\f135",
|
||||
"channelviews": "\f136",
|
||||
"chat-badge": "\f137",
|
||||
"chats-badge": "\f138",
|
||||
"check-bold": "\f139",
|
||||
"check": "\f13a",
|
||||
"clock-edit": "\f13b",
|
||||
"clock": "\f13c",
|
||||
"close-circle": "\f13d",
|
||||
"close-topic": "\f13e",
|
||||
"close": "\f13f",
|
||||
"closed-gift": "\f140",
|
||||
"cloud-download": "\f141",
|
||||
"collapse-modal": "\f142",
|
||||
"collapse": "\f143",
|
||||
"colorize": "\f144",
|
||||
"combine-craft": "\f145",
|
||||
"comments-sticker": "\f146",
|
||||
"comments": "\f147",
|
||||
"copy-media": "\f148",
|
||||
"copy": "\f149",
|
||||
"craft": "\f14a",
|
||||
"crop": "\f14b",
|
||||
"crown-take-off-outline": "\f14c",
|
||||
"crown-take-off": "\f14d",
|
||||
"crown-wear-outline": "\f14e",
|
||||
"crown-wear": "\f14f",
|
||||
"darkmode": "\f150",
|
||||
"data": "\f151",
|
||||
"delete-filled": "\f152",
|
||||
"delete-left": "\f153",
|
||||
"delete-user": "\f154",
|
||||
"delete": "\f155",
|
||||
"diamond": "\f156",
|
||||
"document": "\f157",
|
||||
"double-badge": "\f158",
|
||||
"down": "\f159",
|
||||
"download": "\f15a",
|
||||
"dropdown-arrows": "\f15b",
|
||||
"eats": "\f15c",
|
||||
"edit": "\f15d",
|
||||
"email": "\f15e",
|
||||
"enter": "\f15f",
|
||||
"expand-modal": "\f160",
|
||||
"expand": "\f161",
|
||||
"eye-crossed-outline": "\f162",
|
||||
"eye-crossed": "\f163",
|
||||
"eye-outline": "\f164",
|
||||
"eye": "\f165",
|
||||
"favorite-filled": "\f166",
|
||||
"favorite": "\f167",
|
||||
"file-badge": "\f168",
|
||||
"flag": "\f169",
|
||||
"flip": "\f16a",
|
||||
"folder-badge": "\f16b",
|
||||
"folder-tabs-bot": "\f16c",
|
||||
"folder-tabs-channel": "\f16d",
|
||||
"folder-tabs-chat": "\f16e",
|
||||
"folder-tabs-chats": "\f16f",
|
||||
"folder-tabs-folder": "\f170",
|
||||
"folder-tabs-group": "\f171",
|
||||
"folder-tabs-star": "\f172",
|
||||
"folder-tabs-user": "\f173",
|
||||
"folder": "\f174",
|
||||
"fontsize": "\f175",
|
||||
"forums": "\f176",
|
||||
"forward": "\f177",
|
||||
"fragment": "\f178",
|
||||
"frozen-time": "\f179",
|
||||
"fullscreen": "\f17a",
|
||||
"gifs": "\f17b",
|
||||
"gift-transfer-inline": "\f17c",
|
||||
"gift": "\f17d",
|
||||
"group-filled": "\f17e",
|
||||
"group": "\f17f",
|
||||
"grouped-disable": "\f180",
|
||||
"grouped": "\f181",
|
||||
"hand-stop-filled": "\f182",
|
||||
"hand-stop": "\f183",
|
||||
"hashtag": "\f184",
|
||||
"hd-photo": "\f185",
|
||||
"heart-outline": "\f186",
|
||||
"heart": "\f187",
|
||||
"help": "\f188",
|
||||
"info-filled": "\f189",
|
||||
"info": "\f18a",
|
||||
"install": "\f18b",
|
||||
"italic": "\f18c",
|
||||
"key": "\f18d",
|
||||
"keyboard": "\f18e",
|
||||
"lamp": "\f18f",
|
||||
"language": "\f190",
|
||||
"large-pause": "\f191",
|
||||
"large-play": "\f192",
|
||||
"link-badge": "\f193",
|
||||
"link-broken": "\f194",
|
||||
"link": "\f195",
|
||||
"location": "\f196",
|
||||
"lock-badge": "\f197",
|
||||
"lock": "\f198",
|
||||
"logout": "\f199",
|
||||
"loop": "\f19a",
|
||||
"mention": "\f19b",
|
||||
"menu": "\f19c",
|
||||
"message-failed": "\f19d",
|
||||
"message-pending": "\f19e",
|
||||
"message-read": "\f19f",
|
||||
"message-succeeded": "\f1a0",
|
||||
"message": "\f1a1",
|
||||
"microphone-alt": "\f1a2",
|
||||
"microphone": "\f1a3",
|
||||
"monospace": "\f1a4",
|
||||
"more-circle": "\f1a5",
|
||||
"more": "\f1a6",
|
||||
"move-caption-down": "\f1a7",
|
||||
"move-caption-up": "\f1a8",
|
||||
"mute": "\f1a9",
|
||||
"muted": "\f1aa",
|
||||
"my-notes": "\f1ab",
|
||||
"new-chat-filled": "\f1ac",
|
||||
"new-send": "\f1ad",
|
||||
"next-link": "\f1ae",
|
||||
"next": "\f1af",
|
||||
"no-download": "\f1b0",
|
||||
"no-share": "\f1b1",
|
||||
"nochannel": "\f1b2",
|
||||
"noise-suppression": "\f1b3",
|
||||
"non-contacts": "\f1b4",
|
||||
"note": "\f1b5",
|
||||
"one-filled": "\f1b6",
|
||||
"open-in-new-tab": "\f1b7",
|
||||
"password-off": "\f1b8",
|
||||
"pause": "\f1b9",
|
||||
"permissions": "\f1ba",
|
||||
"phone-discard-outline": "\f1bb",
|
||||
"phone-discard": "\f1bc",
|
||||
"phone": "\f1bd",
|
||||
"photo": "\f1be",
|
||||
"pin-badge": "\f1bf",
|
||||
"pin-list": "\f1c0",
|
||||
"pin": "\f1c1",
|
||||
"pinned-chat": "\f1c2",
|
||||
"pinned-message": "\f1c3",
|
||||
"pip": "\f1c4",
|
||||
"play-story": "\f1c5",
|
||||
"play": "\f1c6",
|
||||
"poll": "\f1c7",
|
||||
"previous-link": "\f1c8",
|
||||
"previous": "\f1c9",
|
||||
"privacy-policy": "\f1ca",
|
||||
"proof-of-ownership": "\f1cb",
|
||||
"quote-text": "\f1cc",
|
||||
"quote": "\f1cd",
|
||||
"radial-badge": "\f1ce",
|
||||
"rating-icons-level1": "\f1cf",
|
||||
"rating-icons-level10": "\f1d0",
|
||||
"rating-icons-level2": "\f1d1",
|
||||
"rating-icons-level20": "\f1d2",
|
||||
"rating-icons-level3": "\f1d3",
|
||||
"rating-icons-level30": "\f1d4",
|
||||
"rating-icons-level4": "\f1d5",
|
||||
"rating-icons-level40": "\f1d6",
|
||||
"rating-icons-level5": "\f1d7",
|
||||
"rating-icons-level50": "\f1d8",
|
||||
"rating-icons-level6": "\f1d9",
|
||||
"rating-icons-level60": "\f1da",
|
||||
"rating-icons-level7": "\f1db",
|
||||
"rating-icons-level70": "\f1dc",
|
||||
"rating-icons-level8": "\f1dd",
|
||||
"rating-icons-level80": "\f1de",
|
||||
"rating-icons-level9": "\f1df",
|
||||
"rating-icons-level90": "\f1e0",
|
||||
"rating-icons-negative": "\f1e1",
|
||||
"readchats": "\f1e2",
|
||||
"recent": "\f1e3",
|
||||
"redo": "\f1e4",
|
||||
"refund": "\f1e5",
|
||||
"reload": "\f1e6",
|
||||
"remove-quote": "\f1e7",
|
||||
"remove": "\f1e8",
|
||||
"reopen-topic": "\f1e9",
|
||||
"reorder-tabs": "\f1ea",
|
||||
"replace": "\f1eb",
|
||||
"replies": "\f1ec",
|
||||
"reply-filled": "\f1ed",
|
||||
"reply": "\f1ee",
|
||||
"revenue-split": "\f1ef",
|
||||
"revote": "\f1f0",
|
||||
"rotate": "\f1f1",
|
||||
"save-story": "\f1f2",
|
||||
"saved-messages": "\f1f3",
|
||||
"schedule": "\f1f4",
|
||||
"scheduled": "\f1f5",
|
||||
"sd-photo": "\f1f6",
|
||||
"search": "\f1f7",
|
||||
"select-filled": "\f1f8",
|
||||
"select": "\f1f9",
|
||||
"sell-outline": "\f1fa",
|
||||
"sell": "\f1fb",
|
||||
"send-outline": "\f1fc",
|
||||
"send": "\f1fd",
|
||||
"settings-filled": "\f1fe",
|
||||
"settings": "\f1ff",
|
||||
"share-filled": "\f200",
|
||||
"share-screen-outlined": "\f201",
|
||||
"share-screen-stop": "\f202",
|
||||
"share-screen": "\f203",
|
||||
"show-message": "\f204",
|
||||
"sidebar": "\f205",
|
||||
"skip-next": "\f206",
|
||||
"skip-previous": "\f207",
|
||||
"smallscreen": "\f208",
|
||||
"smile": "\f209",
|
||||
"sort-by-date": "\f20a",
|
||||
"sort-by-number": "\f20b",
|
||||
"sort-by-price": "\f20c",
|
||||
"sort": "\f20d",
|
||||
"speaker-muted-story": "\f20e",
|
||||
"speaker-outline": "\f20f",
|
||||
"speaker-story": "\f210",
|
||||
"speaker": "\f211",
|
||||
"spoiler-disable": "\f212",
|
||||
"spoiler": "\f213",
|
||||
"sport": "\f214",
|
||||
"star": "\f215",
|
||||
"stars-lock": "\f216",
|
||||
"stars-refund": "\f217",
|
||||
"stats": "\f218",
|
||||
"stealth-future": "\f219",
|
||||
"stealth-past": "\f21a",
|
||||
"stickers": "\f21b",
|
||||
"stop-raising-hand": "\f21c",
|
||||
"stop": "\f21d",
|
||||
"story-caption": "\f21e",
|
||||
"story-expired": "\f21f",
|
||||
"story-priority": "\f220",
|
||||
"story-reply": "\f221",
|
||||
"strikethrough": "\f222",
|
||||
"tag-add": "\f223",
|
||||
"tag-crossed": "\f224",
|
||||
"tag-filter": "\f225",
|
||||
"tag-name": "\f226",
|
||||
"tag": "\f227",
|
||||
"timer": "\f228",
|
||||
"toncoin": "\f229",
|
||||
"tools": "\f22a",
|
||||
"topic-new": "\f22b",
|
||||
"trade": "\f22c",
|
||||
"transcribe": "\f22d",
|
||||
"truck": "\f22e",
|
||||
"unarchive": "\f22f",
|
||||
"underlined": "\f230",
|
||||
"understood": "\f231",
|
||||
"undo": "\f232",
|
||||
"unique-profile": "\f233",
|
||||
"unlist-outline": "\f234",
|
||||
"unlist": "\f235",
|
||||
"unlock-badge": "\f236",
|
||||
"unlock": "\f237",
|
||||
"unmute": "\f238",
|
||||
"unpin": "\f239",
|
||||
"unread": "\f23a",
|
||||
"up": "\f23b",
|
||||
"user-filled": "\f23c",
|
||||
"user-online": "\f23d",
|
||||
"user-stars": "\f23e",
|
||||
"user-tag": "\f23f",
|
||||
"user": "\f240",
|
||||
"video-outlined": "\f241",
|
||||
"video-stop": "\f242",
|
||||
"video": "\f243",
|
||||
"view-once": "\f244",
|
||||
"voice-chat": "\f245",
|
||||
"volume-1": "\f246",
|
||||
"volume-2": "\f247",
|
||||
"volume-3": "\f248",
|
||||
"warning": "\f249",
|
||||
"web": "\f24a",
|
||||
"webapp": "\f24b",
|
||||
"word-wrap": "\f24c",
|
||||
"zoom-in": "\f24d",
|
||||
"zoom-out": "\f24e",
|
||||
"allow-share": "\f109",
|
||||
"allow-speak": "\f10a",
|
||||
"animals": "\f10b",
|
||||
"animations": "\f10c",
|
||||
"archive-filled": "\f10d",
|
||||
"archive-from-main": "\f10e",
|
||||
"archive-to-main": "\f10f",
|
||||
"archive": "\f110",
|
||||
"arrow-down-circle": "\f111",
|
||||
"arrow-down": "\f112",
|
||||
"arrow-left": "\f113",
|
||||
"arrow-right": "\f114",
|
||||
"ask-support": "\f115",
|
||||
"attach": "\f116",
|
||||
"auction-drop": "\f117",
|
||||
"auction-filled": "\f118",
|
||||
"auction-next-round": "\f119",
|
||||
"auction": "\f11a",
|
||||
"author-hidden": "\f11b",
|
||||
"avatar-archived-chats": "\f11c",
|
||||
"avatar-deleted-account": "\f11d",
|
||||
"avatar-saved-messages": "\f11e",
|
||||
"bold": "\f11f",
|
||||
"boost-craft-chance": "\f120",
|
||||
"boost-outline": "\f121",
|
||||
"boost": "\f122",
|
||||
"boostcircle": "\f123",
|
||||
"boosts": "\f124",
|
||||
"bot-command": "\f125",
|
||||
"bot-commands-filled": "\f126",
|
||||
"bots": "\f127",
|
||||
"brush": "\f128",
|
||||
"bug": "\f129",
|
||||
"calendar-filter": "\f12a",
|
||||
"calendar": "\f12b",
|
||||
"camera-add": "\f12c",
|
||||
"camera": "\f12d",
|
||||
"car": "\f12e",
|
||||
"card": "\f12f",
|
||||
"cash-circle": "\f130",
|
||||
"channel-filled": "\f131",
|
||||
"channel": "\f132",
|
||||
"channelviews": "\f133",
|
||||
"chat-badge": "\f134",
|
||||
"chats-badge": "\f135",
|
||||
"check-bold": "\f136",
|
||||
"check": "\f137",
|
||||
"clock-edit": "\f138",
|
||||
"clock": "\f139",
|
||||
"close-circle": "\f13a",
|
||||
"close-topic": "\f13b",
|
||||
"close": "\f13c",
|
||||
"closed-gift": "\f13d",
|
||||
"cloud-download": "\f13e",
|
||||
"collapse-modal": "\f13f",
|
||||
"collapse": "\f140",
|
||||
"colorize": "\f141",
|
||||
"combine-craft": "\f142",
|
||||
"comments-sticker": "\f143",
|
||||
"comments": "\f144",
|
||||
"copy-media": "\f145",
|
||||
"copy": "\f146",
|
||||
"craft": "\f147",
|
||||
"crop": "\f148",
|
||||
"crown-take-off-outline": "\f149",
|
||||
"crown-take-off": "\f14a",
|
||||
"crown-wear-outline": "\f14b",
|
||||
"crown-wear": "\f14c",
|
||||
"darkmode": "\f14d",
|
||||
"data": "\f14e",
|
||||
"delete-filled": "\f14f",
|
||||
"delete-left": "\f150",
|
||||
"delete-user": "\f151",
|
||||
"delete": "\f152",
|
||||
"diamond": "\f153",
|
||||
"document": "\f154",
|
||||
"double-badge": "\f155",
|
||||
"down": "\f156",
|
||||
"download": "\f157",
|
||||
"dropdown-arrows": "\f158",
|
||||
"eats": "\f159",
|
||||
"edit": "\f15a",
|
||||
"email": "\f15b",
|
||||
"enter": "\f15c",
|
||||
"expand-modal": "\f15d",
|
||||
"expand": "\f15e",
|
||||
"eye-crossed-outline": "\f15f",
|
||||
"eye-crossed": "\f160",
|
||||
"eye-outline": "\f161",
|
||||
"eye": "\f162",
|
||||
"favorite-filled": "\f163",
|
||||
"favorite": "\f164",
|
||||
"file-badge": "\f165",
|
||||
"flag": "\f166",
|
||||
"flip": "\f167",
|
||||
"folder-badge": "\f168",
|
||||
"folder-tabs-bot": "\f169",
|
||||
"folder-tabs-channel": "\f16a",
|
||||
"folder-tabs-chat": "\f16b",
|
||||
"folder-tabs-chats": "\f16c",
|
||||
"folder-tabs-folder": "\f16d",
|
||||
"folder-tabs-group": "\f16e",
|
||||
"folder-tabs-star": "\f16f",
|
||||
"folder-tabs-user": "\f170",
|
||||
"folder": "\f171",
|
||||
"fontsize": "\f172",
|
||||
"forums": "\f173",
|
||||
"forward": "\f174",
|
||||
"fragment": "\f175",
|
||||
"frozen-time": "\f176",
|
||||
"fullscreen": "\f177",
|
||||
"gifs": "\f178",
|
||||
"gift-transfer-inline": "\f179",
|
||||
"gift": "\f17a",
|
||||
"group-filled": "\f17b",
|
||||
"group": "\f17c",
|
||||
"grouped-disable": "\f17d",
|
||||
"grouped": "\f17e",
|
||||
"hand-stop-filled": "\f17f",
|
||||
"hand-stop": "\f180",
|
||||
"hashtag": "\f181",
|
||||
"hd-photo": "\f182",
|
||||
"heart-outline": "\f183",
|
||||
"heart": "\f184",
|
||||
"help": "\f185",
|
||||
"info-filled": "\f186",
|
||||
"info": "\f187",
|
||||
"install": "\f188",
|
||||
"italic": "\f189",
|
||||
"key": "\f18a",
|
||||
"keyboard": "\f18b",
|
||||
"lamp": "\f18c",
|
||||
"language": "\f18d",
|
||||
"large-pause": "\f18e",
|
||||
"large-play": "\f18f",
|
||||
"link-badge": "\f190",
|
||||
"link-broken": "\f191",
|
||||
"link": "\f192",
|
||||
"location": "\f193",
|
||||
"lock-badge": "\f194",
|
||||
"lock": "\f195",
|
||||
"logout": "\f196",
|
||||
"loop": "\f197",
|
||||
"mention": "\f198",
|
||||
"menu": "\f199",
|
||||
"message-failed": "\f19a",
|
||||
"message-pending": "\f19b",
|
||||
"message-read": "\f19c",
|
||||
"message-succeeded": "\f19d",
|
||||
"message": "\f19e",
|
||||
"microphone-alt": "\f19f",
|
||||
"microphone": "\f1a0",
|
||||
"monospace": "\f1a1",
|
||||
"more-circle": "\f1a2",
|
||||
"more": "\f1a3",
|
||||
"move-caption-down": "\f1a4",
|
||||
"move-caption-up": "\f1a5",
|
||||
"mute": "\f1a6",
|
||||
"muted": "\f1a7",
|
||||
"my-notes": "\f1a8",
|
||||
"new-chat-filled": "\f1a9",
|
||||
"new-send": "\f1aa",
|
||||
"next-link": "\f1ab",
|
||||
"next": "\f1ac",
|
||||
"no-download": "\f1ad",
|
||||
"no-share": "\f1ae",
|
||||
"nochannel": "\f1af",
|
||||
"noise-suppression": "\f1b0",
|
||||
"non-contacts": "\f1b1",
|
||||
"note": "\f1b2",
|
||||
"one-filled": "\f1b3",
|
||||
"open-in-new-tab": "\f1b4",
|
||||
"password-off": "\f1b5",
|
||||
"pause": "\f1b6",
|
||||
"permissions": "\f1b7",
|
||||
"phone-discard-outline": "\f1b8",
|
||||
"phone-discard": "\f1b9",
|
||||
"phone": "\f1ba",
|
||||
"photo": "\f1bb",
|
||||
"pin-badge": "\f1bc",
|
||||
"pin-list": "\f1bd",
|
||||
"pin": "\f1be",
|
||||
"pinned-chat": "\f1bf",
|
||||
"pinned-message": "\f1c0",
|
||||
"pip": "\f1c1",
|
||||
"play-story": "\f1c2",
|
||||
"play": "\f1c3",
|
||||
"poll-badge": "\f1c4",
|
||||
"poll": "\f1c5",
|
||||
"previous-link": "\f1c6",
|
||||
"previous": "\f1c7",
|
||||
"privacy-policy": "\f1c8",
|
||||
"proof-of-ownership": "\f1c9",
|
||||
"quote-text": "\f1ca",
|
||||
"quote": "\f1cb",
|
||||
"radial-badge": "\f1cc",
|
||||
"rating-icons-level1": "\f1cd",
|
||||
"rating-icons-level10": "\f1ce",
|
||||
"rating-icons-level2": "\f1cf",
|
||||
"rating-icons-level20": "\f1d0",
|
||||
"rating-icons-level3": "\f1d1",
|
||||
"rating-icons-level30": "\f1d2",
|
||||
"rating-icons-level4": "\f1d3",
|
||||
"rating-icons-level40": "\f1d4",
|
||||
"rating-icons-level5": "\f1d5",
|
||||
"rating-icons-level50": "\f1d6",
|
||||
"rating-icons-level6": "\f1d7",
|
||||
"rating-icons-level60": "\f1d8",
|
||||
"rating-icons-level7": "\f1d9",
|
||||
"rating-icons-level70": "\f1da",
|
||||
"rating-icons-level8": "\f1db",
|
||||
"rating-icons-level80": "\f1dc",
|
||||
"rating-icons-level9": "\f1dd",
|
||||
"rating-icons-level90": "\f1de",
|
||||
"rating-icons-negative": "\f1df",
|
||||
"readchats": "\f1e0",
|
||||
"recent": "\f1e1",
|
||||
"redo": "\f1e2",
|
||||
"refund": "\f1e3",
|
||||
"reload": "\f1e4",
|
||||
"remove-quote": "\f1e5",
|
||||
"remove": "\f1e6",
|
||||
"reopen-topic": "\f1e7",
|
||||
"reorder-tabs": "\f1e8",
|
||||
"replace": "\f1e9",
|
||||
"replies": "\f1ea",
|
||||
"reply-filled": "\f1eb",
|
||||
"reply": "\f1ec",
|
||||
"revenue-split": "\f1ed",
|
||||
"revote": "\f1ee",
|
||||
"rotate": "\f1ef",
|
||||
"save-story": "\f1f0",
|
||||
"saved-messages": "\f1f1",
|
||||
"schedule": "\f1f2",
|
||||
"scheduled": "\f1f3",
|
||||
"sd-photo": "\f1f4",
|
||||
"search": "\f1f5",
|
||||
"select-filled": "\f1f6",
|
||||
"select": "\f1f7",
|
||||
"sell-outline": "\f1f8",
|
||||
"sell": "\f1f9",
|
||||
"send-outline": "\f1fa",
|
||||
"send": "\f1fb",
|
||||
"settings-filled": "\f1fc",
|
||||
"settings": "\f1fd",
|
||||
"share-filled": "\f1fe",
|
||||
"share-screen-outlined": "\f1ff",
|
||||
"share-screen-stop": "\f200",
|
||||
"share-screen": "\f201",
|
||||
"show-message": "\f202",
|
||||
"sidebar": "\f203",
|
||||
"skip-next": "\f204",
|
||||
"skip-previous": "\f205",
|
||||
"smallscreen": "\f206",
|
||||
"smile": "\f207",
|
||||
"sort-by-date": "\f208",
|
||||
"sort-by-number": "\f209",
|
||||
"sort-by-price": "\f20a",
|
||||
"sort": "\f20b",
|
||||
"speaker-muted-story": "\f20c",
|
||||
"speaker-outline": "\f20d",
|
||||
"speaker-story": "\f20e",
|
||||
"speaker": "\f20f",
|
||||
"spoiler-disable": "\f210",
|
||||
"spoiler": "\f211",
|
||||
"sport": "\f212",
|
||||
"star": "\f213",
|
||||
"stars-lock": "\f214",
|
||||
"stars-refund": "\f215",
|
||||
"stats": "\f216",
|
||||
"stealth-future": "\f217",
|
||||
"stealth-past": "\f218",
|
||||
"stickers": "\f219",
|
||||
"stop-raising-hand": "\f21a",
|
||||
"stop": "\f21b",
|
||||
"story-caption": "\f21c",
|
||||
"story-expired": "\f21d",
|
||||
"story-priority": "\f21e",
|
||||
"story-reply": "\f21f",
|
||||
"strikethrough": "\f220",
|
||||
"tag-add": "\f221",
|
||||
"tag-crossed": "\f222",
|
||||
"tag-filter": "\f223",
|
||||
"tag-name": "\f224",
|
||||
"tag": "\f225",
|
||||
"timer": "\f226",
|
||||
"toncoin": "\f227",
|
||||
"tools": "\f228",
|
||||
"topic-new": "\f229",
|
||||
"trade": "\f22a",
|
||||
"transcribe": "\f22b",
|
||||
"truck": "\f22c",
|
||||
"unarchive": "\f22d",
|
||||
"underlined": "\f22e",
|
||||
"understood": "\f22f",
|
||||
"undo": "\f230",
|
||||
"unique-profile": "\f231",
|
||||
"unlist-outline": "\f232",
|
||||
"unlist": "\f233",
|
||||
"unlock-badge": "\f234",
|
||||
"unlock": "\f235",
|
||||
"unmute": "\f236",
|
||||
"unpin": "\f237",
|
||||
"unread": "\f238",
|
||||
"up": "\f239",
|
||||
"user-filled": "\f23a",
|
||||
"user-online": "\f23b",
|
||||
"user-stars": "\f23c",
|
||||
"user-tag": "\f23d",
|
||||
"user": "\f23e",
|
||||
"video-outlined": "\f23f",
|
||||
"video-stop": "\f240",
|
||||
"video": "\f241",
|
||||
"view-once": "\f242",
|
||||
"voice-chat": "\f243",
|
||||
"volume-1": "\f244",
|
||||
"volume-2": "\f245",
|
||||
"volume-3": "\f246",
|
||||
"warning": "\f247",
|
||||
"web": "\f248",
|
||||
"webapp": "\f249",
|
||||
"word-wrap": "\f24a",
|
||||
"zoom-in": "\f24b",
|
||||
"zoom-out": "\f24c",
|
||||
);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -197,6 +197,7 @@ export type FontIconName =
|
||||
| 'pip'
|
||||
| 'play-story'
|
||||
| 'play'
|
||||
| 'poll-badge'
|
||||
| 'poll'
|
||||
| 'previous-link'
|
||||
| 'previous'
|
||||
|
||||
@ -623,8 +623,10 @@ export interface ThreadReadState {
|
||||
unreadCount?: number;
|
||||
unreadMentionsCount?: number;
|
||||
unreadReactionsCount?: number;
|
||||
unreadPollVotesCount?: number;
|
||||
unreadReactions?: number[];
|
||||
unreadMentions?: number[];
|
||||
unreadPollVotes?: number[];
|
||||
hasUnreadMark?: boolean;
|
||||
|
||||
lastReadOutboxMessageId?: number;
|
||||
|
||||
1
src/types/language.d.ts
vendored
1
src/types/language.d.ts
vendored
@ -91,6 +91,7 @@ export interface LangPair {
|
||||
'AnonymousPoll': undefined;
|
||||
'AccDescrReactionMentionDown': undefined;
|
||||
'AccDescrMentionDown': undefined;
|
||||
'AccDescrPollVoteDown': undefined;
|
||||
'AccDescrPageDown': undefined;
|
||||
'ChannelPrivate': undefined;
|
||||
'ChannelPrivateInfo': undefined;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user