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