/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "dialogs/dialogs_inner_widget_accessibility.h" #include "data/notify/data_notify_settings.h" #include "data/data_channel.h" #include "data/data_chat_filters.h" #include "data/data_drafts.h" #include "data/data_folder.h" #include "data/data_forum_topic.h" #include "data/data_message_reactions.h" #include "data/data_peer_values.h" #include "data/data_saved_sublist.h" #include "data/data_session.h" #include "data/data_stories.h" #include "data/data_user.h" #include "dialogs/dialogs_entry.h" #include "dialogs/dialogs_row.h" #include "history/history.h" #include "history/history_item.h" #include "history/history_item_components.h" #include "history/history_item_helpers.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "ui/text/format_values.h" namespace Dialogs { namespace { [[nodiscard]] QString ChatTypeString(not_null peer) { if (const auto user = peer->asUser()) { if (user->isInaccessible()) { return tr::lng_deleted(tr::now); } else if (user->isSelf()) { return tr::lng_saved_messages(tr::now); } else if (user->isServiceUser()) { return tr::lng_sr_chat_service(tr::now); } else if (user->isBot()) { return tr::lng_sr_chat_bot(tr::now); } } else if (peer->isBroadcast()) { return tr::lng_sr_chat_channel(tr::now); } else if (peer->isMegagroup() || peer->isChat()) { return tr::lng_sr_chat_group(tr::now); } return QString(); } [[nodiscard]] QStringList MessageTailParts( not_null item, not_null history, not_null peer) { QStringList parts; if (item->isService()) { return parts; } const auto dateTime = ItemDateTime(item); QString status; auto statusBeforeMessage = false; if (item->out()) { if (item->isSending()) { status = tr::lng_sr_chat_sending(tr::now); statusBeforeMessage = true; } else if (item->hasFailed()) { status = tr::lng_sr_chat_failed(tr::now); statusBeforeMessage = true; } else if (item->unread(history)) { status = tr::lng_sr_message_not_seen(tr::now); statusBeforeMessage = true; } else { status = tr::lng_sr_message_seen(tr::now); statusBeforeMessage = true; } } else { status = tr::lng_sr_chat_received(tr::now); statusBeforeMessage = false; } auto messageText = item->notificationText().text; if (!messageText.isEmpty()) { constexpr auto kMaxMessageLength = 100; if (messageText.size() > kMaxMessageLength) { messageText = messageText.left(kMaxMessageLength) + u"..."_q; } if (peer->isChat() || peer->isMegagroup()) { if (const auto from = item->from()) { if (!item->out()) { messageText = from->shortName() + u": "_q + messageText; } } } if (statusBeforeMessage) { messageText = status + u", "_q + messageText; } parts << messageText; } const auto &reactions = item->reactions(); if (!reactions.empty()) { QStringList reactionParts; for (const auto &reaction : reactions) { QString reactionText; if (reaction.id.paid()) { reactionText = tr::lng_sr_chat_reaction_star(tr::now); } else if (const auto emoji = reaction.id.emoji(); !emoji.isEmpty()) { reactionText = emoji; } else { reactionText = tr::lng_sr_chat_reaction_custom(tr::now); } if (reaction.count > 1) { reactionText += u" "_q + QString::number(reaction.count); } reactionParts << reactionText; } if (!reactionParts.isEmpty()) { parts << tr::lng_sr_chat_message_reactions( tr::now, lt_reactions, reactionParts.join(u", "_q)); } } if (!statusBeforeMessage) { parts << status; } if (dateTime.isValid()) { const auto now = QDateTime::currentDateTime(); QString timeText; if (dateTime.date() == now.date()) { timeText = tr::lng_schedule_at(tr::now) + u" "_q + QLocale().toString( dateTime.time(), QLocale::ShortFormat); } else { timeText = Ui::FormatDateTime(dateTime); } parts << timeText; } return parts; } } // namespace QString RowAccessibilityName( not_null row, FilterId filterId) { if (const auto topic = row->topic()) { const auto title = topic->isGeneral() ? Data::ForumGeneralIconTitle() : topic->title(); return tr::lng_sr_chat_topic(tr::now) + u" "_q + title; } if (const auto folder = row->folder()) { return tr::lng_sr_chat_folder(tr::now) + u" "_q + folder->chatListName(); } if (const auto sublist = row->sublist()) { return tr::lng_saved_messages(tr::now) + u" "_q + sublist->sublistPeer()->name(); } const auto history = row->history(); if (!history) { return {}; } const auto peer = history->peer; if (!peer) { return {}; } QStringList parts; const auto type = ChatTypeString(peer); if (!type.isEmpty()) { parts << type; } parts << peer->name(); if (peer->isScam()) { parts << tr::lng_sr_chat_scam(tr::now); } else if (peer->isFake()) { parts << tr::lng_sr_chat_fake(tr::now); } if (const auto user = peer->asUser()) { if (user->isPremium()) { parts << tr::lng_premium(tr::now); } } if (peer->isVerified()) { parts << tr::lng_sr_chat_verified(tr::now); } if (const auto user = peer->asUser()) { if (Data::IsUserOnline(user)) { parts << tr::lng_sr_chat_online(tr::now); } } if (peer->owner().notifySettings().isMuted(peer)) { parts << tr::lng_notification_exceptions_muted(tr::now); } if (row->entry()->isPinnedDialog(filterId)) { parts << tr::lng_sr_chat_pinned(tr::now); } if (const auto draft = history->cloudDraft(MsgId(0), PeerId(0))) { if (!draft->textWithTags.text.isEmpty()) { parts << tr::lng_from_draft(tr::now); } } else if (const auto localDraft = history->localDraft(MsgId(0), PeerId(0))) { if (!localDraft->textWithTags.text.isEmpty()) { parts << tr::lng_from_draft(tr::now); } } const auto badges = row->entry()->chatListBadgesState(); if (badges.unreadCounter > 0) { parts << tr::lng_sr_chat_unread( tr::now, lt_count, badges.unreadCounter); } else if (badges.unread) { parts << tr::lng_settings_quick_dialog_action_unread(tr::now); } if (badges.mention) { parts << tr::lng_sr_chat_mention(tr::now); } if (const auto item = history->chatListMessage()) { parts += MessageTailParts(item, history, peer); } return parts.join(u", "_q); } QString SubItemLabel(SubItem item) { switch (item) { case SubItem::Type: return tr::lng_sr_chat_column_type(tr::now); case SubItem::Name: return tr::lng_sr_chat_column_name(tr::now); case SubItem::Warning: return tr::lng_sr_chat_column_warning(tr::now); case SubItem::Premium: return tr::lng_sr_chat_column_premium(tr::now); case SubItem::Verified: return tr::lng_sr_chat_column_verified(tr::now); case SubItem::Activity: return tr::lng_sr_chat_column_activity(tr::now); case SubItem::Muted: return tr::lng_sr_chat_column_muted(tr::now); case SubItem::Pinned: return tr::lng_sr_chat_column_pinned(tr::now); case SubItem::Draft: return tr::lng_sr_chat_column_draft(tr::now); case SubItem::Unread: return tr::lng_sr_chat_column_unread(tr::now); case SubItem::Mention: return tr::lng_sr_chat_column_mention(tr::now); case SubItem::Sender: return tr::lng_sr_chat_column_sender(tr::now); case SubItem::Message: return tr::lng_sr_chat_column_message(tr::now); case SubItem::Delivery: return tr::lng_sr_chat_column_delivery(tr::now); case SubItem::Reactions: return tr::lng_sr_chat_column_reactions(tr::now); case SubItem::Time: return tr::lng_sr_chat_column_time(tr::now); case SubItem::Sponsored: return tr::lng_sr_chat_column_sponsored(tr::now); case SubItem::Stories: return tr::lng_sr_chat_column_stories(tr::now); case SubItem::Autodelete: return tr::lng_sr_chat_column_autodelete(tr::now); case SubItem::Subscription: return tr::lng_sr_chat_column_subscription(tr::now); case SubItem::Closed: return tr::lng_sr_chat_column_closed(tr::now); case SubItem::Forward: return tr::lng_sr_chat_column_forward(tr::now); case SubItem::Folders: return tr::lng_sr_chat_column_folders(tr::now); case SubItem::Count: break; } return {}; } QString SubItemValue( not_null row, FilterId filterId, SubItem item) { if (const auto topic = row->topic()) { switch (item) { case SubItem::Type: return tr::lng_sr_chat_topic(tr::now); case SubItem::Name: return topic->isGeneral() ? Data::ForumGeneralIconTitle() : topic->title(); case SubItem::Closed: if (topic->closed()) { return tr::lng_hours_closed(tr::now); } return {}; default: return {}; } } if (const auto folder = row->folder()) { switch (item) { case SubItem::Type: return tr::lng_sr_chat_folder(tr::now); case SubItem::Name: return folder->chatListName(); default: return {}; } } if (const auto sublist = row->sublist()) { switch (item) { case SubItem::Type: return tr::lng_saved_messages(tr::now); case SubItem::Name: return sublist->sublistPeer()->name(); default: return {}; } } const auto history = row->history(); if (!history) { return {}; } const auto peer = history->peer; if (!peer) { return {}; } switch (item) { case SubItem::Type: return ChatTypeString(peer); case SubItem::Name: return peer->name(); case SubItem::Warning: if (peer->isScam()) { return tr::lng_sr_chat_scam(tr::now); } else if (peer->isFake()) { return tr::lng_sr_chat_fake(tr::now); } return {}; case SubItem::Premium: if (const auto user = peer->asUser()) { if (user->isPremium()) { return tr::lng_premium(tr::now); } } return {}; case SubItem::Verified: if (peer->isVerified()) { return tr::lng_sr_chat_verified(tr::now); } return {}; case SubItem::Activity: if (const auto user = peer->asUser()) { if (Data::IsUserOnline(user)) { return tr::lng_sr_chat_online(tr::now); } } return {}; case SubItem::Muted: if (peer->owner().notifySettings().isMuted(peer)) { return tr::lng_notification_exceptions_muted(tr::now); } return {}; case SubItem::Pinned: if (row->entry()->isPinnedDialog(filterId)) { return tr::lng_sr_chat_pinned(tr::now); } return {}; case SubItem::Draft: if (const auto draft = history->cloudDraft(MsgId(0), PeerId(0))) { if (!draft->textWithTags.text.isEmpty()) { return tr::lng_from_draft(tr::now); } } else if (const auto localDraft = history->localDraft(MsgId(0), PeerId(0))) { if (!localDraft->textWithTags.text.isEmpty()) { return tr::lng_from_draft(tr::now); } } return {}; case SubItem::Unread: { const auto badges = row->entry()->chatListBadgesState(); if (badges.unreadCounter > 0) { return tr::lng_sr_chat_unread( tr::now, lt_count, badges.unreadCounter); } else if (badges.unread) { return tr::lng_settings_quick_dialog_action_unread(tr::now); } return {}; } case SubItem::Mention: { const auto badges = row->entry()->chatListBadgesState(); if (badges.mention) { return tr::lng_sr_chat_mention(tr::now); } return {}; } case SubItem::Sender: { const auto chatItem = history->chatListMessage(); if (!chatItem || chatItem->isService() || chatItem->out()) { return {}; } if (peer->isChat() || peer->isMegagroup()) { if (const auto from = chatItem->from()) { return from->shortName(); } } return {}; } case SubItem::Message: { const auto chatItem = history->chatListMessage(); if (!chatItem || chatItem->isService()) { return {}; } auto messageText = chatItem->notificationText().text; if (messageText.isEmpty()) { return {}; } constexpr auto kMaxMessageLength = 100; if (messageText.size() > kMaxMessageLength) { messageText = messageText.left(kMaxMessageLength) + u"..."_q; } return messageText; } case SubItem::Delivery: { const auto chatItem = history->chatListMessage(); if (!chatItem || chatItem->isService()) { return {}; } if (chatItem->out()) { if (chatItem->isSending()) { return tr::lng_sr_chat_sending(tr::now); } else if (chatItem->hasFailed()) { return tr::lng_sr_chat_failed(tr::now); } else if (chatItem->unread(history)) { return tr::lng_sr_message_not_seen(tr::now); } else { return tr::lng_sr_message_seen(tr::now); } } return tr::lng_sr_chat_received(tr::now); } case SubItem::Reactions: { const auto chatItem = history->chatListMessage(); if (!chatItem || chatItem->isService()) { return {}; } const auto &reactions = chatItem->reactions(); if (reactions.empty()) { return {}; } QStringList reactionParts; for (const auto &reaction : reactions) { QString reactionText; if (reaction.id.paid()) { reactionText = tr::lng_sr_chat_reaction_star(tr::now); } else if (const auto emoji = reaction.id.emoji(); !emoji.isEmpty()) { reactionText = emoji; } else { reactionText = tr::lng_sr_chat_reaction_custom(tr::now); } if (reaction.count > 1) { reactionText += u" "_q + QString::number(reaction.count); } reactionParts << reactionText; } return reactionParts.join(u", "_q); } case SubItem::Time: { const auto chatItem = history->chatListMessage(); if (!chatItem || chatItem->isService()) { return {}; } const auto dateTime = ItemDateTime(chatItem); if (!dateTime.isValid()) { return {}; } const auto now = QDateTime::currentDateTime(); if (dateTime.date() == now.date()) { return QLocale().toString( dateTime.time(), QLocale::ShortFormat); } return Ui::FormatDateTime(dateTime); } case SubItem::Sponsored: { if (history->useTopPromotion()) { const auto type = history->topPromotionType(); if (type.isEmpty()) { return tr::lng_sr_chat_sponsored(tr::now); } const auto custom = Lang::GetNonDefaultValue( "cloud_lng_badge_psa_" + type.toUtf8()); return custom.isEmpty() ? tr::lng_badge_psa_default(tr::now) : custom; } return {}; } case SubItem::Stories: { if (peer->hasActiveStories()) { if (peer->hasUnreadStories()) { const auto source = peer->owner().stories().source( peer->id); const auto count = source ? int(source->unreadCount()) : 1; return tr::lng_sr_chat_stories_unread( tr::now, lt_count, count); } return tr::lng_sr_chat_stories_read(tr::now); } return {}; } case SubItem::Autodelete: if (peer->messagesTTL()) { return tr::lng_sr_chat_autodelete(tr::now); } return {}; case SubItem::Subscription: if (Data::ChannelHasSubscriptionUntilDate(peer->asChannel())) { return tr::lng_sr_chat_subscribed(tr::now); } return {}; case SubItem::Closed: return {}; case SubItem::Forward: { const auto chatItem = history->chatListMessage(); if (!chatItem || chatItem->isService()) { return {}; } if (chatItem->Get()) { return tr::lng_sr_chat_forwarded(tr::now); } if (chatItem->replyToStory().valid()) { return tr::lng_sr_chat_story_reply(tr::now); } return {}; } case SubItem::Folders: { const auto entry = row->entry(); if (!entry->hasChatsFilterTags(filterId)) { return {}; } QStringList tags; const auto &list = row->history()->session().data().chatsFilters().list(); for (const auto &filter : list) { if (!entry->inChatList(filter.id()) || (filter.id() == filterId)) { continue; } tags << filter.title().text.text; } return tags.join(u", "_q); } case SubItem::Count: break; } return {}; } std::vector ActiveSubItems( not_null row, FilterId filterId) { auto result = std::vector(); result.reserve(int(SubItem::Count)); for (auto i = 0; i != int(SubItem::Count); ++i) { const auto item = SubItem(i); if (!SubItemValue(row, filterId, item).isEmpty()) { result.push_back(item); } } return result; } QString HashtagAccessibilityName(QStringView tag) { return tr::lng_sr_chat_hashtag(tr::now) + u": #"_q + tag.toString(); } QString PeerSearchResultAccessibilityName( not_null peer, bool sponsored) { QStringList parts; const auto type = ChatTypeString(peer); if (!type.isEmpty()) { parts << type; } parts << peer->name(); if (peer->isScam()) { parts << tr::lng_sr_chat_scam(tr::now); } else if (peer->isFake()) { parts << tr::lng_sr_chat_fake(tr::now); } if (const auto user = peer->asUser()) { if (user->isPremium()) { parts << tr::lng_premium(tr::now); } } if (peer->isVerified()) { parts << tr::lng_sr_chat_verified(tr::now); } if (const auto user = peer->asUser()) { if (Data::IsUserOnline(user)) { parts << tr::lng_sr_chat_online(tr::now); } } if (sponsored) { parts << tr::lng_sr_chat_sponsored(tr::now); } return parts.join(u", "_q); } QString SearchedMessageAccessibilityName( not_null row) { const auto item = row->item(); const auto history = item->history(); const auto peer = history->peer; QStringList parts; const auto type = ChatTypeString(peer); if (!type.isEmpty()) { parts << type; } parts << peer->name(); if (const auto topic = row->topic()) { parts << (topic->isGeneral() ? Data::ForumGeneralIconTitle() : topic->title()); } parts += MessageTailParts(item, history, peer); return parts.join(u", "_q); } } // namespace Dialogs