Combine notification updates.
GitOrigin-RevId: 2e9d85f46a7e4de568ca8d80a806ebadf62e67ef
This commit is contained in:
parent
1f40a2dc78
commit
12e779bc99
@ -15,6 +15,7 @@
|
||||
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <tuple>
|
||||
|
||||
namespace td {
|
||||
@ -24,6 +25,9 @@ int VERBOSITY_NAME(notifications) = VERBOSITY_NAME(WARNING);
|
||||
NotificationManager::NotificationManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
|
||||
flush_pending_notifications_timeout_.set_callback(on_flush_pending_notifications_timeout_callback);
|
||||
flush_pending_notifications_timeout_.set_callback_data(static_cast<void *>(this));
|
||||
|
||||
flush_pending_updates_timeout_.set_callback(on_flush_pending_updates_timeout_callback);
|
||||
flush_pending_updates_timeout_.set_callback_data(static_cast<void *>(this));
|
||||
}
|
||||
|
||||
void NotificationManager::on_flush_pending_notifications_timeout_callback(void *notification_manager_ptr,
|
||||
@ -38,6 +42,17 @@ void NotificationManager::on_flush_pending_notifications_timeout_callback(void *
|
||||
NotificationGroupId(narrow_cast<int32>(group_id_int)));
|
||||
}
|
||||
|
||||
void NotificationManager::on_flush_pending_updates_timeout_callback(void *notification_manager_ptr,
|
||||
int64 group_id_int) {
|
||||
if (G()->close_flag()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto notification_manager = static_cast<NotificationManager *>(notification_manager_ptr);
|
||||
send_closure_later(notification_manager->actor_id(notification_manager), &NotificationManager::flush_pending_updates,
|
||||
narrow_cast<int32>(group_id_int));
|
||||
}
|
||||
|
||||
bool NotificationManager::is_disabled() const {
|
||||
return td_->auth_manager_->is_bot();
|
||||
}
|
||||
@ -203,24 +218,186 @@ td_api::object_ptr<td_api::notification> NotificationManager::get_notification_o
|
||||
notification.type->get_notification_type_object(dialog_id));
|
||||
}
|
||||
|
||||
void NotificationManager::send_update_notification_group(td_api::object_ptr<td_api::updateNotificationGroup> update) {
|
||||
// TODO delay and combine updates while getDifference is running
|
||||
VLOG(notifications) << "Send " << to_string(update);
|
||||
send_closure(G()->td(), &Td::send_update, std::move(update));
|
||||
void NotificationManager::add_update(int32 group_id, td_api::object_ptr<td_api::Update> update) {
|
||||
// TODO delay updates while getDifference is running
|
||||
// flush updates when getDifference or getChannelDiference is finished
|
||||
VLOG(notifications) << "Add " << to_string(update);
|
||||
pending_updates_[group_id].push_back(std::move(update));
|
||||
if (!running_get_difference_) {
|
||||
flush_pending_updates_timeout_.add_timeout_in(group_id, MIN_UPDATE_DELAY_MS * 1e-3);
|
||||
} else {
|
||||
flush_pending_updates_timeout_.set_timeout_in(group_id, MAX_UPDATE_DELAY_MS * 1e-3);
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::send_update_notification(NotificationGroupId notification_group_id, DialogId dialog_id,
|
||||
const Notification ¬ification) {
|
||||
void NotificationManager::add_update_notification_group(td_api::object_ptr<td_api::updateNotificationGroup> update) {
|
||||
auto group_id = update->notification_group_id_;
|
||||
if (update->notification_settings_chat_id_ == 0) {
|
||||
update->notification_settings_chat_id_ = update->chat_id_;
|
||||
}
|
||||
add_update(group_id, std::move(update));
|
||||
}
|
||||
|
||||
void NotificationManager::add_update_notification(NotificationGroupId notification_group_id, DialogId dialog_id,
|
||||
const Notification ¬ification) {
|
||||
auto notification_object = get_notification_object(dialog_id, notification);
|
||||
if (notification_object->type_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO delay and combine updates while getDifference is running
|
||||
auto update =
|
||||
td_api::make_object<td_api::updateNotification>(notification_group_id.get(), std::move(notification_object));
|
||||
VLOG(notifications) << "Send " << to_string(update);
|
||||
send_closure(G()->td(), &Td::send_update, std::move(update));
|
||||
add_update(notification_group_id.get(), td_api::make_object<td_api::updateNotification>(
|
||||
notification_group_id.get(), std::move(notification_object)));
|
||||
}
|
||||
|
||||
void NotificationManager::flush_pending_updates(int32 group_id) {
|
||||
auto it = pending_updates_.find(group_id);
|
||||
if (it == pending_updates_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
VLOG(notifications) << "Send pending updates in " << NotificationGroupId(group_id);
|
||||
|
||||
auto updates = std::move(it->second);
|
||||
pending_updates_.erase(it);
|
||||
|
||||
std::unordered_map<int32, size_t> notification_pos;
|
||||
size_t cur_pos = 1;
|
||||
for (auto &update : updates) {
|
||||
if (update->get_id() == td_api::updateNotificationGroup::ID) {
|
||||
auto update_ptr = static_cast<td_api::updateNotificationGroup *>(update.get());
|
||||
bool is_deletion = !update_ptr->removed_notification_ids_.empty() &&
|
||||
(update_ptr->new_notifications_.empty() ||
|
||||
update_ptr->new_notifications_.back()->id_ < update_ptr->removed_notification_ids_[0]);
|
||||
|
||||
for (auto ¬ification : update_ptr->new_notifications_) {
|
||||
auto notification_id = notification->id_;
|
||||
auto &pos = notification_pos[notification_id];
|
||||
CHECK(pos < cur_pos);
|
||||
if (pos != 0) {
|
||||
// this notification was deleted by previous update, but we can't remove the deletion or the addition
|
||||
}
|
||||
pos = cur_pos;
|
||||
}
|
||||
for (auto ¬ification_id : update_ptr->removed_notification_ids_) {
|
||||
auto &pos = notification_pos[notification_id];
|
||||
CHECK(pos < cur_pos);
|
||||
if (pos == 0 || !is_deletion) {
|
||||
pos = cur_pos;
|
||||
} else {
|
||||
// this notification was added by previous update, we can remove the addition and the deletion
|
||||
auto &previous_update = updates[pos - 1];
|
||||
CHECK(previous_update != nullptr);
|
||||
|
||||
if (previous_update->get_id() == td_api::updateNotificationGroup::ID) {
|
||||
auto previous_update_ptr = static_cast<td_api::updateNotificationGroup *>(previous_update.get());
|
||||
bool found = false;
|
||||
size_t i = 0;
|
||||
for (auto ¬ification : previous_update_ptr->new_notifications_) {
|
||||
if (notification->id_ == notification_id) {
|
||||
previous_update_ptr->new_notifications_.erase(previous_update_ptr->new_notifications_.begin() + i);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
CHECK(found); // there should be no deletions without previous addition
|
||||
if (previous_update_ptr->new_notifications_.empty() &&
|
||||
previous_update_ptr->removed_notification_ids_.empty()) {
|
||||
previous_update = nullptr;
|
||||
}
|
||||
} else {
|
||||
auto previous_update_ptr = static_cast<td_api::updateNotification *>(previous_update.get());
|
||||
CHECK(previous_update_ptr->notification_->id_ == notification_id);
|
||||
previous_update = nullptr;
|
||||
}
|
||||
|
||||
notification_id = 0;
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
update_ptr->removed_notification_ids_.erase(
|
||||
std::remove_if(update_ptr->removed_notification_ids_.begin(), update_ptr->removed_notification_ids_.end(),
|
||||
[](auto ¬ification_id) { return notification_id == 0; }),
|
||||
update_ptr->removed_notification_ids_.end());
|
||||
if (update_ptr->removed_notification_ids_.empty() && update_ptr->new_notifications_.empty()) {
|
||||
update = nullptr;
|
||||
}
|
||||
} else {
|
||||
auto update_ptr = static_cast<td_api::updateNotification *>(update.get());
|
||||
auto notification_id = update_ptr->notification_->id_;
|
||||
auto &pos = notification_pos[notification_id];
|
||||
if (pos == 0) {
|
||||
pos = cur_pos;
|
||||
} else {
|
||||
VLOG(notifications) << "Previous update with " << notification_id
|
||||
<< " is not sent, so we can edit the notification in-place";
|
||||
auto type = std::move(update_ptr->notification_->type_);
|
||||
auto &previous_update = updates[pos - 1];
|
||||
CHECK(previous_update != nullptr);
|
||||
|
||||
if (previous_update->get_id() == td_api::updateNotificationGroup::ID) {
|
||||
auto previous_update_ptr = static_cast<td_api::updateNotificationGroup *>(previous_update.get());
|
||||
bool found = false;
|
||||
for (auto ¬ification : previous_update_ptr->new_notifications_) {
|
||||
if (notification->id_ == notification_id) {
|
||||
notification->type_ = std::move(type);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK(found); // there should be no update about editing of deleted message
|
||||
} else {
|
||||
auto previous_update_ptr = static_cast<td_api::updateNotification *>(previous_update.get());
|
||||
CHECK(previous_update_ptr->notification_->id_ == notification_id);
|
||||
previous_update_ptr->notification_->type_ = std::move(type);
|
||||
}
|
||||
|
||||
update = nullptr;
|
||||
}
|
||||
}
|
||||
cur_pos++;
|
||||
}
|
||||
|
||||
updates.erase(std::remove_if(updates.begin(), updates.end(), [](auto &update) { return update == nullptr; }),
|
||||
updates.end());
|
||||
if (updates.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t last_update_pos = 0;
|
||||
for (size_t i = 1; i < updates.size(); i++) {
|
||||
if (updates[last_update_pos]->get_id() == td_api::updateNotificationGroup::ID &&
|
||||
updates[i]->get_id() == td_api::updateNotificationGroup::ID) {
|
||||
auto last_update_ptr = static_cast<td_api::updateNotificationGroup *>(updates[last_update_pos].get());
|
||||
auto update_ptr = static_cast<td_api::updateNotificationGroup *>(updates[i].get());
|
||||
if (last_update_ptr->notification_settings_chat_id_ == update_ptr->notification_settings_chat_id_ &&
|
||||
last_update_ptr->is_silent_ == update_ptr->is_silent_) {
|
||||
if ((last_update_ptr->new_notifications_.empty() && update_ptr->new_notifications_.empty()) ||
|
||||
(last_update_ptr->removed_notification_ids_.empty() && update_ptr->removed_notification_ids_.empty())) {
|
||||
// combine updates
|
||||
VLOG(notifications) << "Combine " << to_string(*last_update_ptr) << " and " << to_string(*update_ptr);
|
||||
CHECK(last_update_ptr->notification_group_id_ == update_ptr->notification_group_id_);
|
||||
CHECK(last_update_ptr->chat_id_ == update_ptr->chat_id_);
|
||||
last_update_ptr->total_count_ = update_ptr->total_count_;
|
||||
append(last_update_ptr->new_notifications_, std::move(update_ptr->new_notifications_));
|
||||
append(last_update_ptr->removed_notification_ids_, std::move(update_ptr->removed_notification_ids_));
|
||||
updates[i] = nullptr;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
last_update_pos++;
|
||||
if (last_update_pos != i) {
|
||||
updates[last_update_pos] = std::move(updates[i]);
|
||||
}
|
||||
}
|
||||
updates.resize(last_update_pos + 1);
|
||||
|
||||
for (auto &update : updates) {
|
||||
VLOG(notifications) << "Send " << to_string(update);
|
||||
send_closure(G()->td(), &Td::send_update, std::move(update));
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::do_flush_pending_notifications(NotificationGroupKey &group_key, NotificationGroup &group,
|
||||
@ -267,7 +444,7 @@ void NotificationManager::do_flush_pending_notifications(NotificationGroupKey &g
|
||||
}
|
||||
|
||||
if (!added_notifications.empty()) {
|
||||
send_update_notification_group(td_api::make_object<td_api::updateNotificationGroup>(
|
||||
add_update_notification_group(td_api::make_object<td_api::updateNotificationGroup>(
|
||||
group_key.group_id.get(), group_key.dialog_id.get(), pending_notifications[0].settings_dialog_id.get(),
|
||||
pending_notifications[0].is_silent, group.total_count, std::move(added_notifications),
|
||||
std::move(removed_notification_ids)));
|
||||
@ -290,7 +467,7 @@ void NotificationManager::send_remove_group_update(const NotificationGroupKey &g
|
||||
}
|
||||
|
||||
if (!removed_notification_ids.empty()) {
|
||||
send_update_notification_group(td_api::make_object<td_api::updateNotificationGroup>(
|
||||
add_update_notification_group(td_api::make_object<td_api::updateNotificationGroup>(
|
||||
group_key.group_id.get(), group_key.dialog_id.get(), group_key.dialog_id.get(), true, 0,
|
||||
vector<td_api::object_ptr<td_api::notification>>(), std::move(removed_notification_ids)));
|
||||
}
|
||||
@ -310,7 +487,7 @@ void NotificationManager::send_add_group_update(const NotificationGroupKey &grou
|
||||
}
|
||||
|
||||
if (!added_notifications.empty()) {
|
||||
send_update_notification_group(
|
||||
add_update_notification_group(
|
||||
td_api::make_object<td_api::updateNotificationGroup>(group_key.group_id.get(), group_key.dialog_id.get(), 0,
|
||||
true, 0, std::move(added_notifications), vector<int32>()));
|
||||
}
|
||||
@ -408,7 +585,7 @@ void NotificationManager::edit_notification(NotificationGroupId group_id, Notifi
|
||||
if (notification.notification_id == notification_id) {
|
||||
notification.type = std::move(type);
|
||||
if (i + max_notification_group_size_ >= group.notifications.size()) {
|
||||
send_update_notification(group_it->first.group_id, group_it->first.dialog_id, notification);
|
||||
add_update_notification(group_it->first.group_id, group_it->first.dialog_id, notification);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -456,7 +633,7 @@ void NotificationManager::on_notifications_removed(
|
||||
} else {
|
||||
if (is_updated) {
|
||||
// group is still visible
|
||||
send_update_notification_group(td_api::make_object<td_api::updateNotificationGroup>(
|
||||
add_update_notification_group(td_api::make_object<td_api::updateNotificationGroup>(
|
||||
group_key.group_id.get(), group_key.dialog_id.get(), 0, true, group.total_count,
|
||||
std::move(added_notifications), std::move(removed_notification_ids)));
|
||||
} else {
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace td {
|
||||
|
||||
@ -77,6 +78,9 @@ class NotificationManager : public Actor {
|
||||
|
||||
static constexpr int32 MIN_NOTIFICATION_DELAY_MS = 1;
|
||||
|
||||
static constexpr int32 MIN_UPDATE_DELAY_MS = 50;
|
||||
static constexpr int32 MAX_UPDATE_DELAY_MS = 60000;
|
||||
|
||||
struct Notification {
|
||||
NotificationId notification_id;
|
||||
int32 date = 0;
|
||||
@ -128,6 +132,8 @@ class NotificationManager : public Actor {
|
||||
|
||||
static void on_flush_pending_notifications_timeout_callback(void *notification_manager_ptr, int64 group_id_int);
|
||||
|
||||
static void on_flush_pending_updates_timeout_callback(void *notification_manager_ptr, int64 group_id_int);
|
||||
|
||||
bool is_disabled() const;
|
||||
|
||||
void start_up() override;
|
||||
@ -136,10 +142,12 @@ class NotificationManager : public Actor {
|
||||
static td_api::object_ptr<td_api::notification> get_notification_object(DialogId dialog_id,
|
||||
const Notification ¬ification);
|
||||
|
||||
void send_update_notification_group(td_api::object_ptr<td_api::updateNotificationGroup> update);
|
||||
void add_update(int32 group_id, td_api::object_ptr<td_api::Update> update);
|
||||
|
||||
void send_update_notification(NotificationGroupId notification_group_id, DialogId dialog_id,
|
||||
const Notification ¬ification);
|
||||
void add_update_notification_group(td_api::object_ptr<td_api::updateNotificationGroup> update);
|
||||
|
||||
void add_update_notification(NotificationGroupId notification_group_id, DialogId dialog_id,
|
||||
const Notification ¬ification);
|
||||
|
||||
NotificationGroups::iterator get_group(NotificationGroupId group_id);
|
||||
|
||||
@ -161,6 +169,8 @@ class NotificationManager : public Actor {
|
||||
vector<td_api::object_ptr<td_api::notification>> &&added_notifications,
|
||||
vector<int32> &&removed_notification_ids);
|
||||
|
||||
void flush_pending_updates(int32 group_id);
|
||||
|
||||
NotificationId current_notification_id_;
|
||||
NotificationGroupId current_notification_group_id_;
|
||||
|
||||
@ -172,9 +182,14 @@ class NotificationManager : public Actor {
|
||||
int32 notification_cloud_delay_ms_ = DEFAULT_ONLINE_CLOUD_DELAY_MS;
|
||||
int32 notification_default_delay_ms_ = DEFAULT_DEFAULT_DELAY_MS;
|
||||
|
||||
bool running_get_difference_ = false;
|
||||
|
||||
NotificationGroups groups_;
|
||||
|
||||
std::unordered_map<int32, vector<td_api::object_ptr<td_api::Update>>> pending_updates_;
|
||||
|
||||
MultiTimeout flush_pending_notifications_timeout_{"FlushPendingNotificationsTimeout"};
|
||||
MultiTimeout flush_pending_updates_timeout_{"FlushPendingUpdatesTimeout"};
|
||||
|
||||
Td *td_;
|
||||
ActorShared<> parent_;
|
||||
|
Reference in New Issue
Block a user