From eab8731f5bd695342ad6412d42b1065872f12691 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 8 Jul 2023 19:22:50 +0200 Subject: Port state events and images --- src/timeline/EventStore.cpp | 4 +- src/timeline/TimelineModel.cpp | 210 ++++++++++++++++++++++++----------------- src/timeline/TimelineModel.h | 15 ++- 3 files changed, 138 insertions(+), 91 deletions(-) (limited to 'src') diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index 63b67474..e29dfb4c 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -843,8 +843,8 @@ EventStore::get(const std::string &id, nhlog::net()->error( "Failed to retrieve event with id {}, which was " "requested to show the replyTo for event {}", - relatedTo, - id); + id, + relatedTo); return; } emit eventFetched(id, relatedTo, timeline); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 69ab3f5a..066d8b01 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -532,6 +532,7 @@ TimelineModel::roleNames() const {IsOnlyEmoji, "isOnlyEmoji"}, {Body, "body"}, {FormattedBody, "formattedBody"}, + {FormattedStateEvent, "formattedStateEvent"}, {IsSender, "isSender"}, {UserId, "userId"}, {UserName, "userName"}, @@ -694,6 +695,76 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r return QVariant(utils::replaceEmoji(utils::linkifyMessage(formattedBody_))); } + case FormattedStateEvent: { + if (mtx::accessors::is_state_event(event)) { + return std::visit( + [this](const auto &e) { + constexpr auto t = mtx::events::state_content_to_type; + if constexpr (t == mtx::events::EventType::RoomServerAcl) + return tr("%1 changed which servers are allowed in this room.") + .arg(displayName(QString::fromStdString(e.sender))); + else if constexpr (t == mtx::events::EventType::RoomName) { + if (e.content.name.empty()) + return tr("%1 removed the room name.") + .arg(displayName(QString::fromStdString(e.sender))); + else + return tr("%1 changed the room name to: %2") + .arg(displayName(QString::fromStdString(e.sender))) + .arg(QString::fromStdString(e.content.name).toHtmlEscaped()); + } else if constexpr (t == mtx::events::EventType::RoomTopic) { + if (e.content.topic.empty()) + return tr("%1 removed the topic.") + .arg(displayName(QString::fromStdString(e.sender))); + else + return tr("%1 changed the topic to: %2") + .arg(displayName(QString::fromStdString(e.sender))) + .arg(QString::fromStdString(e.content.topic).toHtmlEscaped()); + } else if constexpr (t == mtx::events::EventType::RoomAvatar) { + if (e.content.url.starts_with("mxc://")) + return tr("%1 changed the room avatar to: %2") + .arg(displayName(QString::fromStdString(e.sender))) + .arg(QStringLiteral("") + .arg(QUrl::toPercentEncoding( + QString::fromStdString(e.content.url)))); + else + return tr("%1 removed the room avatar.") + .arg(displayName(QString::fromStdString(e.sender))); + } else if constexpr (t == mtx::events::EventType::RoomPinnedEvents) + return tr("%1 changed the pinned messages.") + .arg(displayName(QString::fromStdString(e.sender))); + else if constexpr (t == mtx::events::EventType::ImagePackInRoom) + formatImagePackEvent(e); + else if constexpr (t == mtx::events::EventType::RoomCanonicalAlias) + return tr("%1 changed the addresses for this room.") + .arg(displayName(QString::fromStdString(e.sender))); + else if constexpr (t == mtx::events::EventType::SpaceParent) + return tr("%1 changed the parent communities for this room.") + .arg(displayName(QString::fromStdString(e.sender))); + else if constexpr (t == mtx::events::EventType::RoomCreate) + return tr("%1 created and configured room: %2") + .arg(displayName(QString::fromStdString(e.sender))) + .arg(room_id_); + else if constexpr (t == mtx::events::EventType::RoomPowerLevels) + return formatPowerLevelEvent(e); + else if constexpr (t == mtx::events::EventType::PolicyRuleRoom) + return formatPolicyRule(QString::fromStdString(e.event_id)); + else if constexpr (t == mtx::events::EventType::PolicyRuleUser) + return formatPolicyRule(QString::fromStdString(e.event_id)); + else if constexpr (t == mtx::events::EventType::PolicyRuleServer) + return formatPolicyRule(QString::fromStdString(e.event_id)); + else if constexpr (t == mtx::events::EventType::RoomHistoryVisibility) + return formatHistoryVisibilityEvent(e); + else if constexpr (t == mtx::events::EventType::RoomGuestAccess) + return formatGuestAccessEvent(e); + + return tr("%1 changed unknown state event %2.") + .arg(displayName(QString::fromStdString(e.sender))) + .arg(QString::fromStdString(to_string(e.type))); + }, + event); + } + return QString(); + } case Url: return QVariant(QString::fromStdString(url(event))); case ThumbnailUrl: @@ -2308,20 +2379,13 @@ TimelineModel::formatJoinRuleEvent(const QString &id) } QString -TimelineModel::formatGuestAccessEvent(const QString &id) +TimelineModel::formatGuestAccessEvent( + const mtx::events::StateEvent &event) const { - auto e = events.get(id.toStdString(), ""); - if (!e) - return {}; - - auto event = std::get_if>(e); - if (!event) - return {}; - - QString user = QString::fromStdString(event->sender); + QString user = QString::fromStdString(event.sender); QString name = utils::replaceEmoji(displayName(user)); - switch (event->content.guest_access) { + switch (event.content.guest_access) { case mtx::events::state::AccessState::CanJoin: return tr("%1 made the room open to guests.").arg(name); case mtx::events::state::AccessState::Forbidden: @@ -2332,21 +2396,13 @@ TimelineModel::formatGuestAccessEvent(const QString &id) } QString -TimelineModel::formatHistoryVisibilityEvent(const QString &id) +TimelineModel::formatHistoryVisibilityEvent( + const mtx::events::StateEvent &event) const { - auto e = events.get(id.toStdString(), ""); - if (!e) - return {}; - - auto event = std::get_if>(e); - - if (!event) - return {}; - - QString user = QString::fromStdString(event->sender); + QString user = QString::fromStdString(event.sender); QString name = utils::replaceEmoji(displayName(user)); - switch (event->content.history_visibility) { + switch (event.content.history_visibility) { case mtx::events::state::Visibility::WorldReadable: return tr("%1 made the room history world readable. Events may be now read by " "non-joined people.") @@ -2364,32 +2420,25 @@ TimelineModel::formatHistoryVisibilityEvent(const QString &id) } QString -TimelineModel::formatPowerLevelEvent(const QString &id) +TimelineModel::formatPowerLevelEvent( + const mtx::events::StateEvent &event) const { - auto e = events.get(id.toStdString(), ""); - if (!e) - return {}; - - auto event = std::get_if>(e); - if (!event) - return QString(); - mtx::events::StateEvent const *prevEvent = nullptr; - if (!event->unsigned_data.replaces_state.empty()) { - auto tempPrevEvent = events.get(event->unsigned_data.replaces_state, event->event_id); + if (!event.unsigned_data.replaces_state.empty()) { + auto tempPrevEvent = events.get(event.unsigned_data.replaces_state, event.event_id); if (tempPrevEvent) { prevEvent = std::get_if>(tempPrevEvent); } } - QString user = QString::fromStdString(event->sender); + QString user = QString::fromStdString(event.sender); QString sender_name = utils::replaceEmoji(displayName(user)); // Get the rooms levels for redactions and powerlevel changes to determine "Administrator" and // "Moderator" powerlevels. - auto administrator_power_level = event->content.state_level("m.room.power_levels"); - auto moderator_power_level = event->content.redact; - auto default_powerlevel = event->content.users_default; + auto administrator_power_level = event.content.state_level("m.room.power_levels"); + auto moderator_power_level = event.content.redact; + auto default_powerlevel = event.content.users_default; if (!prevEvent) return tr("%1 has changed the room's permissions.").arg(sender_name); @@ -2399,7 +2448,7 @@ TimelineModel::formatPowerLevelEvent(const QString &id) auto numberOfAffected = 0; // We do only compare to people with explicit PL. Usually others are not going to be // affected either way and this is cheaper to iterate over. - for (auto const &[mxid, currentPowerlevel] : event->content.users) { + for (auto const &[mxid, currentPowerlevel] : event.content.users) { if (currentPowerlevel == newPowerlevelSetting && prevEvent->content.user_level(mxid) < newPowerlevelSetting) { numberOfAffected++; @@ -2413,16 +2462,16 @@ TimelineModel::formatPowerLevelEvent(const QString &id) QStringList resultingMessage{}; // These affect only a few people. Therefor we can print who is affected. - if (event->content.kick != prevEvent->content.kick) { + if (event.content.kick != prevEvent->content.kick) { auto default_message = tr("%1 has changed the room's kick powerlevel from %2 to %3.") .arg(sender_name) .arg(prevEvent->content.kick) - .arg(event->content.kick); + .arg(event.content.kick); // We only calculate affected users if we change to a level above the default users PL // to not accidentally have a DoS vector - if (event->content.kick > default_powerlevel) { - auto [affected, number_of_affected] = calc_affected(event->content.kick); + if (event.content.kick > default_powerlevel) { + auto [affected, number_of_affected] = calc_affected(event.content.kick); if (number_of_affected != 0) { auto true_affected_rest = number_of_affected - affected.size(); @@ -2444,16 +2493,16 @@ TimelineModel::formatPowerLevelEvent(const QString &id) } } - if (event->content.redact != prevEvent->content.redact) { + if (event.content.redact != prevEvent->content.redact) { auto default_message = tr("%1 has changed the room's redact powerlevel from %2 to %3.") .arg(sender_name) .arg(prevEvent->content.redact) - .arg(event->content.redact); + .arg(event.content.redact); // We only calculate affected users if we change to a level above the default users PL // to not accidentally have a DoS vector - if (event->content.redact > default_powerlevel) { - auto [affected, number_of_affected] = calc_affected(event->content.redact); + if (event.content.redact > default_powerlevel) { + auto [affected, number_of_affected] = calc_affected(event.content.redact); if (number_of_affected != 0) { auto true_affected_rest = number_of_affected - affected.size(); @@ -2476,16 +2525,16 @@ TimelineModel::formatPowerLevelEvent(const QString &id) } } - if (event->content.ban != prevEvent->content.ban) { + if (event.content.ban != prevEvent->content.ban) { auto default_message = tr("%1 has changed the room's ban powerlevel from %2 to %3.") .arg(sender_name) .arg(prevEvent->content.ban) - .arg(event->content.ban); + .arg(event.content.ban); // We only calculate affected users if we change to a level above the default users PL // to not accidentally have a DoS vector - if (event->content.ban > default_powerlevel) { - auto [affected, number_of_affected] = calc_affected(event->content.ban); + if (event.content.ban > default_powerlevel) { + auto [affected, number_of_affected] = calc_affected(event.content.ban); if (number_of_affected != 0) { auto true_affected_rest = number_of_affected - affected.size(); @@ -2507,17 +2556,17 @@ TimelineModel::formatPowerLevelEvent(const QString &id) } } - if (event->content.state_default != prevEvent->content.state_default) { + if (event.content.state_default != prevEvent->content.state_default) { auto default_message = tr("%1 has changed the room's state_default powerlevel from %2 to %3.") .arg(sender_name) .arg(prevEvent->content.state_default) - .arg(event->content.state_default); + .arg(event.content.state_default); // We only calculate affected users if we change to a level above the default users PL // to not accidentally have a DoS vector - if (event->content.state_default > default_powerlevel) { - auto [affected, number_of_affected] = calc_affected(event->content.kick); + if (event.content.state_default > default_powerlevel) { + auto [affected, number_of_affected] = calc_affected(event.content.kick); if (number_of_affected != 0) { auto true_affected_rest = number_of_affected - affected.size(); @@ -2541,42 +2590,42 @@ TimelineModel::formatPowerLevelEvent(const QString &id) // These affect potentially the whole room. We there for do not calculate who gets affected // by this to prevent huge lists of people. - if (event->content.invite != prevEvent->content.invite) { + if (event.content.invite != prevEvent->content.invite) { resultingMessage.append(tr("%1 has changed the room's invite powerlevel from %2 to %3.") .arg(sender_name, QString::number(prevEvent->content.invite), - QString::number(event->content.invite))); + QString::number(event.content.invite))); } - if (event->content.events_default != prevEvent->content.events_default) { - if ((event->content.events_default > default_powerlevel) && + if (event.content.events_default != prevEvent->content.events_default) { + if ((event.content.events_default > default_powerlevel) && prevEvent->content.events_default <= default_powerlevel) { resultingMessage.append( tr("%1 has changed the room's events_default powerlevel from %2 to %3. New " "users can now not send any events.") .arg(sender_name, QString::number(prevEvent->content.events_default), - QString::number(event->content.events_default))); - } else if ((event->content.events_default < prevEvent->content.events_default) && - (event->content.events_default < default_powerlevel) && + QString::number(event.content.events_default))); + } else if ((event.content.events_default < prevEvent->content.events_default) && + (event.content.events_default < default_powerlevel) && (prevEvent->content.events_default > default_powerlevel)) { resultingMessage.append( tr("%1 has changed the room's events_default powerlevel from %2 to %3. New " "users can now send events that are not otherwise restricted.") .arg(sender_name, QString::number(prevEvent->content.events_default), - QString::number(event->content.events_default))); + QString::number(event.content.events_default))); } else { resultingMessage.append( tr("%1 has changed the room's events_default powerlevel from %2 to %3.") .arg(sender_name, QString::number(prevEvent->content.events_default), - QString::number(event->content.events_default))); + QString::number(event.content.events_default))); } } // Compare if a Powerlevel of a user changed - for (auto const &[mxid, powerlevel] : event->content.users) { + for (auto const &[mxid, powerlevel] : event.content.users) { auto nameOfChangedUser = utils::replaceEmoji(displayName(QString::fromStdString(mxid))); if (prevEvent->content.user_level(mxid) != powerlevel) { if (powerlevel >= administrator_power_level) { @@ -2601,7 +2650,7 @@ TimelineModel::formatPowerLevelEvent(const QString &id) } // Handle added/removed/changed event type - for (auto const &[event_type, powerlevel] : event->content.events) { + for (auto const &[event_type, powerlevel] : event.content.events) { auto prev_not_present = prevEvent->content.events.find(event_type) == prevEvent->content.events.end(); @@ -2640,26 +2689,19 @@ TimelineModel::formatPowerLevelEvent(const QString &id) } QString -TimelineModel::formatImagePackEvent(const QString &id) +TimelineModel::formatImagePackEvent( + const mtx::events::StateEvent &event) const { - auto e = events.get(id.toStdString(), ""); - if (!e) - return {}; - - auto event = std::get_if>(e); - if (!event) - return {}; - mtx::events::StateEvent const *prevEvent = nullptr; - if (!event->unsigned_data.replaces_state.empty()) { - auto tempPrevEvent = events.get(event->unsigned_data.replaces_state, event->event_id); + if (!event.unsigned_data.replaces_state.empty()) { + auto tempPrevEvent = events.get(event.unsigned_data.replaces_state, event.event_id); if (tempPrevEvent) { prevEvent = std::get_if>(tempPrevEvent); } } - const auto &newImages = event->content.images; + const auto &newImages = event.content.images; const auto oldImages = prevEvent ? prevEvent->content.images : decltype(newImages){}; auto ascent = QFontMetrics(UserSettings::instance()->font()).ascent(); @@ -2682,12 +2724,12 @@ TimelineModel::formatImagePackEvent(const QString &id) auto added = calcChange(newImages, oldImages); auto removed = calcChange(oldImages, newImages); - auto sender = utils::replaceEmoji(displayName(QString::fromStdString(event->sender))); + auto sender = utils::replaceEmoji(displayName(QString::fromStdString(event.sender))); const auto packId = [&event]() -> QString { - if (event->content.pack && !event->content.pack->display_name.empty()) { - return event->content.pack->display_name.c_str(); - } else if (!event->state_key.empty()) { - return event->state_key.c_str(); + if (event.content.pack && !event.content.pack->display_name.empty()) { + return event.content.pack->display_name.c_str(); + } else if (!event.state_key.empty()) { + return event.state_key.c_str(); } return tr("(empty)"); }(); @@ -2712,7 +2754,7 @@ TimelineModel::formatImagePackEvent(const QString &id) } QString -TimelineModel::formatPolicyRule(const QString &id) +TimelineModel::formatPolicyRule(const QString &id) const { auto idStr = id.toStdString(); auto e = events.get(idStr, ""); diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 57caf26d..b81fc209 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -238,6 +238,7 @@ public: IsOnlyEmoji, Body, FormattedBody, + FormattedStateEvent, IsSender, UserId, UserName, @@ -310,11 +311,15 @@ public: Q_INVOKABLE void joinReplacementRoom(const QString &id); Q_INVOKABLE QString formatMemberEvent(const QString &id); Q_INVOKABLE QString formatJoinRuleEvent(const QString &id); - Q_INVOKABLE QString formatHistoryVisibilityEvent(const QString &id); - Q_INVOKABLE QString formatGuestAccessEvent(const QString &id); - Q_INVOKABLE QString formatPowerLevelEvent(const QString &id); - Q_INVOKABLE QString formatImagePackEvent(const QString &id); - Q_INVOKABLE QString formatPolicyRule(const QString &id); + QString formatHistoryVisibilityEvent( + const mtx::events::StateEvent &event) const; + QString + formatGuestAccessEvent(const mtx::events::StateEvent &) const; + QString formatPowerLevelEvent( + const mtx::events::StateEvent &event) const; + QString formatImagePackEvent( + const mtx::events::StateEvent &event) const; + Q_INVOKABLE QString formatPolicyRule(const QString &id) const; Q_INVOKABLE QVariantMap formatRedactedEvent(const QString &id); Q_INVOKABLE void viewRawMessage(const QString &id); -- cgit 1.4.1