import type { ApiChat, ApiUser, ApiChatBannedRights, ApiChatAdminRights, ApiChatFolder, } from '../../api/types'; import { MAIN_THREAD_ID, } from '../../api/types'; import type { NotifyException, NotifySettings } from '../../types'; import type { LangFn } from '../../hooks/useLang'; import { ARCHIVED_FOLDER_ID, REPLIES_USER_ID, TME_LINK_PREFIX } from '../../config'; import { orderBy } from '../../util/iteratees'; import { getUserFirstOrLastName } from './users'; import { formatDateToString, formatTime } from '../../util/dateFormat'; import { prepareSearchWordsForNeedle } from '../../util/searchWords'; import { getVideoAvatarMediaHash } from './media'; const FOREVER_BANNED_DATE = Date.now() / 1000 + 31622400; // 366 days const VERIFIED_PRIORITY_BASE = 3e9; const PINNED_PRIORITY_BASE = 3e8; export function isUserId(entityId: string) { // Workaround for old-fashioned IDs stored locally if (typeof entityId === 'number') { return entityId > 0; } return !entityId.startsWith('-'); } export function isChatGroup(chat: ApiChat) { return isChatBasicGroup(chat) || isChatSuperGroup(chat); } export function isChatBasicGroup(chat: ApiChat) { return chat.type === 'chatTypeBasicGroup'; } export function isChatSuperGroup(chat: ApiChat) { return chat.type === 'chatTypeSuperGroup'; } export function isChatChannel(chat: ApiChat) { return chat.type === 'chatTypeChannel'; } export function isCommonBoxChat(chat: ApiChat) { return chat.type === 'chatTypePrivate' || chat.type === 'chatTypeBasicGroup'; } export function isChatWithRepliesBot(chatId: string) { return chatId === REPLIES_USER_ID; } export function getChatTypeString(chat: ApiChat) { switch (chat.type) { case 'chatTypePrivate': return 'PrivateChat'; case 'chatTypeBasicGroup': case 'chatTypeSuperGroup': return 'AccDescrGroup'; case 'chatTypeChannel': return 'AccDescrChannel'; default: return 'Chat'; } } export function getPrivateChatUserId(chat: ApiChat) { if (chat.type !== 'chatTypePrivate' && chat.type !== 'chatTypeSecret') { return undefined; } return chat.id; } // TODO Get rid of `user` export function getChatTitle(lang: LangFn, chat: ApiChat, user?: ApiUser, isSelf = false) { if (isSelf || (user && chat.id === user.id && user.isSelf)) { return lang('SavedMessages'); } return chat.title || lang('HiddenName'); } export function getChatDescription(chat: ApiChat) { if (!chat.fullInfo) { return undefined; } return chat.fullInfo.about; } export function getChatLink(chat: ApiChat) { const { username } = chat; if (username) { return `${TME_LINK_PREFIX}${username}`; } const { inviteLink } = chat.fullInfo || {}; return inviteLink; } export function getChatAvatarHash( owner: ApiChat | ApiUser, size: 'normal' | 'big' = 'normal', type: 'photo' | 'video' = 'photo', ) { if (!owner.avatarHash) { return undefined; } const { fullInfo } = owner; if (type === 'video') { if (fullInfo?.profilePhoto?.isVideo) { return getVideoAvatarMediaHash(fullInfo.profilePhoto); } return undefined; } switch (size) { case 'big': return `profile${owner.id}?${owner.avatarHash}`; default: return `avatar${owner.id}?${owner.avatarHash}`; } } export function isChatSummaryOnly(chat: ApiChat) { return !chat.lastMessage; } export function isChatAdmin(chat: ApiChat) { return Boolean(chat.adminRights); } export function getHasAdminRight(chat: ApiChat, key: keyof ApiChatAdminRights) { return chat.adminRights ? chat.adminRights[key] : false; } export function isUserRightBanned(chat: ApiChat, key: keyof ApiChatBannedRights) { return Boolean( (chat.currentUserBannedRights?.[key]) || (chat.defaultBannedRights?.[key]), ); } export function getCanPostInChat(chat: ApiChat, threadId: number) { if (threadId !== MAIN_THREAD_ID) { return true; } if (chat.isRestricted || chat.isForbidden || chat.migratedTo || chat.isNotJoined || isChatWithRepliesBot(chat.id)) { return false; } if (chat.isCreator) { return true; } if (isUserId(chat.id)) { return true; } if (isChatChannel(chat)) { return getHasAdminRight(chat, 'postMessages'); } return isChatAdmin(chat) || !isUserRightBanned(chat, 'sendMessages'); } export interface IAllowedAttachmentOptions { canAttachMedia: boolean; canAttachPolls: boolean; canSendStickers: boolean; canSendGifs: boolean; canAttachEmbedLinks: boolean; } export function getAllowedAttachmentOptions(chat?: ApiChat, isChatWithBot = false): IAllowedAttachmentOptions { if (!chat) { return { canAttachMedia: false, canAttachPolls: false, canSendStickers: false, canSendGifs: false, canAttachEmbedLinks: false, }; } const isAdmin = isChatAdmin(chat); return { canAttachMedia: isAdmin || !isUserRightBanned(chat, 'sendMedia'), canAttachPolls: (isAdmin || !isUserRightBanned(chat, 'sendPolls')) && (!isUserId(chat.id) || isChatWithBot), canSendStickers: isAdmin || !isUserRightBanned(chat, 'sendStickers'), canSendGifs: isAdmin || !isUserRightBanned(chat, 'sendGifs'), canAttachEmbedLinks: isAdmin || !isUserRightBanned(chat, 'embedLinks'), }; } export function getMessageSendingRestrictionReason( lang: LangFn, currentUserBannedRights?: ApiChatBannedRights, defaultBannedRights?: ApiChatBannedRights, ) { if (currentUserBannedRights?.sendMessages) { const { untilDate } = currentUserBannedRights; return untilDate && untilDate < FOREVER_BANNED_DATE ? lang( 'Channel.Persmission.Denied.SendMessages.Until', lang( 'formatDateAtTime', [formatDateToString(new Date(untilDate * 1000), lang.code), formatTime(lang, untilDate * 1000)], ), ) : lang('Channel.Persmission.Denied.SendMessages.Forever'); } if (defaultBannedRights?.sendMessages) { return lang('Channel.Persmission.Denied.SendMessages.DefaultRestrictedText'); } return undefined; } export function getChatSlowModeOptions(chat?: ApiChat) { if (!chat || !chat.fullInfo) { return undefined; } return chat.fullInfo.slowMode; } export function getChatOrder(chat: ApiChat) { return Math.max(chat.joinDate || 0, chat.draftDate || 0, chat.lastMessage?.date || 0); } export function isChatArchived(chat: ApiChat) { return chat.folderId === ARCHIVED_FOLDER_ID; } export function selectIsChatMuted( chat: ApiChat, notifySettings: NotifySettings, notifyExceptions: Record = {}, ) { // If this chat is in exceptions they take precedence if (notifyExceptions[chat.id] && notifyExceptions[chat.id].isMuted !== undefined) { return notifyExceptions[chat.id].isMuted; } return ( chat.isMuted || (isUserId(chat.id) && !notifySettings.hasPrivateChatsNotifications) || (isChatChannel(chat) && !notifySettings.hasBroadcastNotifications) || (isChatGroup(chat) && !notifySettings.hasGroupNotifications) ); } export function selectShouldShowMessagePreview( chat: ApiChat, notifySettings: NotifySettings, notifyExceptions: Record = {}, ) { const { hasPrivateChatsMessagePreview = true, hasBroadcastMessagePreview = true, hasGroupMessagePreview = true, } = notifySettings; // If this chat is in exceptions they take precedence if (notifyExceptions[chat.id] && notifyExceptions[chat.id].shouldShowPreviews !== undefined) { return notifyExceptions[chat.id].shouldShowPreviews; } return (isUserId(chat.id) && hasPrivateChatsMessagePreview) || (isChatChannel(chat) && hasBroadcastMessagePreview) || (isChatGroup(chat) && hasGroupMessagePreview); } export function getCanDeleteChat(chat: ApiChat) { return isChatBasicGroup(chat) || ((isChatSuperGroup(chat) || isChatChannel(chat)) && chat.isCreator); } export function getFolderDescriptionText(lang: LangFn, folder: ApiChatFolder, chatsCount?: number) { const { id, title, emoticon, description, pinnedChatIds, excludedChatIds, includedChatIds, excludeArchived, excludeMuted, excludeRead, ...filters } = folder; // If folder has multiple additive filters or uses include/exclude lists, // we display folder chats count if ( chatsCount !== undefined && ( Object.values(filters).filter(Boolean).length > 1 || (excludedChatIds?.length) || (includedChatIds?.length) )) { return lang('Chats', chatsCount); } // Otherwise, we return a short description of a single filter if (filters.bots) { return lang('FilterBots'); } else if (filters.groups) { return lang('FilterGroups'); } else if (filters.channels) { return lang('FilterChannels'); } else if (filters.contacts) { return lang('FilterContacts'); } else if (filters.nonContacts) { return lang('FilterNonContacts'); } else { return undefined; } } export function getMessageSenderName(lang: LangFn, chatId: string, sender?: ApiUser) { if (!sender || isUserId(chatId)) { return undefined; } if (sender.isSelf) { return lang('FromYou'); } return getUserFirstOrLastName(sender); } export function sortChatIds( chatIds: string[], chatsById: Record, shouldPrioritizeVerified = false, priorityIds?: string[], ) { return orderBy(chatIds, (id) => { const chat = chatsById[id]; if (!chat) { return 0; } let priority = 0; if (chat.lastMessage) { priority += chat.lastMessage.date; } if (shouldPrioritizeVerified && chat.isVerified) { priority += VERIFIED_PRIORITY_BASE; // ~100 years in seconds } if (priorityIds && priorityIds.includes(id)) { priority = Date.now() + PINNED_PRIORITY_BASE + (priorityIds.length - priorityIds.indexOf(id)); } return priority; }, 'desc'); } export function filterChatsByName( lang: LangFn, chatIds: string[], chatsById: Record, query?: string, currentUserId?: string, ) { if (!query) { return chatIds; } const searchWords = prepareSearchWordsForNeedle(query); return chatIds.filter((id) => { const chat = chatsById[id]; if (!chat) { return false; } return searchWords(getChatTitle(lang, chat, undefined, id === currentUserId)); }); }