mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2025-12-24 20:26:24 +00:00
221 lines
5.9 KiB
TypeScript
221 lines
5.9 KiB
TypeScript
// Copyright 2023 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
import { isConversationInChatFolder } from '../types/ChatFolder.std.js';
|
|
import { CurrentChatFolders } from '../types/CurrentChatFolders.std.js';
|
|
import { isConversationMuted } from './isConversationMuted.std.js';
|
|
|
|
import type { ConversationType } from '../state/ducks/conversations.preload.js';
|
|
import type { ChatFolderId } from '../types/ChatFolder.std.js';
|
|
import type { NotificationProfileType } from '../types/NotificationProfile.std.js';
|
|
|
|
type MutableUnreadStats = {
|
|
/**
|
|
* Total of `conversation.unreadCount`
|
|
* in all countable conversations in the set.
|
|
*
|
|
* Note: `conversation.unreadCount` should always include the number of
|
|
* unread messages with mentions.
|
|
*/
|
|
unreadCount: number;
|
|
|
|
/**
|
|
* Total of `conversation.unreadMentionsCount`
|
|
* in all countable conversations in the set.
|
|
*/
|
|
unreadMentionsCount: number;
|
|
|
|
/**
|
|
* Total of `unreadCount === 0 && markedRead == true`
|
|
* in all countable conversations in the set.
|
|
*/
|
|
readChatsMarkedUnreadCount: number;
|
|
};
|
|
|
|
/**
|
|
* This can be used to describe unread counts of chats, stories, and calls,
|
|
* individually or all of them together.
|
|
*/
|
|
export type UnreadStats = Readonly<MutableUnreadStats>;
|
|
|
|
/** @internal exported for testing */
|
|
export function _createUnreadStats(): MutableUnreadStats {
|
|
return {
|
|
unreadCount: 0,
|
|
unreadMentionsCount: 0,
|
|
readChatsMarkedUnreadCount: 0,
|
|
};
|
|
}
|
|
|
|
export type UnreadStatsIncludeMuted =
|
|
| 'setting-on' // badge-count-muted-conversations == true
|
|
| 'setting-off' // badge-count-muted-conversations == false
|
|
| 'force-include'
|
|
| 'force-exclude';
|
|
|
|
export type UnreadStatsOptions = Readonly<{
|
|
includeMuted: UnreadStatsIncludeMuted;
|
|
activeProfile: NotificationProfileType | undefined;
|
|
}>;
|
|
|
|
export type ConversationPropsForUnreadStats = Readonly<
|
|
Pick<
|
|
ConversationType,
|
|
| 'id'
|
|
| 'type'
|
|
| 'activeAt'
|
|
| 'isArchived'
|
|
| 'markedUnread'
|
|
| 'muteExpiresAt'
|
|
| 'unreadCount'
|
|
| 'unreadMentionsCount'
|
|
| 'left'
|
|
>
|
|
>;
|
|
|
|
export type AllChatFoldersUnreadStats = Map<ChatFolderId, UnreadStats>;
|
|
|
|
/** @internal exported for testing */
|
|
export function _shouldExcludeMuted(
|
|
includeMuted: UnreadStatsIncludeMuted
|
|
): boolean {
|
|
return includeMuted === 'setting-off' || includeMuted === 'force-exclude';
|
|
}
|
|
|
|
/** @internal exported for testing */
|
|
export function _canCountConversation(
|
|
conversation: ConversationPropsForUnreadStats,
|
|
options: UnreadStatsOptions
|
|
): boolean {
|
|
if (conversation.activeAt == null || conversation.activeAt === 0) {
|
|
return false;
|
|
}
|
|
if (conversation.isArchived) {
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
_shouldExcludeMuted(options.includeMuted) &&
|
|
(isConversationMuted(conversation) ||
|
|
(options.activeProfile &&
|
|
!options.activeProfile.allowedMembers.has(conversation.id)))
|
|
) {
|
|
return false;
|
|
}
|
|
if (conversation.left) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** @internal exported for testing */
|
|
export function _countConversation(
|
|
unreadStats: MutableUnreadStats,
|
|
conversation: ConversationPropsForUnreadStats
|
|
): void {
|
|
const mutable = unreadStats;
|
|
const {
|
|
unreadCount = 0,
|
|
unreadMentionsCount = 0,
|
|
markedUnread = false,
|
|
} = conversation;
|
|
|
|
const hasUnreadCount = unreadCount > 0 || unreadMentionsCount > 0;
|
|
|
|
if (hasUnreadCount) {
|
|
mutable.unreadCount += unreadCount;
|
|
mutable.unreadMentionsCount += unreadMentionsCount;
|
|
} else if (markedUnread) {
|
|
mutable.readChatsMarkedUnreadCount += 1;
|
|
}
|
|
}
|
|
|
|
export function isConversationUnread(
|
|
conversation: ConversationPropsForUnreadStats,
|
|
options: UnreadStatsOptions
|
|
): boolean {
|
|
if (!_canCountConversation(conversation, options)) {
|
|
return false;
|
|
}
|
|
const { unreadCount, unreadMentionsCount, markedUnread } = conversation;
|
|
if (unreadCount != null && unreadCount !== 0) {
|
|
return true;
|
|
}
|
|
if (unreadMentionsCount != null && unreadMentionsCount !== 0) {
|
|
return true;
|
|
}
|
|
if (markedUnread) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export function countConversationUnreadStats(
|
|
conversation: ConversationPropsForUnreadStats,
|
|
options: UnreadStatsOptions
|
|
): UnreadStats {
|
|
const unreadStats = _createUnreadStats();
|
|
if (_canCountConversation(conversation, options)) {
|
|
_countConversation(unreadStats, conversation);
|
|
}
|
|
return unreadStats;
|
|
}
|
|
|
|
export function countAllConversationsUnreadStats(
|
|
conversations: ReadonlyArray<ConversationPropsForUnreadStats | undefined>,
|
|
options: UnreadStatsOptions
|
|
): UnreadStats {
|
|
const unreadStats = _createUnreadStats();
|
|
|
|
for (const conversation of conversations) {
|
|
if (conversation && _canCountConversation(conversation, options)) {
|
|
_countConversation(unreadStats, conversation);
|
|
}
|
|
}
|
|
|
|
return unreadStats;
|
|
}
|
|
|
|
export function countAllChatFoldersUnreadStats(
|
|
currentChatFolders: CurrentChatFolders,
|
|
conversations: ReadonlyArray<ConversationPropsForUnreadStats>,
|
|
options: UnreadStatsOptions
|
|
): AllChatFoldersUnreadStats {
|
|
const results = new Map<ChatFolderId, MutableUnreadStats>();
|
|
const sortedChatFolders =
|
|
CurrentChatFolders.toSortedArray(currentChatFolders);
|
|
|
|
for (const conversation of conversations) {
|
|
// skip if we shouldn't count it
|
|
if (!_canCountConversation(conversation, options)) {
|
|
continue;
|
|
}
|
|
|
|
const {
|
|
unreadCount = 0,
|
|
unreadMentionsCount = 0,
|
|
markedUnread = false,
|
|
} = conversation;
|
|
|
|
// skip if we don't have any unreads
|
|
if (unreadCount === 0 && unreadMentionsCount === 0 && !markedUnread) {
|
|
continue;
|
|
}
|
|
|
|
// check which chatFolders should count this conversation
|
|
for (const chatFolder of sortedChatFolders) {
|
|
if (isConversationInChatFolder(chatFolder, conversation)) {
|
|
let unreadStats = results.get(chatFolder.id);
|
|
if (unreadStats == null) {
|
|
unreadStats = _createUnreadStats();
|
|
results.set(chatFolder.id, unreadStats);
|
|
}
|
|
|
|
_countConversation(unreadStats, conversation);
|
|
}
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|