diff options
author | Nicolas Werner <nicolas.werner@hotmail.de> | 2023-03-14 03:02:54 +0100 |
---|---|---|
committer | Nicolas Werner <nicolas.werner@hotmail.de> | 2023-03-14 18:16:08 +0100 |
commit | 234ac79acc5d4ea1566b1b587c866e30f8cdfe83 (patch) | |
tree | cbfaac61845a32d352c298796bafac3152651b85 | |
parent | Speed up writes of events with statekeys to state db (diff) | |
download | nheko-234ac79acc5d4ea1566b1b587c866e30f8cdfe83.tar.xz |
Update spaces events automatically in the background
-rw-r--r-- | src/ChatPage.cpp | 13 | ||||
-rw-r--r-- | src/ChatPage.h | 4 | ||||
-rw-r--r-- | src/PowerlevelsEditModels.cpp | 9 | ||||
-rw-r--r-- | src/UserSettingsPage.cpp | 35 | ||||
-rw-r--r-- | src/UserSettingsPage.h | 7 | ||||
-rw-r--r-- | src/Utils.cpp | 180 | ||||
-rw-r--r-- | src/Utils.h | 3 |
7 files changed, 246 insertions, 5 deletions
diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 37449980..b5c8d3b4 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -95,6 +95,12 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QObject *parent) return; } + // only update spaces every 20 minutes + if (lastSpacesUpdate < QDateTime::currentDateTime().addSecs(-20 * 60)) { + lastSpacesUpdate = QDateTime::currentDateTime(); + utils::updateSpaceVias(); + } + if (!isConnected_) emit connectionRestored(); }); @@ -380,6 +386,13 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QObject *parent) }, Qt::QueuedConnection); + connect( + this, + &ChatPage::callFunctionOnGuiThread, + this, + [](std::function<void()> f) { f(); }, + Qt::QueuedConnection); + connectCallMessage<mtx::events::voip::CallInvite>(); connectCallMessage<mtx::events::voip::CallCandidates>(); connectCallMessage<mtx::events::voip::CallAnswer>(); diff --git a/src/ChatPage.h b/src/ChatPage.h index 2c707be1..fbf4fbce 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -175,6 +175,8 @@ signals: bool promptForConfirmation = true); void newOnlineKeyBackupAvailable(); + void callFunctionOnGuiThread(std::function<void()>); + private slots: void logout(); void removeRoom(const QString &room_id); @@ -221,6 +223,8 @@ private: CallManager *callManager_; std::unique_ptr<mtx::pushrules::PushRuleEvaluator> pushrules; + + QDateTime lastSpacesUpdate = QDateTime::currentDateTime(); }; template<class Collection> diff --git a/src/PowerlevelsEditModels.cpp b/src/PowerlevelsEditModels.cpp index 720ed338..f0fd9194 100644 --- a/src/PowerlevelsEditModels.cpp +++ b/src/PowerlevelsEditModels.cpp @@ -727,9 +727,12 @@ struct PowerLevelApplier [self = *this](const mtx::responses::EventId &, mtx::http::RequestErr e) mutable { if (e) { if (e->status_code == 429 && e->matrix_error.retry_after.count() != 0) { - QTimer::singleShot(e->matrix_error.retry_after, - ChatPage::instance(), - [self = std::move(self)]() mutable { self.next(); }); + ChatPage::instance()->callFunctionOnGuiThread( + [self = std::move(self), interval = e->matrix_error.retry_after]() { + QTimer::singleShot(interval, + ChatPage::instance(), + [self = std::move(self)]() mutable { self.next(); }); + }); return; } diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index 7527c17f..dac9aef2 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -98,6 +98,8 @@ UserSettings::load(std::optional<QString> profile) privacyScreenTimeout_ = settings.value(QStringLiteral("user/privacy_screen_timeout"), 0).toInt(); exposeDBusApi_ = settings.value(QStringLiteral("user/expose_dbus_api"), false).toBool(); + updateSpaceVias_ = + settings.value(QStringLiteral("user/space_background_maintenance"), true).toBool(); mobileMode_ = settings.value(QStringLiteral("user/mobile_mode"), false).toBool(); emojiFont_ = settings.value(QStringLiteral("user/emoji_font_family"), "emoji").toString(); @@ -294,6 +296,17 @@ UserSettings::setExposeDBusApi(bool state) } void +UserSettings::setUpdateSpaceVias(bool state) +{ + if (updateSpaceVias_ == state) + return; + + updateSpaceVias_ = state; + emit updateSpaceViasChanged(state); + save(); +} + +void UserSettings::setMarkdown(bool state) { if (state == markdown_) @@ -901,6 +914,7 @@ UserSettings::save() settings.setValue(QStringLiteral("open_image_external"), openImageExternal_); settings.setValue(QStringLiteral("open_video_external"), openVideoExternal_); settings.setValue(QStringLiteral("expose_dbus_api"), exposeDBusApi_); + settings.setValue(QStringLiteral("space_background_maintenance"), updateSpaceVias_); settings.endGroup(); // user @@ -1102,6 +1116,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const return tr("Master signing key"); case ExposeDBusApi: return tr("Expose room information via D-Bus"); + case UpdateSpaceVias: + return tr("Periodically update community routing information"); } } else if (role == Value) { switch (index.row()) { @@ -1235,6 +1251,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const return cache::secret(mtx::secret_storage::secrets::cross_signing_master).has_value(); case ExposeDBusApi: return i->exposeDBusApi(); + case UpdateSpaceVias: + return i->updateSpaceVias(); } } else if (role == Description) { switch (index.row()) { @@ -1405,6 +1423,12 @@ UserSettingsModel::data(const QModelIndex &index, int role) const "This can have useful applications, but it also could be used for nefarious " "purposes. Enable at your own risk.\n\n" "This setting will take effect upon restart."); + case UpdateSpaceVias: + return tr( + "To allow new users to join a community, the community needs to expose some " + "information about what servers participate in a room to community members. Since " + "the room participants can change over time, this needs to be updated from time to " + "time. This setting enables a background job to do that automatically."); } } else if (role == Type) { switch (index.row()) { @@ -1453,6 +1477,7 @@ UserSettingsModel::data(const QModelIndex &index, int role) const case ShareKeysWithTrustedUsers: case UseOnlineKeyBackup: case ExposeDBusApi: + case UpdateSpaceVias: case SpaceNotifications: case FancyEffects: case ReducedMotion: @@ -1938,6 +1963,13 @@ UserSettingsModel::setData(const QModelIndex &index, const QVariant &value, int } else return false; } + case UpdateSpaceVias: { + if (value.userType() == QMetaType::Bool) { + i->setUpdateSpaceVias(value.toBool()); + return true; + } else + return false; + } } } return false; @@ -2187,4 +2219,7 @@ UserSettingsModel::UserSettingsModel(QObject *p) connect(s.get(), &UserSettings::exposeDBusApiChanged, this, [this] { emit dataChanged(index(ExposeDBusApi), index(ExposeDBusApi), {Value}); }); + connect(s.get(), &UserSettings::updateSpaceViasChanged, this, [this] { + emit dataChanged(index(UpdateSpaceVias), index(UpdateSpaceVias), {Value}); + }); } diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h index d150c282..8477818b 100644 --- a/src/UserSettingsPage.h +++ b/src/UserSettingsPage.h @@ -121,6 +121,8 @@ class UserSettings final : public QObject hiddenWidgetsChanged) Q_PROPERTY( bool exposeDBusApi READ exposeDBusApi WRITE setExposeDBusApi NOTIFY exposeDBusApiChanged) + Q_PROPERTY(bool updateSpaceVias READ updateSpaceVias WRITE setUpdateSpaceVias NOTIFY + updateSpaceViasChanged) UserSettings(); @@ -205,6 +207,7 @@ public: void setOpenVideoExternal(bool state); void setCollapsedSpaces(QList<QStringList> spaces); void setExposeDBusApi(bool state); + void setUpdateSpaceVias(bool state); QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; } bool messageHoverHighlight() const { return messageHoverHighlight_; } @@ -277,6 +280,7 @@ public: bool openVideoExternal() const { return openVideoExternal_; } QList<QStringList> collapsedSpaces() const { return collapsedSpaces_; } bool exposeDBusApi() const { return exposeDBusApi_; } + bool updateSpaceVias() const { return updateSpaceVias_; } signals: void groupViewStateChanged(bool state); @@ -339,6 +343,7 @@ signals: void hiddenWidgetsChanged(); void recentReactionsChanged(); void exposeDBusApiChanged(bool state); + void updateSpaceViasChanged(bool state); private: // Default to system theme if QT_QPA_PLATFORMTHEME var is set. @@ -410,6 +415,7 @@ private: bool openImageExternal_; bool openVideoExternal_; bool exposeDBusApi_; + bool updateSpaceVias_; QSettings settings; @@ -439,6 +445,7 @@ class UserSettingsModel final : public QAbstractListModel #ifdef NHEKO_DBUS_SYS ExposeDBusApi, #endif + UpdateSpaceVias, AccessibilitySection, ReducedMotion, diff --git a/src/Utils.cpp b/src/Utils.cpp index 54bd51ab..c5b2abd1 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -16,6 +16,7 @@ #include <QStringBuilder> #include <QTextBoundaryFinder> #include <QTextDocument> +#include <QTimer> #include <QWindow> #include <QXmlStreamReader> @@ -28,11 +29,13 @@ #include "Cache.h" #include "Cache_p.h" +#include "ChatPage.h" #include "Config.h" #include "EventAccessors.h" #include "Logging.h" #include "MatrixClient.h" #include "UserSettingsPage.h" +#include "timeline/Permissions.h" using TimelineEvent = mtx::events::collections::TimelineEvents; @@ -1294,8 +1297,6 @@ utils::roomVias(const std::string &roomid) deniedServers.reserve(acls->content.deny.size()); for (const auto &s : acls->content.deny) allowedServers.push_back(globToRegexp(s)); - - nhlog::ui()->critical("ACL: {}", nlohmann::json(acls->content).dump(2)); } auto isHostAllowed = [&acls, &allowedServers, &deniedServers](const std::string &host) { @@ -1423,3 +1424,178 @@ utils::roomVias(const std::string &roomid) return vias; } + +void +utils::updateSpaceVias() +{ + if (!UserSettings::instance()->updateSpaceVias()) + return; + + nhlog::net()->info("update space vias called"); + + auto rooms = cache::roomInfo(false); + + auto us = http::client()->user_id().to_string(); + + auto weekAgo = (uint64_t)QDateTime::currentDateTime().addDays(-7).toMSecsSinceEpoch(); + + struct ApplySpaceUpdatesState + { + std::vector<mtx::events::StateEvent<mtx::events::state::space::Child>> childrenToUpdate; + std::vector<mtx::events::StateEvent<mtx::events::state::space::Parent>> parentsToUpdate; + + static void next(std::shared_ptr<ApplySpaceUpdatesState> state) + { + if (!state->childrenToUpdate.empty()) { + const auto &child = state->childrenToUpdate.back(); + + http::client()->send_state_event( + child.room_id, + child.state_key, + child.content, + [state = std::move(state)](const mtx::responses::EventId &, + mtx::http::RequestErr e) mutable { + const auto &child_ = state->childrenToUpdate.back(); + if (e) { + if (e->status_code == 429 && e->matrix_error.retry_after.count() != 0) { + ChatPage::instance()->callFunctionOnGuiThread( + [state = std::move(state), + interval = e->matrix_error.retry_after]() { + QTimer::singleShot(interval, + ChatPage::instance(), + [self = std::move(state)]() mutable { + next(std::move(self)); + }); + }); + return; + } + + nhlog::net()->error("Failed to update space child {} -> {}: {}", + child_.room_id, + child_.state_key, + *e); + } + nhlog::net()->info( + "Updated space child {} -> {}", child_.room_id, child_.state_key); + state->childrenToUpdate.pop_back(); + next(std::move(state)); + }); + return; + } else if (!state->parentsToUpdate.empty()) { + const auto &parent = state->parentsToUpdate.back(); + + http::client()->send_state_event( + parent.room_id, + parent.state_key, + parent.content, + [state = std::move(state)](const mtx::responses::EventId &, + mtx::http::RequestErr e) mutable { + const auto &parent_ = state->parentsToUpdate.back(); + if (e) { + if (e->status_code == 429 && e->matrix_error.retry_after.count() != 0) { + ChatPage::instance()->callFunctionOnGuiThread( + [state = std::move(state), + interval = e->matrix_error.retry_after]() { + QTimer::singleShot(interval, + ChatPage::instance(), + [self = std::move(state)]() mutable { + next(std::move(self)); + }); + }); + return; + } + + nhlog::net()->error("Failed to update space parent {} -> {}: {}", + parent_.room_id, + parent_.state_key, + *e); + } + nhlog::net()->info( + "Updated space parent {} -> {}", parent_.room_id, parent_.state_key); + state->parentsToUpdate.pop_back(); + next(std::move(state)); + }); + return; + } + } + }; + + auto asus = std::make_shared<ApplySpaceUpdatesState>(); + + for (const auto &[roomid, info] : rooms.toStdMap()) { + if (!info.is_space) + continue; + + auto spaceid = roomid.toStdString(); + + if (auto pl = cache::client() + ->getStateEvent<mtx::events::state::PowerLevels>(spaceid) + .value_or(mtx::events::StateEvent<mtx::events::state::PowerLevels>{}) + .content; + pl.user_level(us) < pl.state_level(to_string(mtx::events::EventType::SpaceChild))) + continue; + + auto children = cache::client()->getChildRoomIds(spaceid); + + for (const auto &childid : children) { + // only update children we are joined to + if (!rooms.contains(QString::fromStdString(childid))) + continue; + + auto child = + cache::client()->getStateEvent<mtx::events::state::space::Child>(spaceid, childid); + if (child && + // don't update too often + child->origin_server_ts < weekAgo && + // ignore unset spaces + (child->content.via && !child->content.via->empty())) { + auto newVias = utils::roomVias(childid); + + if (!newVias.empty() && newVias != child->content.via) { + nhlog::net()->info("Will update {} -> {} child relation from {} to {}", + spaceid, + childid, + fmt::join(*child->content.via, ","), + fmt::join(newVias, ",")); + + child->content.via = std::move(newVias); + child->room_id = spaceid; + asus->childrenToUpdate.push_back(*std::move(child)); + } + } + + auto parent = + cache::client()->getStateEvent<mtx::events::state::space::Parent>(childid, spaceid); + if (parent && + // don't update too often + parent->origin_server_ts < weekAgo && + // ignore unset spaces + (parent->content.via && !parent->content.via->empty())) { + if (auto pl = + cache::client() + ->getStateEvent<mtx::events::state::PowerLevels>(childid) + .value_or(mtx::events::StateEvent<mtx::events::state::PowerLevels>{}) + .content; + pl.user_level(us) < + pl.state_level(to_string(mtx::events::EventType::SpaceParent))) + continue; + + auto newVias = utils::roomVias(spaceid); + + if (!newVias.empty() && newVias != parent->content.via) { + nhlog::net()->info("Will update {} -> {} parent relation from {} to {}", + childid, + spaceid, + fmt::join(*parent->content.via, ","), + fmt::join(newVias, ",")); + + parent->content.via = std::move(newVias); + parent->room_id = childid; + asus->parentsToUpdate.push_back(*std::move(parent)); + } + } + } + } + + ApplySpaceUpdatesState::next(std::move(asus)); +} diff --git a/src/Utils.h b/src/Utils.h index bf411178..2bf01f84 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -335,4 +335,7 @@ markRoomAsDirect(QString roomid, std::vector<RoomMember> members); std::vector<std::string> roomVias(const std::string &roomid); + +void +updateSpaceVias(); } |