mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Forum three-dot menu, except search.
This commit is contained in:
parent
9b0cae9c97
commit
6997e165c6
20 changed files with 354 additions and 172 deletions
|
@ -1136,6 +1136,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_info_bot_title" = "Bot Info";
|
"lng_info_bot_title" = "Bot Info";
|
||||||
"lng_info_group_title" = "Group Info";
|
"lng_info_group_title" = "Group Info";
|
||||||
"lng_info_channel_title" = "Channel Info";
|
"lng_info_channel_title" = "Channel Info";
|
||||||
|
"lng_info_topic_title" = "Topic Info";
|
||||||
"lng_profile_enable_notifications" = "Notifications";
|
"lng_profile_enable_notifications" = "Notifications";
|
||||||
"lng_profile_send_message" = "Send Message";
|
"lng_profile_send_message" = "Send Message";
|
||||||
"lng_info_add_as_contact" = "Add to contacts";
|
"lng_info_add_as_contact" = "Add to contacts";
|
||||||
|
@ -1484,6 +1485,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_action_webview_data_done" = "You have just successfully transferred data from the «{text}» button to the bot.";
|
"lng_action_webview_data_done" = "You have just successfully transferred data from the «{text}» button to the bot.";
|
||||||
"lng_action_gift_received" = "{user} sent you a gift for {cost}";
|
"lng_action_gift_received" = "{user} sent you a gift for {cost}";
|
||||||
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
|
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
|
||||||
|
"lng_action_topic_created" = "Topic created";
|
||||||
|
"lng_action_topic_renamed" = "Topic renamed to «{title}»";
|
||||||
|
"lng_action_topic_icon_changed" = "Topic icon changed to {emoji}";
|
||||||
|
"lng_action_topic_icon_removed" = "Topic icon removed";
|
||||||
|
"lng_action_topic_closed" = "Topic closed";
|
||||||
|
"lng_action_topic_reopened" = "Topic reopened";
|
||||||
|
|
||||||
"lng_premium_gift_duration_months#one" = "for {count} month";
|
"lng_premium_gift_duration_months#one" = "for {count} month";
|
||||||
"lng_premium_gift_duration_months#other" = "for {count} months";
|
"lng_premium_gift_duration_months#other" = "for {count} months";
|
||||||
|
@ -2116,6 +2123,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_context_send_message" = "Send message";
|
"lng_context_send_message" = "Send message";
|
||||||
"lng_context_view_group" = "View group info";
|
"lng_context_view_group" = "View group info";
|
||||||
"lng_context_view_channel" = "View channel info";
|
"lng_context_view_channel" = "View channel info";
|
||||||
|
"lng_context_view_topic" = "View topic info";
|
||||||
"lng_context_hide_psa" = "Hide this announcement";
|
"lng_context_hide_psa" = "Hide this announcement";
|
||||||
"lng_context_pin_to_top" = "Pin to top";
|
"lng_context_pin_to_top" = "Pin to top";
|
||||||
"lng_context_unpin_from_top" = "Unpin from top";
|
"lng_context_unpin_from_top" = "Unpin from top";
|
||||||
|
@ -3470,6 +3478,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_ringtones_error_max_size" = "Sorry, but your file is too big. The maximum size for ringtones is {size}.";
|
"lng_ringtones_error_max_size" = "Sorry, but your file is too big. The maximum size for ringtones is {size}.";
|
||||||
"lng_ringtones_error_max_duration" = "Sorry, but your file is too long. The maximum duration for ringtones is {duration}.";
|
"lng_ringtones_error_max_duration" = "Sorry, but your file is too long. The maximum duration for ringtones is {duration}.";
|
||||||
|
|
||||||
|
"lng_forum_topic_new" = "New Topic";
|
||||||
|
"lng_forum_topic_edit" = "Edit Topic";
|
||||||
|
"lng_forum_topic_title" = "Topic Title";
|
||||||
|
"lng_forum_topics_switch" = "Topics";
|
||||||
|
"lng_forum_no_topics" = "No topics currently created in this forum.";
|
||||||
|
"lng_forum_create_topic" = "Create topic";
|
||||||
|
"lng_forum_discard_sure" = "Discard sure?";
|
||||||
|
|
||||||
// Wnd specific
|
// Wnd specific
|
||||||
|
|
||||||
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
||||||
|
|
|
@ -337,8 +337,9 @@ void EditForumTopicBox(
|
||||||
const auto topic = (!creating && forum->peer->forum())
|
const auto topic = (!creating && forum->peer->forum())
|
||||||
? forum->peer->forum()->topicFor(rootId)
|
? forum->peer->forum()->topicFor(rootId)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
// #TODO lang-forum
|
box->setTitle(creating
|
||||||
box->setTitle(rpl::single(creating ? u"New topic"_q : u"Edit topic"_q));
|
? tr::lng_forum_topic_new()
|
||||||
|
: tr::lng_forum_topic_edit());
|
||||||
|
|
||||||
box->setMaxHeight(st::editTopicMaxHeight);
|
box->setMaxHeight(st::editTopicMaxHeight);
|
||||||
|
|
||||||
|
@ -365,7 +366,7 @@ void EditForumTopicBox(
|
||||||
object_ptr<Ui::InputField>(
|
object_ptr<Ui::InputField>(
|
||||||
box,
|
box,
|
||||||
st::defaultInputField,
|
st::defaultInputField,
|
||||||
rpl::single(u"Topic Title"_q), // #TODO lang-forum
|
tr::lng_forum_topic_title(),
|
||||||
topic ? topic->title() : QString()),
|
topic ? topic->title() : QString()),
|
||||||
st::editTopicTitleMargin);
|
st::editTopicTitleMargin);
|
||||||
box->setFocusCallback([=] {
|
box->setFocusCallback([=] {
|
||||||
|
|
|
@ -812,13 +812,13 @@ void Controller::fillForumButton() {
|
||||||
Expects(_controls.buttonsLayout != nullptr);
|
Expects(_controls.buttonsLayout != nullptr);
|
||||||
|
|
||||||
const auto channel = _peer->asChannel();
|
const auto channel = _peer->asChannel();
|
||||||
if (!channel) {
|
if (!channel || !channel->amCreator()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddButtonWithText(
|
AddButtonWithText(
|
||||||
_controls.buttonsLayout,
|
_controls.buttonsLayout,
|
||||||
rpl::single(u"Topics"_q), // #TODO lang-forum
|
tr::lng_forum_topics_switch(),
|
||||||
rpl::single(QString()),
|
rpl::single(QString()),
|
||||||
[] {},
|
[] {},
|
||||||
{ &st::settingsIconGroup, Settings::kIconPurple }
|
{ &st::settingsIconGroup, Settings::kIconPurple }
|
||||||
|
|
|
@ -142,7 +142,8 @@ ForumTopic::ForumTopic(not_null<Forum*> forum, MsgId rootId)
|
||||||
, _forum(forum)
|
, _forum(forum)
|
||||||
, _list(_forum->topicsList())
|
, _list(_forum->topicsList())
|
||||||
, _replies(std::make_shared<RepliesList>(history(), rootId))
|
, _replies(std::make_shared<RepliesList>(history(), rootId))
|
||||||
, _rootId(rootId) {
|
, _rootId(rootId)
|
||||||
|
, _lastKnownServerMessageId(rootId) {
|
||||||
Thread::setMuted(owner().notifySettings().isMuted(this));
|
Thread::setMuted(owner().notifySettings().isMuted(this));
|
||||||
|
|
||||||
_replies->unreadCountValue(
|
_replies->unreadCountValue(
|
||||||
|
@ -193,6 +194,7 @@ MsgId ForumTopic::rootId() const {
|
||||||
void ForumTopic::setRealRootId(MsgId realId) {
|
void ForumTopic::setRealRootId(MsgId realId) {
|
||||||
if (_rootId != realId) {
|
if (_rootId != realId) {
|
||||||
_rootId = realId;
|
_rootId = realId;
|
||||||
|
_lastKnownServerMessageId = realId;
|
||||||
_replies = std::make_shared<RepliesList>(history(), _rootId);
|
_replies = std::make_shared<RepliesList>(history(), _rootId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,6 +265,7 @@ int ForumTopic::chatListNameVersion() const {
|
||||||
|
|
||||||
void ForumTopic::applyTopicTopMessage(MsgId topMessageId) {
|
void ForumTopic::applyTopicTopMessage(MsgId topMessageId) {
|
||||||
if (topMessageId) {
|
if (topMessageId) {
|
||||||
|
growLastKnownServerMessageId(topMessageId);
|
||||||
const auto itemId = FullMsgId(channel()->id, topMessageId);
|
const auto itemId = FullMsgId(channel()->id, topMessageId);
|
||||||
if (const auto item = owner().message(itemId)) {
|
if (const auto item = owner().message(itemId)) {
|
||||||
setLastServerMessage(item);
|
setLastServerMessage(item);
|
||||||
|
@ -285,7 +288,14 @@ void ForumTopic::applyTopicTopMessage(MsgId topMessageId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ForumTopic::growLastKnownServerMessageId(MsgId id) {
|
||||||
|
_lastKnownServerMessageId = std::max(_lastKnownServerMessageId, id);
|
||||||
|
}
|
||||||
|
|
||||||
void ForumTopic::setLastServerMessage(HistoryItem *item) {
|
void ForumTopic::setLastServerMessage(HistoryItem *item) {
|
||||||
|
if (item) {
|
||||||
|
growLastKnownServerMessageId(item->id);
|
||||||
|
}
|
||||||
_lastServerMessage = item;
|
_lastServerMessage = item;
|
||||||
if (_lastMessage
|
if (_lastMessage
|
||||||
&& *_lastMessage
|
&& *_lastMessage
|
||||||
|
@ -303,6 +313,9 @@ void ForumTopic::setLastMessage(HistoryItem *item) {
|
||||||
_lastMessage = item;
|
_lastMessage = item;
|
||||||
if (!item || item->isRegular()) {
|
if (!item || item->isRegular()) {
|
||||||
_lastServerMessage = item;
|
_lastServerMessage = item;
|
||||||
|
if (item) {
|
||||||
|
growLastKnownServerMessageId(item->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setChatListMessage(item);
|
setChatListMessage(item);
|
||||||
}
|
}
|
||||||
|
@ -413,6 +426,10 @@ bool ForumTopic::lastServerMessageKnown() const {
|
||||||
return _lastServerMessage.has_value();
|
return _lastServerMessage.has_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MsgId ForumTopic::lastKnownServerMessageId() const {
|
||||||
|
return _lastKnownServerMessageId;
|
||||||
|
}
|
||||||
|
|
||||||
QString ForumTopic::title() const {
|
QString ForumTopic::title() const {
|
||||||
return _title;
|
return _title;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,7 @@ public:
|
||||||
[[nodiscard]] HistoryItem *lastServerMessage() const;
|
[[nodiscard]] HistoryItem *lastServerMessage() const;
|
||||||
[[nodiscard]] bool lastMessageKnown() const;
|
[[nodiscard]] bool lastMessageKnown() const;
|
||||||
[[nodiscard]] bool lastServerMessageKnown() const;
|
[[nodiscard]] bool lastServerMessageKnown() const;
|
||||||
|
[[nodiscard]] MsgId lastKnownServerMessageId() const;
|
||||||
|
|
||||||
[[nodiscard]] QString title() const;
|
[[nodiscard]] QString title() const;
|
||||||
void applyTitle(const QString &title);
|
void applyTitle(const QString &title);
|
||||||
|
@ -116,6 +117,7 @@ private:
|
||||||
void indexTitleParts();
|
void indexTitleParts();
|
||||||
void validateDefaultIcon() const;
|
void validateDefaultIcon() const;
|
||||||
void applyTopicTopMessage(MsgId topMessageId);
|
void applyTopicTopMessage(MsgId topMessageId);
|
||||||
|
void growLastKnownServerMessageId(MsgId id);
|
||||||
|
|
||||||
void setLastMessage(HistoryItem *item);
|
void setLastMessage(HistoryItem *item);
|
||||||
void setLastServerMessage(HistoryItem *item);
|
void setLastServerMessage(HistoryItem *item);
|
||||||
|
@ -131,6 +133,7 @@ private:
|
||||||
const not_null<Dialogs::MainList*> _list;
|
const not_null<Dialogs::MainList*> _list;
|
||||||
std::shared_ptr<RepliesList> _replies;
|
std::shared_ptr<RepliesList> _replies;
|
||||||
MsgId _rootId = 0;
|
MsgId _rootId = 0;
|
||||||
|
MsgId _lastKnownServerMessageId = 0;
|
||||||
|
|
||||||
PeerNotifySettings _notify;
|
PeerNotifySettings _notify;
|
||||||
|
|
||||||
|
|
|
@ -314,7 +314,15 @@ void PeerData::paintUserpic(
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
userpic->pix(size, size, { .options = rounding }));
|
userpic->pix(size, size, { .options = rounding }));
|
||||||
} else {
|
} else if (isForum()) {
|
||||||
|
ensureEmptyUserpic()->paintRounded(
|
||||||
|
p,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
x + size + x,
|
||||||
|
size,
|
||||||
|
st::roundRadiusLarge);
|
||||||
|
} else{
|
||||||
ensureEmptyUserpic()->paint(p, x, y, x + size + x, size);
|
ensureEmptyUserpic()->paint(p, x, y, x + size + x, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -426,6 +434,9 @@ QImage PeerData::generateUserpicImage(
|
||||||
ensureEmptyUserpic()->paint(p, 0, 0, size, size);
|
ensureEmptyUserpic()->paint(p, 0, 0, size, size);
|
||||||
} else if (radius == ImageRoundRadius::None) {
|
} else if (radius == ImageRoundRadius::None) {
|
||||||
ensureEmptyUserpic()->paintSquare(p, 0, 0, size, size);
|
ensureEmptyUserpic()->paintSquare(p, 0, 0, size, size);
|
||||||
|
} else if (radius == ImageRoundRadius::Large) {
|
||||||
|
const auto radius = st::roundRadiusLarge;
|
||||||
|
ensureEmptyUserpic()->paintRounded(p, 0, 0, size, size, radius);
|
||||||
} else {
|
} else {
|
||||||
ensureEmptyUserpic()->paintRounded(p, 0, 0, size, size);
|
ensureEmptyUserpic()->paintRounded(p, 0, 0, size, size);
|
||||||
}
|
}
|
||||||
|
@ -519,6 +530,10 @@ bool PeerData::canPinMessages() const {
|
||||||
Unexpected("Peer type in PeerData::canPinMessages.");
|
Unexpected("Peer type in PeerData::canPinMessages.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PeerData::canCreateTopics() const {
|
||||||
|
return isForum() && canPinMessages();
|
||||||
|
}
|
||||||
|
|
||||||
bool PeerData::canEditMessagesIndefinitely() const {
|
bool PeerData::canEditMessagesIndefinitely() const {
|
||||||
if (const auto user = asUser()) {
|
if (const auto user = asUser()) {
|
||||||
return user->isSelf();
|
return user->isSelf();
|
||||||
|
|
|
@ -339,7 +339,7 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] bool canPinMessages() const;
|
[[nodiscard]] bool canPinMessages() const;
|
||||||
[[nodiscard]] bool canEditMessagesIndefinitely() const;
|
[[nodiscard]] bool canEditMessagesIndefinitely() const;
|
||||||
|
[[nodiscard]] bool canCreateTopics() const;
|
||||||
[[nodiscard]] bool canExportChatHistory() const;
|
[[nodiscard]] bool canExportChatHistory() const;
|
||||||
|
|
||||||
// Returns true if about text was changed.
|
// Returns true if about text was changed.
|
||||||
|
|
|
@ -18,6 +18,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_messages.h"
|
#include "data/data_messages.h"
|
||||||
#include "data/data_forum.h"
|
#include "data/data_forum.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
|
#include "window/notifications_manager.h"
|
||||||
|
#include "core/application.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
|
||||||
|
@ -25,6 +27,7 @@ namespace Data {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kMessagesPerPage = 50;
|
constexpr auto kMessagesPerPage = 50;
|
||||||
|
constexpr auto kReadRequestTimeout = 3 * crl::time(1000);
|
||||||
|
|
||||||
[[nodiscard]] HistoryService *GenerateDivider(
|
[[nodiscard]] HistoryService *GenerateDivider(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
|
@ -59,7 +62,8 @@ struct RepliesList::Viewer {
|
||||||
RepliesList::RepliesList(not_null<History*> history, MsgId rootId)
|
RepliesList::RepliesList(not_null<History*> history, MsgId rootId)
|
||||||
: _history(history)
|
: _history(history)
|
||||||
, _rootId(rootId)
|
, _rootId(rootId)
|
||||||
, _creating(IsCreating(history, rootId)) {
|
, _creating(IsCreating(history, rootId))
|
||||||
|
, _readRequestTimer([=] { sendReadTillRequest(); }) {
|
||||||
_history->owner().repliesReadTillUpdates(
|
_history->owner().repliesReadTillUpdates(
|
||||||
) | rpl::filter([=](const RepliesReadTillUpdate &update) {
|
) | rpl::filter([=](const RepliesReadTillUpdate &update) {
|
||||||
return (update.id.msg == _rootId)
|
return (update.id.msg == _rootId)
|
||||||
|
@ -92,6 +96,9 @@ RepliesList::RepliesList(not_null<History*> history, MsgId rootId)
|
||||||
RepliesList::~RepliesList() {
|
RepliesList::~RepliesList() {
|
||||||
histories().cancelRequest(base::take(_beforeId));
|
histories().cancelRequest(base::take(_beforeId));
|
||||||
histories().cancelRequest(base::take(_afterId));
|
histories().cancelRequest(base::take(_afterId));
|
||||||
|
if (_readRequestTimer.isActive()) {
|
||||||
|
sendReadTillRequest();
|
||||||
|
}
|
||||||
if (_divider) {
|
if (_divider) {
|
||||||
_divider->destroy();
|
_divider->destroy();
|
||||||
}
|
}
|
||||||
|
@ -753,6 +760,9 @@ MsgId RepliesList::computeOutboxReadTillFull() const {
|
||||||
|
|
||||||
void RepliesList::setUnreadCount(std::optional<int> count) {
|
void RepliesList::setUnreadCount(std::optional<int> count) {
|
||||||
_unreadCount = count;
|
_unreadCount = count;
|
||||||
|
if (!count && !_readRequestTimer.isActive() && !_readRequestId) {
|
||||||
|
reloadUnreadCountIfNeeded();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RepliesList::checkReadTillEnd() {
|
void RepliesList::checkReadTillEnd() {
|
||||||
|
@ -858,4 +868,71 @@ void RepliesList::requestUnreadCount() {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RepliesList::readTill(not_null<HistoryItem*> item) {
|
||||||
|
readTill(item->id, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepliesList::readTill(MsgId tillId) {
|
||||||
|
if (!IsServerMsgId(tillId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
readTill(tillId, _history->owner().message(_history->peer->id, tillId));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepliesList::readTill(
|
||||||
|
MsgId tillId,
|
||||||
|
HistoryItem *tillIdItem) {
|
||||||
|
const auto was = computeInboxReadTillFull();
|
||||||
|
const auto now = tillId;
|
||||||
|
if (now < was) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto unreadCount = computeUnreadCountLocally(now);
|
||||||
|
const auto fast = (tillIdItem && tillIdItem->out()) || !unreadCount.has_value();
|
||||||
|
if (was < now || (fast && now == was)) {
|
||||||
|
setInboxReadTill(now, unreadCount);
|
||||||
|
const auto rootFullId = FullMsgId(_history->peer->id, _rootId);
|
||||||
|
if (const auto root = _history->owner().message(rootFullId)) {
|
||||||
|
if (const auto post = root->lookupDiscussionPostOriginal()) {
|
||||||
|
post->setCommentsInboxReadTill(now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!_readRequestTimer.isActive()) {
|
||||||
|
_readRequestTimer.callOnce(fast ? 0 : kReadRequestTimeout);
|
||||||
|
} else if (fast && _readRequestTimer.remainingTime() > 0) {
|
||||||
|
_readRequestTimer.callOnce(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (const auto topic = _history->peer->forumTopicFor(_rootId)) {
|
||||||
|
Core::App().notifications().clearIncomingFromTopic(topic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepliesList::sendReadTillRequest() {
|
||||||
|
if (_readRequestTimer.isActive()) {
|
||||||
|
_readRequestTimer.cancel();
|
||||||
|
}
|
||||||
|
const auto api = &_history->session().api();
|
||||||
|
api->request(base::take(_readRequestId)).cancel();
|
||||||
|
|
||||||
|
_readRequestId = api->request(MTPmessages_ReadDiscussion(
|
||||||
|
_history->peer->input,
|
||||||
|
MTP_int(_rootId),
|
||||||
|
MTP_int(computeInboxReadTillFull())
|
||||||
|
)).done(crl::guard(this, [=] {
|
||||||
|
_readRequestId = 0;
|
||||||
|
reloadUnreadCountIfNeeded();
|
||||||
|
})).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepliesList::reloadUnreadCountIfNeeded() {
|
||||||
|
if (unreadCountKnown()) {
|
||||||
|
return;
|
||||||
|
} else if (inboxReadTillId() < computeInboxReadTillFull()) {
|
||||||
|
_readRequestTimer.callOnce(0);
|
||||||
|
} else {
|
||||||
|
requestUnreadCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "base/weak_ptr.h"
|
#include "base/weak_ptr.h"
|
||||||
|
#include "base/timer.h"
|
||||||
|
|
||||||
class History;
|
class History;
|
||||||
class HistoryService;
|
class HistoryService;
|
||||||
|
@ -46,6 +47,9 @@ public:
|
||||||
MsgId afterId) const;
|
MsgId afterId) const;
|
||||||
void requestUnreadCount();
|
void requestUnreadCount();
|
||||||
|
|
||||||
|
void readTill(not_null<HistoryItem*> item);
|
||||||
|
void readTill(MsgId tillId);
|
||||||
|
|
||||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||||
return _lifetime;
|
return _lifetime;
|
||||||
}
|
}
|
||||||
|
@ -81,7 +85,10 @@ private:
|
||||||
|
|
||||||
void changeUnreadCountByPost(MsgId id, int delta);
|
void changeUnreadCountByPost(MsgId id, int delta);
|
||||||
void setUnreadCount(std::optional<int> count);
|
void setUnreadCount(std::optional<int> count);
|
||||||
|
void readTill(MsgId tillId, HistoryItem *tillIdItem);
|
||||||
void checkReadTillEnd();
|
void checkReadTillEnd();
|
||||||
|
void sendReadTillRequest();
|
||||||
|
void reloadUnreadCountIfNeeded();
|
||||||
|
|
||||||
const not_null<History*> _history;
|
const not_null<History*> _history;
|
||||||
const MsgId _rootId = 0;
|
const MsgId _rootId = 0;
|
||||||
|
@ -101,6 +108,9 @@ private:
|
||||||
int _beforeId = 0;
|
int _beforeId = 0;
|
||||||
int _afterId = 0;
|
int _afterId = 0;
|
||||||
|
|
||||||
|
base::Timer _readRequestTimer;
|
||||||
|
mtpRequestId _readRequestId = 0;
|
||||||
|
|
||||||
mtpRequestId _reloadUnreadCountRequestId = 0;
|
mtpRequestId _reloadUnreadCountRequestId = 0;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
|
@ -1935,7 +1935,7 @@ void InnerWidget::contextMenuEvent(QContextMenuEvent *e) {
|
||||||
_controller,
|
_controller,
|
||||||
Dialogs::EntryState{
|
Dialogs::EntryState{
|
||||||
.key = row.key,
|
.key = row.key,
|
||||||
.section = Dialogs::EntryState::Section::ChatsList,
|
.section = Dialogs::EntryState::Section::ContextMenu,
|
||||||
.filterId = _filterId,
|
.filterId = _filterId,
|
||||||
},
|
},
|
||||||
addAction);
|
addAction);
|
||||||
|
@ -2394,7 +2394,7 @@ void InnerWidget::refreshEmptyLabel() {
|
||||||
const auto data = &session().data();
|
const auto data = &session().data();
|
||||||
const auto state = !shownDialogs()->empty()
|
const auto state = !shownDialogs()->empty()
|
||||||
? EmptyState::None
|
? EmptyState::None
|
||||||
: _openedForum
|
: (_openedForum && _openedForum->topicsList()->loaded())
|
||||||
? EmptyState::EmptyForum
|
? EmptyState::EmptyForum
|
||||||
: (!_filterId && data->contactsLoaded().current())
|
: (!_filterId && data->contactsLoaded().current())
|
||||||
? EmptyState::NoContacts
|
? EmptyState::NoContacts
|
||||||
|
@ -2415,16 +2415,14 @@ void InnerWidget::refreshEmptyLabel() {
|
||||||
: (state == EmptyState::EmptyFolder)
|
: (state == EmptyState::EmptyFolder)
|
||||||
? tr::lng_no_chats_filter()
|
? tr::lng_no_chats_filter()
|
||||||
: (state == EmptyState::EmptyForum)
|
: (state == EmptyState::EmptyForum)
|
||||||
// #TODO lang-forum
|
? tr::lng_forum_no_topics()
|
||||||
? rpl::single(u"No chats currently created in this forum."_q)
|
|
||||||
: tr::lng_contacts_loading();
|
: tr::lng_contacts_loading();
|
||||||
auto link = (state == EmptyState::NoContacts)
|
auto link = (state == EmptyState::NoContacts)
|
||||||
? tr::lng_add_contact_button()
|
? tr::lng_add_contact_button()
|
||||||
: (state == EmptyState::EmptyFolder)
|
: (state == EmptyState::EmptyFolder)
|
||||||
? tr::lng_filters_context_edit()
|
? tr::lng_filters_context_edit()
|
||||||
: (state == EmptyState::EmptyForum)
|
: (state == EmptyState::EmptyForum)
|
||||||
// #TODO lang-forum
|
? tr::lng_forum_create_topic()
|
||||||
? rpl::single(u"Create topic"_q)
|
|
||||||
: rpl::single(QString());
|
: rpl::single(QString());
|
||||||
auto full = rpl::combine(
|
auto full = rpl::combine(
|
||||||
std::move(phrase),
|
std::move(phrase),
|
||||||
|
@ -3323,16 +3321,9 @@ void InnerWidget::setupShortcuts() {
|
||||||
return jumpToDialogRow(last);
|
return jumpToDialogRow(last);
|
||||||
});
|
});
|
||||||
request->check(Command::ChatSelf) && request->handle([=] {
|
request->check(Command::ChatSelf) && request->handle([=] {
|
||||||
if (_openedForum) {
|
_controller->content()->choosePeer(
|
||||||
_controller->show(Box(
|
session().userPeerId(),
|
||||||
NewForumTopicBox,
|
ShowAtUnreadMsgId);
|
||||||
_controller,
|
|
||||||
_openedForum->history()));
|
|
||||||
} else {
|
|
||||||
_controller->content()->choosePeer(
|
|
||||||
session().userPeerId(),
|
|
||||||
ShowAtUnreadMsgId);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
request->check(Command::ShowArchive) && request->handle([=] {
|
request->check(Command::ShowArchive) && request->handle([=] {
|
||||||
|
|
|
@ -100,6 +100,7 @@ struct EntryState {
|
||||||
Scheduled,
|
Scheduled,
|
||||||
Pinned,
|
Pinned,
|
||||||
Replies,
|
Replies,
|
||||||
|
ContextMenu,
|
||||||
};
|
};
|
||||||
|
|
||||||
Key key;
|
Key key;
|
||||||
|
|
|
@ -639,27 +639,56 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||||
auto prepareTopicCreate = [&](const MTPDmessageActionTopicCreate &action) {
|
auto prepareTopicCreate = [&](const MTPDmessageActionTopicCreate &action) {
|
||||||
auto result = PreparedText{};
|
auto result = PreparedText{};
|
||||||
// #TODO lang-forum
|
// #TODO lang-forum
|
||||||
result.text = { "topic created: " + qs(action.vtitle()) };
|
result.text = { tr::lng_action_topic_created(tr::now) };
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto prepareTopicEdit = [&](const MTPDmessageActionTopicEdit &action) {
|
auto prepareTopicEdit = [&](const MTPDmessageActionTopicEdit &action) {
|
||||||
auto result = PreparedText{};
|
auto result = PreparedText{};
|
||||||
// #TODO lang-forum
|
const auto wrapIcon = [](DocumentId id) {
|
||||||
result.text = { "topic edited: " };
|
return TextWithEntities{
|
||||||
if (const auto icon = action.vicon_emoji_id()) {
|
|
||||||
result.text.append(TextWithEntities{
|
|
||||||
"@",
|
"@",
|
||||||
{ EntityInText(
|
{ EntityInText(
|
||||||
EntityType::CustomEmoji,
|
EntityType::CustomEmoji,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
Data::SerializeCustomEmojiId({ .id = icon->v }))
|
Data::SerializeCustomEmojiId({ .id = id }))
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
};
|
||||||
|
if (const auto closed = action.vclosed()) {
|
||||||
|
result.text = { mtpIsTrue(*closed)
|
||||||
|
? tr::lng_action_topic_closed(tr::now)
|
||||||
|
: tr::lng_action_topic_reopened(tr::now) };
|
||||||
|
} else if (!action.vtitle()) {
|
||||||
|
if (const auto icon = action.vicon_emoji_id()) {
|
||||||
|
if (const auto iconId = icon->v) {
|
||||||
|
result.text = tr::lng_action_topic_icon_changed(
|
||||||
|
tr::now,
|
||||||
|
lt_emoji,
|
||||||
|
wrapIcon(iconId),
|
||||||
|
Ui::Text::WithEntities);
|
||||||
|
} else {
|
||||||
|
result.text = {
|
||||||
|
tr::lng_action_topic_icon_removed(tr::now)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto title = TextWithEntities{
|
||||||
|
qs(*action.vtitle())
|
||||||
|
};
|
||||||
|
if (const auto icon = action.vicon_emoji_id().value_or_empty()) {
|
||||||
|
title = wrapIcon(icon).append(' ').append(std::move(title));
|
||||||
|
}
|
||||||
|
result.text = tr::lng_action_topic_renamed(
|
||||||
|
tr::now,
|
||||||
|
lt_title,
|
||||||
|
std::move(title),
|
||||||
|
Ui::Text::WithEntities);
|
||||||
}
|
}
|
||||||
if (const auto &title = action.vtitle()) {
|
if (result.text.empty()) {
|
||||||
result.text.append(qs(*title));
|
result.text = { tr::lng_message_empty(tr::now) };
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
@ -709,14 +738,10 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||||
return prepareProximityReached(data);
|
return prepareProximityReached(data);
|
||||||
}, [](const MTPDmessageActionPaymentSentMe &) {
|
}, [](const MTPDmessageActionPaymentSentMe &) {
|
||||||
LOG(("API Error: messageActionPaymentSentMe received."));
|
LOG(("API Error: messageActionPaymentSentMe received."));
|
||||||
return PreparedText{
|
return PreparedText{ { tr::lng_message_empty(tr::now) } };
|
||||||
tr::lng_message_empty(tr::now, Ui::Text::WithEntities)
|
|
||||||
};
|
|
||||||
}, [](const MTPDmessageActionSecureValuesSentMe &) {
|
}, [](const MTPDmessageActionSecureValuesSentMe &) {
|
||||||
LOG(("API Error: messageActionSecureValuesSentMe received."));
|
LOG(("API Error: messageActionSecureValuesSentMe received."));
|
||||||
return PreparedText{
|
return PreparedText{ { tr::lng_message_empty(tr::now) } };
|
||||||
tr::lng_message_empty(tr::now, Ui::Text::WithEntities)
|
|
||||||
};
|
|
||||||
}, [&](const MTPDmessageActionGroupCall &data) {
|
}, [&](const MTPDmessageActionGroupCall &data) {
|
||||||
return prepareGroupCall(data);
|
return prepareGroupCall(data);
|
||||||
}, [&](const MTPDmessageActionInviteToGroupCall &data) {
|
}, [&](const MTPDmessageActionInviteToGroupCall &data) {
|
||||||
|
@ -739,13 +764,9 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||||
return prepareTopicEdit(data);
|
return prepareTopicEdit(data);
|
||||||
}, [&](const MTPDmessageActionWebViewDataSentMe &data) {
|
}, [&](const MTPDmessageActionWebViewDataSentMe &data) {
|
||||||
LOG(("API Error: messageActionWebViewDataSentMe received."));
|
LOG(("API Error: messageActionWebViewDataSentMe received."));
|
||||||
return PreparedText{
|
return PreparedText{ { tr::lng_message_empty(tr::now) } };
|
||||||
tr::lng_message_empty(tr::now, Ui::Text::WithEntities)
|
|
||||||
};
|
|
||||||
}, [](const MTPDmessageActionEmpty &) {
|
}, [](const MTPDmessageActionEmpty &) {
|
||||||
return PreparedText{
|
return PreparedText{ { tr::lng_message_empty(tr::now) } };
|
||||||
tr::lng_message_empty(tr::now, Ui::Text::WithEntities)
|
|
||||||
};
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Additional information.
|
// Additional information.
|
||||||
|
|
|
@ -53,7 +53,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/call_delayed.h"
|
#include "base/call_delayed.h"
|
||||||
#include "base/qt/qt_key_modifiers.h"
|
#include "base/qt/qt_key_modifiers.h"
|
||||||
#include "core/file_utilities.h"
|
#include "core/file_utilities.h"
|
||||||
#include "core/application.h"
|
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
@ -70,7 +69,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "inline_bots/inline_bot_result.h"
|
#include "inline_bots/inline_bot_result.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "facades.h"
|
#include "facades.h"
|
||||||
#include "window/notifications_manager.h"
|
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
#include "styles/style_window.h"
|
#include "styles/style_window.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
|
@ -82,7 +80,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kReadRequestTimeout = 3 * crl::time(1000);
|
|
||||||
constexpr auto kRefreshSlowmodeLabelTimeout = crl::time(200);
|
constexpr auto kRefreshSlowmodeLabelTimeout = crl::time(200);
|
||||||
|
|
||||||
bool CanSendFiles(not_null<const QMimeData*> data) {
|
bool CanSendFiles(not_null<const QMimeData*> data) {
|
||||||
|
@ -218,8 +215,7 @@ RepliesWidget::RepliesWidget(
|
||||||
, _cornerButtons(
|
, _cornerButtons(
|
||||||
_scroll.get(),
|
_scroll.get(),
|
||||||
controller->chatStyle(),
|
controller->chatStyle(),
|
||||||
static_cast<HistoryView::CornerButtonsDelegate*>(this))
|
static_cast<HistoryView::CornerButtonsDelegate*>(this)) {
|
||||||
, _readRequestTimer([=] { sendReadTillRequest(); }) {
|
|
||||||
controller->chatStyle()->paletteChanged(
|
controller->chatStyle()->paletteChanged(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
_scroll->updateBars();
|
_scroll->updateBars();
|
||||||
|
@ -359,9 +355,6 @@ RepliesWidget::~RepliesWidget() {
|
||||||
_topic->forum()->discardCreatingId(_rootId);
|
_topic->forum()->discardCreatingId(_rootId);
|
||||||
_topic = nullptr;
|
_topic = nullptr;
|
||||||
}
|
}
|
||||||
if (_readRequestTimer.isActive()) {
|
|
||||||
sendReadTillRequest();
|
|
||||||
}
|
|
||||||
base::take(_sendAction);
|
base::take(_sendAction);
|
||||||
_history->owner().sendActionManager().repliesPainterRemoved(
|
_history->owner().sendActionManager().repliesPainterRemoved(
|
||||||
_history,
|
_history,
|
||||||
|
@ -380,23 +373,6 @@ void RepliesWidget::orderWidgets() {
|
||||||
_composeControls->raisePanels();
|
_composeControls->raisePanels();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RepliesWidget::sendReadTillRequest() {
|
|
||||||
if (_readRequestTimer.isActive()) {
|
|
||||||
_readRequestTimer.cancel();
|
|
||||||
}
|
|
||||||
const auto api = &_history->session().api();
|
|
||||||
api->request(base::take(_readRequestId)).cancel();
|
|
||||||
|
|
||||||
_readRequestId = api->request(MTPmessages_ReadDiscussion(
|
|
||||||
_history->peer->input,
|
|
||||||
MTP_int(_rootId),
|
|
||||||
MTP_int(_replies->computeInboxReadTillFull())
|
|
||||||
)).done(crl::guard(this, [=] {
|
|
||||||
_readRequestId = 0;
|
|
||||||
reloadUnreadCountIfNeeded();
|
|
||||||
})).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RepliesWidget::setupRoot() {
|
void RepliesWidget::setupRoot() {
|
||||||
if (!_root) {
|
if (!_root) {
|
||||||
const auto done = crl::guard(this, [=] {
|
const auto done = crl::guard(this, [=] {
|
||||||
|
@ -1366,19 +1342,6 @@ MsgId RepliesWidget::replyToId() const {
|
||||||
void RepliesWidget::refreshUnreadCountBadge(std::optional<int> count) {
|
void RepliesWidget::refreshUnreadCountBadge(std::optional<int> count) {
|
||||||
if (count.has_value()) {
|
if (count.has_value()) {
|
||||||
_cornerButtons.updateJumpDownVisibility(count);
|
_cornerButtons.updateJumpDownVisibility(count);
|
||||||
} else if (!_readRequestTimer.isActive() && !_readRequestId) {
|
|
||||||
reloadUnreadCountIfNeeded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RepliesWidget::reloadUnreadCountIfNeeded() {
|
|
||||||
if (_replies->unreadCountKnown()) {
|
|
||||||
return;
|
|
||||||
} else if (_replies->inboxReadTillId()
|
|
||||||
< _replies->computeInboxReadTillFull()) {
|
|
||||||
_readRequestTimer.callOnce(0);
|
|
||||||
} else {
|
|
||||||
_replies->requestUnreadCount();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1926,35 +1889,9 @@ void RepliesWidget::listSelectionChanged(SelectedItems &&items) {
|
||||||
_topBar->showSelected(state);
|
_topBar->showSelected(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RepliesWidget::readTill(not_null<HistoryItem*> item) {
|
|
||||||
const auto was = _replies->computeInboxReadTillFull();
|
|
||||||
const auto now = item->id;
|
|
||||||
if (now < was) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto unreadCount = _replies->computeUnreadCountLocally(now);
|
|
||||||
const auto fast = item->out() || !unreadCount.has_value();
|
|
||||||
if (was < now || (fast && now == was)) {
|
|
||||||
_replies->setInboxReadTill(now, unreadCount);
|
|
||||||
if (_root) {
|
|
||||||
if (const auto post = _root->lookupDiscussionPostOriginal()) {
|
|
||||||
post->setCommentsInboxReadTill(now);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!_readRequestTimer.isActive()) {
|
|
||||||
_readRequestTimer.callOnce(fast ? 0 : kReadRequestTimeout);
|
|
||||||
} else if (fast && _readRequestTimer.remainingTime() > 0) {
|
|
||||||
_readRequestTimer.callOnce(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_topic) {
|
|
||||||
Core::App().notifications().clearIncomingFromTopic(_topic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RepliesWidget::listMarkReadTill(not_null<HistoryItem*> item) {
|
void RepliesWidget::listMarkReadTill(not_null<HistoryItem*> item) {
|
||||||
if (true/*doWeReadServerHistory()*/) { // #TODO forum active
|
if (true/*doWeReadServerHistory()*/) { // #TODO forum active
|
||||||
readTill(item);
|
_replies->readTill(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1976,7 +1913,7 @@ MessagesBarData RepliesWidget::listMessagesBar(
|
||||||
const auto item = elements[i]->data();
|
const auto item = elements[i]->data();
|
||||||
if (item->isRegular() && item->id > till) {
|
if (item->isRegular() && item->id > till) {
|
||||||
if (item->out() || !item->replyToId()) {
|
if (item->out() || !item->replyToId()) {
|
||||||
readTill(item);
|
_replies->readTill(item);
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
.bar = {
|
.bar = {
|
||||||
|
|
|
@ -201,8 +201,6 @@ private:
|
||||||
void subscribeToTopic();
|
void subscribeToTopic();
|
||||||
void setTopic(Data::ForumTopic *topic);
|
void setTopic(Data::ForumTopic *topic);
|
||||||
void setupDragArea();
|
void setupDragArea();
|
||||||
void sendReadTillRequest();
|
|
||||||
void readTill(not_null<HistoryItem*> item);
|
|
||||||
|
|
||||||
void scrollDownAnimationFinish();
|
void scrollDownAnimationFinish();
|
||||||
void updatePinnedVisibility();
|
void updatePinnedVisibility();
|
||||||
|
@ -312,9 +310,6 @@ private:
|
||||||
|
|
||||||
bool _choosingAttach = false;
|
bool _choosingAttach = false;
|
||||||
|
|
||||||
base::Timer _readRequestTimer;
|
|
||||||
mtpRequestId _readRequestId = 0;
|
|
||||||
|
|
||||||
bool _loaded = false;
|
bool _loaded = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -980,9 +980,15 @@ void TopBarWidget::updateControlsVisibility() {
|
||||||
const auto hasPollsMenu = _activeChat.key.peer()
|
const auto hasPollsMenu = _activeChat.key.peer()
|
||||||
&& _activeChat.key.peer()->canSendPolls();
|
&& _activeChat.key.peer()->canSendPolls();
|
||||||
const auto hasMenu = !_activeChat.key.folder()
|
const auto hasMenu = !_activeChat.key.folder()
|
||||||
&& ((section == Section::Scheduled || section == Section::Replies)
|
&& (section == Section::History
|
||||||
|
? true
|
||||||
|
: (section == Section::Scheduled)
|
||||||
? hasPollsMenu
|
? hasPollsMenu
|
||||||
: historyMode);
|
: (section == Section::Replies)
|
||||||
|
? (hasPollsMenu || _activeChat.key.topic())
|
||||||
|
: (section == Section::ChatsList)
|
||||||
|
? (_activeChat.key.peer() && _activeChat.key.peer()->isForum())
|
||||||
|
: false);
|
||||||
updateSearchVisibility();
|
updateSearchVisibility();
|
||||||
_menuToggle->setVisible(hasMenu && !_chooseForReportReason);
|
_menuToggle->setVisible(hasMenu && !_chooseForReportReason);
|
||||||
_infoToggle->setVisible(historyMode
|
_infoToggle->setVisible(historyMode
|
||||||
|
|
|
@ -90,7 +90,7 @@ void Widget::setInnerFocus() {
|
||||||
|
|
||||||
rpl::producer<QString> Widget::title() {
|
rpl::producer<QString> Widget::title() {
|
||||||
if (const auto topic = controller()->key().topic()) {
|
if (const auto topic = controller()->key().topic()) {
|
||||||
return rpl::single(u"Topic Info"_q); // #TODO lang-forum
|
return tr::lng_info_topic_title();
|
||||||
}
|
}
|
||||||
const auto peer = controller()->key().peer();
|
const auto peer = controller()->key().peer();
|
||||||
if (const auto user = peer->asUser()) {
|
if (const auto user = peer->asUser()) {
|
||||||
|
|
|
@ -224,19 +224,28 @@ void EmptyUserpic::paint(
|
||||||
int y,
|
int y,
|
||||||
int outerWidth,
|
int outerWidth,
|
||||||
int size) const {
|
int size) const {
|
||||||
paint(p, x, y, outerWidth, size, [&p, x, y, size] {
|
paint(p, x, y, outerWidth, size, [&] {
|
||||||
p.drawEllipse(x, y, size, size);
|
p.drawEllipse(x, y, size, size);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmptyUserpic::paintRounded(QPainter &p, int x, int y, int outerWidth, int size) const {
|
void EmptyUserpic::paintRounded(
|
||||||
paint(p, x, y, outerWidth, size, [&p, x, y, size] {
|
QPainter &p,
|
||||||
p.drawRoundedRect(x, y, size, size, st::roundRadiusSmall, st::roundRadiusSmall);
|
int x,
|
||||||
|
int y,
|
||||||
|
int outerWidth,
|
||||||
|
int size,
|
||||||
|
int radius) const {
|
||||||
|
if (!radius) {
|
||||||
|
radius = st::roundRadiusSmall;
|
||||||
|
}
|
||||||
|
paint(p, x, y, outerWidth, size, [&] {
|
||||||
|
p.drawRoundedRect(x, y, size, size, radius, radius);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmptyUserpic::paintSquare(QPainter &p, int x, int y, int outerWidth, int size) const {
|
void EmptyUserpic::paintSquare(QPainter &p, int x, int y, int outerWidth, int size) const {
|
||||||
paint(p, x, y, outerWidth, size, [&p, x, y, size] {
|
paint(p, x, y, outerWidth, size, [&] {
|
||||||
p.fillRect(x, y, size, size, p.brush());
|
p.fillRect(x, y, size, size, p.brush());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,8 @@ public:
|
||||||
int x,
|
int x,
|
||||||
int y,
|
int y,
|
||||||
int outerWidth,
|
int outerWidth,
|
||||||
int size) const;
|
int size,
|
||||||
|
int radius = 0) const;
|
||||||
void paintSquare(
|
void paintSquare(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
int x,
|
int x,
|
||||||
|
|
|
@ -88,6 +88,7 @@ menuIconPhoto: icon {{ "menu/image", menuIconColor }};
|
||||||
menuIconAddToFolder: icon {{ "menu/add_to_folder", menuIconColor }};
|
menuIconAddToFolder: icon {{ "menu/add_to_folder", menuIconColor }};
|
||||||
menuIconLeave: icon {{ "menu/leave", menuIconColor }};
|
menuIconLeave: icon {{ "menu/leave", menuIconColor }};
|
||||||
menuIconGiftPremium: icon {{ "menu/gift_premium", menuIconColor }};
|
menuIconGiftPremium: icon {{ "menu/gift_premium", menuIconColor }};
|
||||||
|
menuIconSearch: icon {{ "menu/search", menuIconColor }};
|
||||||
|
|
||||||
menuIconTTLAny: icon {{ "menu/auto_delete_plain", menuIconColor }};
|
menuIconTTLAny: icon {{ "menu/auto_delete_plain", menuIconColor }};
|
||||||
menuIconTTLAnyTextPosition: point(11px, 22px);
|
menuIconTTLAnyTextPosition: point(11px, 22px);
|
||||||
|
|
|
@ -63,7 +63,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
#include "data/data_drafts.h"
|
#include "data/data_drafts.h"
|
||||||
|
#include "data/data_forum.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
|
#include "data/data_replies_list.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_scheduled_messages.h"
|
#include "data/data_scheduled_messages.h"
|
||||||
#include "data/data_histories.h"
|
#include "data/data_histories.h"
|
||||||
|
@ -80,6 +82,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kTopicsSearchMinCount = 10;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
const char kOptionViewProfileInChatsListContextMenu[] =
|
const char kOptionViewProfileInChatsListContextMenu[] =
|
||||||
"view-profile-in-chats-list-context-menu";
|
"view-profile-in-chats-list-context-menu";
|
||||||
|
@ -104,20 +111,31 @@ void SetActionText(not_null<QAction*> action, rpl::producer<QString> &&text) {
|
||||||
}, *lifetime);
|
}, *lifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool IsUnreadHistory(not_null<History*> history) {
|
[[nodiscard]] bool IsUnreadThread(not_null<Data::Thread*> thread) {
|
||||||
return (history->chatListUnreadCount() > 0)
|
return (thread->chatListUnreadCount() > 0)
|
||||||
|| (history->chatListUnreadMark());
|
|| (thread->chatListUnreadMark());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarkAsReadHistory(not_null<History*> history) {
|
void MarkAsReadThread(not_null<Data::Thread*> thread) {
|
||||||
const auto read = [&](not_null<History*> history) {
|
const auto readHistory = [&](not_null<History*> history) {
|
||||||
if (IsUnreadHistory(history)) {
|
history->owner().histories().readInbox(history);
|
||||||
history->peer->owner().histories().readInbox(history);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
read(history);
|
if (!IsUnreadThread(thread)) {
|
||||||
if (const auto migrated = history->migrateSibling()) {
|
return;
|
||||||
read(migrated);
|
} else if (const auto history = thread->asHistory()) {
|
||||||
|
if (const auto forum = history->peer->forum()) {
|
||||||
|
forum->enumerateTopics([](
|
||||||
|
not_null<Data::ForumTopic*> topic) {
|
||||||
|
MarkAsReadThread(topic);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
readHistory(history);
|
||||||
|
if (const auto migrated = history->migrateSibling()) {
|
||||||
|
readHistory(migrated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (const auto topic = thread->asTopic()) {
|
||||||
|
topic->replies()->readTill(topic->lastKnownServerMessageId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +146,7 @@ void MarkAsReadChatList(not_null<Dialogs::MainList*> list) {
|
||||||
mark.push_back(history);
|
mark.push_back(history);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ranges::for_each(mark, MarkAsReadHistory);
|
ranges::for_each(mark, MarkAsReadThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerMenuAddMuteSubmenuAction(
|
void PeerMenuAddMuteSubmenuAction(
|
||||||
|
@ -188,6 +206,7 @@ private:
|
||||||
void fillRepliesActions();
|
void fillRepliesActions();
|
||||||
void fillScheduledActions();
|
void fillScheduledActions();
|
||||||
void fillArchiveActions();
|
void fillArchiveActions();
|
||||||
|
void fillContextMenuActions();
|
||||||
|
|
||||||
void addHidePromotion();
|
void addHidePromotion();
|
||||||
void addTogglePin();
|
void addTogglePin();
|
||||||
|
@ -200,6 +219,7 @@ private:
|
||||||
void addClearHistory();
|
void addClearHistory();
|
||||||
void addDeleteChat();
|
void addDeleteChat();
|
||||||
void addLeaveChat();
|
void addLeaveChat();
|
||||||
|
void addJoinChat();
|
||||||
void addManageTopic();
|
void addManageTopic();
|
||||||
void addManageChat();
|
void addManageChat();
|
||||||
void addCreatePoll();
|
void addCreatePoll();
|
||||||
|
@ -216,10 +236,13 @@ private:
|
||||||
void addDeleteContact();
|
void addDeleteContact();
|
||||||
void addTTLSubmenu(bool addSeparator);
|
void addTTLSubmenu(bool addSeparator);
|
||||||
void addGiftPremium();
|
void addGiftPremium();
|
||||||
|
void addCreateTopic();
|
||||||
|
void addSearchTopics();
|
||||||
|
|
||||||
not_null<SessionController*> _controller;
|
not_null<SessionController*> _controller;
|
||||||
Dialogs::EntryState _request;
|
Dialogs::EntryState _request;
|
||||||
Data::Thread *_thread = nullptr;
|
Data::Thread *_thread = nullptr;
|
||||||
|
Data::ForumTopic *_topic = nullptr;
|
||||||
PeerData *_peer = nullptr;
|
PeerData *_peer = nullptr;
|
||||||
Data::Folder *_folder = nullptr;
|
Data::Folder *_folder = nullptr;
|
||||||
const PeerMenuCallback &_addAction;
|
const PeerMenuCallback &_addAction;
|
||||||
|
@ -340,6 +363,7 @@ Filler::Filler(
|
||||||
: _controller(controller)
|
: _controller(controller)
|
||||||
, _request(request)
|
, _request(request)
|
||||||
, _thread(request.key.thread())
|
, _thread(request.key.thread())
|
||||||
|
, _topic(request.key.topic())
|
||||||
, _peer(request.key.peer())
|
, _peer(request.key.peer())
|
||||||
, _folder(request.key.folder())
|
, _folder(request.key.folder())
|
||||||
, _addAction(addAction) {
|
, _addAction(addAction) {
|
||||||
|
@ -347,7 +371,8 @@ Filler::Filler(
|
||||||
|
|
||||||
void Filler::addHidePromotion() {
|
void Filler::addHidePromotion() {
|
||||||
const auto history = _request.key.history();
|
const auto history = _request.key.history();
|
||||||
if (!history
|
if (_topic
|
||||||
|
|| !history
|
||||||
|| !history->useTopPromotion()
|
|| !history->useTopPromotion()
|
||||||
|| history->topPromotionType().isEmpty()) {
|
|| history->topPromotionType().isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -361,14 +386,14 @@ void Filler::addHidePromotion() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filler::addTogglePin() {
|
void Filler::addTogglePin() {
|
||||||
if (!_peer) {
|
if (!_peer || _topic) {
|
||||||
// #TODO forum pinned
|
// #TODO forum pinned
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto controller = _controller;
|
const auto controller = _controller;
|
||||||
const auto filterId = _request.filterId;
|
const auto filterId = _request.filterId;
|
||||||
const auto peer = _peer;
|
const auto peer = _peer;
|
||||||
const auto history = peer->owner().historyLoaded(peer);
|
const auto history = _request.key.history();
|
||||||
if (!history || history->fixedOnTopIndex()) {
|
if (!history || history->fixedOnTopIndex()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -435,7 +460,7 @@ void Filler::addInfo() {
|
||||||
const auto controller = _controller;
|
const auto controller = _controller;
|
||||||
const auto weak = base::make_weak(_thread);
|
const auto weak = base::make_weak(_thread);
|
||||||
const auto text = _thread->asTopic()
|
const auto text = _thread->asTopic()
|
||||||
? u"View Topic Info"_q // #TODO lang-forum
|
? tr::lng_context_view_topic(tr::now)
|
||||||
: (_peer->isChat() || _peer->isMegagroup())
|
: (_peer->isChat() || _peer->isMegagroup())
|
||||||
? tr::lng_context_view_group(tr::now)
|
? tr::lng_context_view_group(tr::now)
|
||||||
: _peer->isUser()
|
: _peer->isUser()
|
||||||
|
@ -451,7 +476,7 @@ void Filler::addInfo() {
|
||||||
void Filler::addToggleFolder() {
|
void Filler::addToggleFolder() {
|
||||||
const auto controller = _controller;
|
const auto controller = _controller;
|
||||||
const auto history = _request.key.history();
|
const auto history = _request.key.history();
|
||||||
if (!history || !history->owner().chatsFilters().has()) {
|
if (_topic || !history || !history->owner().chatsFilters().has()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_addAction(PeerMenuCallback::Args{
|
_addAction(PeerMenuCallback::Args{
|
||||||
|
@ -466,38 +491,37 @@ void Filler::addToggleFolder() {
|
||||||
|
|
||||||
void Filler::addToggleUnreadMark() {
|
void Filler::addToggleUnreadMark() {
|
||||||
const auto peer = _peer;
|
const auto peer = _peer;
|
||||||
const auto history = peer->owner().history(peer);
|
const auto history = _request.key.history();
|
||||||
const auto label = [=] {
|
if (!_thread) {
|
||||||
return IsUnreadHistory(history)
|
return;
|
||||||
? tr::lng_context_mark_read(tr::now)
|
}
|
||||||
: tr::lng_context_mark_unread(tr::now);
|
const auto unread = IsUnreadThread(_thread);
|
||||||
};
|
if (_thread->asTopic() && !unread) {
|
||||||
auto action = _addAction(label(), [=] {
|
return;
|
||||||
const auto markAsRead = IsUnreadHistory(history);
|
}
|
||||||
if (markAsRead) {
|
const auto weak = base::make_weak(_thread);
|
||||||
MarkAsReadHistory(history);
|
const auto label = unread
|
||||||
} else {
|
? tr::lng_context_mark_read(tr::now)
|
||||||
peer->owner().histories().changeDialogUnreadMark(
|
: tr::lng_context_mark_unread(tr::now);
|
||||||
history,
|
_addAction(label, [=] {
|
||||||
!markAsRead);
|
const auto thread = weak.get();
|
||||||
|
if (!thread) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}, (IsUnreadHistory(history)
|
if (unread) {
|
||||||
? &st::menuIconMarkRead
|
MarkAsReadThread(thread);
|
||||||
: &st::menuIconMarkUnread));
|
} else if (history) {
|
||||||
|
peer->owner().histories().changeDialogUnreadMark(history, true);
|
||||||
auto actionText = history->session().changes().historyUpdates(
|
}
|
||||||
history,
|
}, (unread ? &st::menuIconMarkRead : &st::menuIconMarkUnread));
|
||||||
Data::HistoryUpdate::Flag::UnreadView
|
|
||||||
) | rpl::map(label);
|
|
||||||
SetActionText(action, std::move(actionText));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filler::addToggleArchive() {
|
void Filler::addToggleArchive() {
|
||||||
if (!_peer) {
|
if (!_peer || _topic) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto peer = _peer;
|
const auto peer = _peer;
|
||||||
const auto history = peer->owner().historyLoaded(peer);
|
const auto history = _request.key.history();
|
||||||
if (history && history->useTopPromotion()) {
|
if (history && history->useTopPromotion()) {
|
||||||
return;
|
return;
|
||||||
} else if (peer->isNotificationsUser() || peer->isSelf()) {
|
} else if (peer->isNotificationsUser() || peer->isSelf()) {
|
||||||
|
@ -529,6 +553,9 @@ void Filler::addToggleArchive() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filler::addClearHistory() {
|
void Filler::addClearHistory() {
|
||||||
|
if (_topic) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auto channel = _peer->asChannel();
|
const auto channel = _peer->asChannel();
|
||||||
const auto isGroup = _peer->isChat() || _peer->isMegagroup();
|
const auto isGroup = _peer->isChat() || _peer->isMegagroup();
|
||||||
if (channel) {
|
if (channel) {
|
||||||
|
@ -546,7 +573,7 @@ void Filler::addClearHistory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filler::addDeleteChat() {
|
void Filler::addDeleteChat() {
|
||||||
if (_peer->isChannel()) {
|
if (_topic || _peer->isChannel()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_addAction({
|
_addAction({
|
||||||
|
@ -561,7 +588,7 @@ void Filler::addDeleteChat() {
|
||||||
|
|
||||||
void Filler::addLeaveChat() {
|
void Filler::addLeaveChat() {
|
||||||
const auto channel = _peer->asChannel();
|
const auto channel = _peer->asChannel();
|
||||||
if (_thread->asTopic() || !channel || !channel->amIn()) {
|
if (_topic || !channel || !channel->amIn()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_addAction({
|
_addAction({
|
||||||
|
@ -574,6 +601,19 @@ void Filler::addLeaveChat() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Filler::addJoinChat() {
|
||||||
|
const auto channel = _peer->asChannel();
|
||||||
|
if (_topic || !channel || channel->amIn()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto label = _peer->isMegagroup()
|
||||||
|
? tr::lng_profile_join_group(tr::now)
|
||||||
|
: tr::lng_profile_join_channel(tr::now);
|
||||||
|
_addAction(label, [=] {
|
||||||
|
channel->session().api().joinChannel(channel);
|
||||||
|
}, &st::menuIconAddToFolder);
|
||||||
|
}
|
||||||
|
|
||||||
void Filler::addBlockUser() {
|
void Filler::addBlockUser() {
|
||||||
const auto user = _peer->asUser();
|
const auto user = _peer->asUser();
|
||||||
if (!user
|
if (!user
|
||||||
|
@ -766,11 +806,10 @@ void Filler::addManageTopic() {
|
||||||
if (!topic) {
|
if (!topic) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// #TODO lang-forum
|
|
||||||
const auto history = topic->history();
|
const auto history = topic->history();
|
||||||
const auto rootId = topic->rootId();
|
const auto rootId = topic->rootId();
|
||||||
const auto navigation = _controller;
|
const auto navigation = _controller;
|
||||||
_addAction(u"Edit topic"_q, [=] {
|
_addAction(tr::lng_forum_topic_edit(tr::now), [=] {
|
||||||
navigation->show(
|
navigation->show(
|
||||||
Box(EditForumTopicBox, navigation, history, rootId));
|
Box(EditForumTopicBox, navigation, history, rootId));
|
||||||
}, &st::menuIconEdit);
|
}, &st::menuIconEdit);
|
||||||
|
@ -885,11 +924,53 @@ void Filler::fill() {
|
||||||
case Section::Profile: fillProfileActions(); break;
|
case Section::Profile: fillProfileActions(); break;
|
||||||
case Section::Replies: fillRepliesActions(); break;
|
case Section::Replies: fillRepliesActions(); break;
|
||||||
case Section::Scheduled: fillScheduledActions(); break;
|
case Section::Scheduled: fillScheduledActions(); break;
|
||||||
|
case Section::ContextMenu: fillContextMenuActions(); break;
|
||||||
default: Unexpected("_request.section in Filler::fill.");
|
default: Unexpected("_request.section in Filler::fill.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Filler::addCreateTopic() {
|
||||||
|
if (!_peer || !_peer->canCreateTopics()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto peer = _peer;
|
||||||
|
const auto controller = _controller;
|
||||||
|
_addAction(tr::lng_forum_create_topic(tr::now), [=] {
|
||||||
|
if (const auto forum = peer->forum()) {
|
||||||
|
controller->show(Box(
|
||||||
|
NewForumTopicBox,
|
||||||
|
controller,
|
||||||
|
forum->history()));
|
||||||
|
}
|
||||||
|
}, &st::menuIconDiscussion);
|
||||||
|
_addAction(PeerMenuCallback::Args{ .isSeparator = true });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filler::addSearchTopics() {
|
||||||
|
_addAction(tr::lng_dlg_filter(tr::now), [=] {
|
||||||
|
|
||||||
|
}, &st::menuIconSearch);
|
||||||
|
}
|
||||||
|
|
||||||
void Filler::fillChatsListActions() {
|
void Filler::fillChatsListActions() {
|
||||||
|
if (!_peer || !_peer->isForum()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addCreateTopic();
|
||||||
|
addInfo();
|
||||||
|
addNewMembers();
|
||||||
|
const auto &all = _peer->forum()->topicsList()->indexed()->all();
|
||||||
|
if (all.size() > kTopicsSearchMinCount) {
|
||||||
|
addSearchTopics();
|
||||||
|
}
|
||||||
|
if (_peer->asChannel()->amIn()) {
|
||||||
|
addLeaveChat();
|
||||||
|
} else {
|
||||||
|
addJoinChat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filler::fillContextMenuActions() {
|
||||||
addHidePromotion();
|
addHidePromotion();
|
||||||
addToggleArchive();
|
addToggleArchive();
|
||||||
addTogglePin();
|
addTogglePin();
|
||||||
|
|
Loading…
Add table
Reference in a new issue