From 7b00411dc44d188ec1deab302fbcdb0bd2a399e7 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 4 Feb 2022 18:47:17 +0100 Subject: [PATCH] Show widgets as links --- CMakeLists.txt | 2 +- io.github.NhekoReborn.Nheko.yaml | 2 +- resources/qml/TopBar.qml | 40 +++++++++++++++++++- src/Cache_p.h | 14 +++---- src/UserSettingsPage.cpp | 10 +++++ src/UserSettingsPage.h | 6 +++ src/timeline/TimelineModel.cpp | 64 ++++++++++++++++++++++++++++++++ src/timeline/TimelineModel.h | 5 +++ 8 files changed, 132 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ff9e9df..4fa41ba5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -402,7 +402,7 @@ if(USE_BUNDLED_MTXCLIENT) FetchContent_Declare( MatrixClient GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git - GIT_TAG dc55f64862aeaf02d75383fbdadcbf64ea585506 + GIT_TAG 29b46c872576d88797178d3c147e8b12b1ac8693 ) set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "") set(BUILD_LIB_TESTS OFF CACHE INTERNAL "") diff --git a/io.github.NhekoReborn.Nheko.yaml b/io.github.NhekoReborn.Nheko.yaml index 8b835092..ed76deba 100644 --- a/io.github.NhekoReborn.Nheko.yaml +++ b/io.github.NhekoReborn.Nheko.yaml @@ -191,7 +191,7 @@ modules: buildsystem: cmake-ninja name: mtxclient sources: - - commit: dc55f64862aeaf02d75383fbdadcbf64ea585506 + - commit: 29b46c872576d88797178d3c147e8b12b1ac8693 #tag: v0.6.1 type: git url: https://github.com/Nheko-Reborn/mtxclient.git diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml index c9a8d0d2..77eed1b0 100644 --- a/resources/qml/TopBar.qml +++ b/resources/qml/TopBar.qml @@ -36,7 +36,7 @@ Pane { TapHandler { onSingleTapped: { - if (eventPoint.position.y > topBar.height - (pinnedMessages.visible ? pinnedMessages.height : 0)) { + if (eventPoint.position.y > topBar.height - (pinnedMessages.visible ? pinnedMessages.height : 0) - (widgets.visible ? widgets.height : 0)) { eventPoint.accepted = true return; } @@ -300,6 +300,42 @@ Pane { } + ScrollHelper { + flickable: parent + anchors.fill: parent + enabled: !Settings.mobileMode + } + } + } + + ScrollView { + id: widgets + + Layout.row: 3 + Layout.column: 2 + Layout.columnSpan: 3 + + Layout.fillWidth: true + Layout.preferredHeight: Math.min(contentHeight, Nheko.avatarSize * 1.5) + + visible: !!room && room.widgetLinks.length > 0 && !Settings.hiddenWidgets.includes(roomId) + clip: true + + palette: Nheko.colors + ScrollBar.horizontal.visible: false + + ListView { + + spacing: Nheko.paddingSmall + model: room ? room.widgetLinks : undefined + delegate: MatrixText { + required property var modelData + + color: Nheko.colors.text + text: modelData + } + + ScrollHelper { flickable: parent anchors.fill: parent @@ -311,7 +347,7 @@ Pane { CursorShape { anchors.fill: parent - anchors.bottomMargin: pinnedMessages.visible ? pinnedMessages.height : 0 + anchors.bottomMargin: (pinnedMessages.visible ? pinnedMessages.height : 0) + (widgets.visible ? widgets.height : 0) cursorShape: Qt.PointingHandCursor } } diff --git a/src/Cache_p.h b/src/Cache_p.h index b30e8f47..160ba626 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -96,10 +96,12 @@ public: return getStateEvent(txn, room_id, state_key); } template - std::vector> getStateEventsWithType(const std::string &room_id) + std::vector> + getStateEventsWithType(const std::string &room_id, + mtx::events::EventType type = mtx::events::state_content_to_type) { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - return getStateEventsWithType(txn, room_id); + return getStateEventsWithType(txn, room_id, type); } //! retrieve a specific event from account data @@ -494,13 +496,11 @@ private: template std::vector> - getStateEventsWithType(lmdb::txn &txn, const std::string &room_id) + getStateEventsWithType(lmdb::txn &txn, + const std::string &room_id, + mtx::events::EventType type = mtx::events::state_content_to_type) { - constexpr auto type = mtx::events::state_content_to_type; - static_assert(type != mtx::events::EventType::Unsupported, - "Not a supported type in state events."); - if (room_id.empty()) return {}; diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index a0aa8f84..bb7edbb5 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -124,6 +124,7 @@ UserSettings::load(std::optional profile) deviceId_ = settings.value(prefix + "auth/device_id", "").toString(); hiddenTags_ = settings.value(prefix + "user/hidden_tags", QStringList{}).toStringList(); hiddenPins_ = settings.value(prefix + "user/hidden_pins", QStringList{}).toStringList(); + hiddenWidgets_ = settings.value(prefix + "user/hidden_widgets", QStringList{}).toStringList(); recentReactions_ = settings.value(prefix + "user/recent_reactions", QStringList{}).toStringList(); @@ -217,6 +218,14 @@ UserSettings::setHiddenPins(QStringList hiddenTags) emit hiddenPinsChanged(); } +void +UserSettings::setHiddenWidgets(QStringList hiddenTags) +{ + hiddenWidgets_ = hiddenTags; + save(); + emit hiddenWidgetsChanged(); +} + void UserSettings::setRecentReactions(QStringList recent) { @@ -735,6 +744,7 @@ UserSettings::save() settings.setValue(prefix + "user/online_key_backup", useOnlineKeyBackup_); settings.setValue(prefix + "user/hidden_tags", hiddenTags_); settings.setValue(prefix + "user/hidden_pins", hiddenPins_); + settings.setValue(prefix + "user/hidden_widgets", hiddenWidgets_); settings.setValue(prefix + "user/recent_reactions", recentReactions_); QVariantList v; diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h index e9b8763d..ebe46672 100644 --- a/src/UserSettingsPage.h +++ b/src/UserSettingsPage.h @@ -107,6 +107,8 @@ class UserSettings : public QObject Q_PROPERTY(QStringList hiddenPins READ hiddenPins WRITE setHiddenPins NOTIFY hiddenPinsChanged) Q_PROPERTY(QStringList recentReactions READ recentReactions WRITE setRecentReactions NOTIFY recentReactionsChanged) + Q_PROPERTY(QStringList hiddenWidgets READ hiddenWidgets WRITE setHiddenWidgets NOTIFY + hiddenWidgetsChanged) UserSettings(); @@ -175,6 +177,7 @@ public: void setDisableCertificateValidation(bool disabled); void setHiddenTags(QStringList hiddenTags); void setHiddenPins(QStringList hiddenTags); + void setHiddenWidgets(QStringList hiddenTags); void setRecentReactions(QStringList recent); void setUseIdenticon(bool state); void setCollapsedSpaces(QList spaces); @@ -234,6 +237,7 @@ public: bool disableCertificateValidation() const { return disableCertificateValidation_; } QStringList hiddenTags() const { return hiddenTags_; } QStringList hiddenPins() const { return hiddenPins_; } + QStringList hiddenWidgets() const { return hiddenWidgets_; } QStringList recentReactions() const { return recentReactions_; } bool useIdenticon() const { return useIdenticon_ && JdenticonProvider::isAvailable(); } QList collapsedSpaces() const { return collapsedSpaces_; } @@ -286,6 +290,7 @@ signals: void disableCertificateValidationChanged(bool disabled); void useIdenticonChanged(bool state); void hiddenPinsChanged(); + void hiddenWidgetsChanged(); void recentReactionsChanged(); private: @@ -342,6 +347,7 @@ private: QString homeserver_; QStringList hiddenTags_; QStringList hiddenPins_; + QStringList hiddenWidgets_; QStringList recentReactions_; QList collapsedSpaces_; bool useIdenticon_; diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 6b380f79..d1613d5b 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -285,6 +285,9 @@ qml_mtx_events::fromRoomEventType(qml_mtx_events::EventType t) /// m.room.pinned_events case qml_mtx_events::PinnedEvents: return mtx::events::EventType::RoomPinnedEvents; + /// m.widget + case qml_mtx_events::Widget: + return mtx::events::EventType::Widget; // m.sticker case qml_mtx_events::Sticker: return mtx::events::EventType::Sticker; @@ -852,6 +855,8 @@ TimelineModel::syncState(const mtx::responses::State &s) emit roomTopicChanged(); else if (std::holds_alternative>(e)) emit pinnedMessagesChanged(); + else if (std::holds_alternative>(e)) + emit widgetLinksChanged(); else if (std::holds_alternative>(e)) { permissions_.invalidate(); emit permissionsChanged(); @@ -916,6 +921,8 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline) emit roomTopicChanged(); else if (std::holds_alternative>(e)) emit pinnedMessagesChanged(); + else if (std::holds_alternative>(e)) + emit widgetLinksChanged(); else if (std::holds_alternative>(e)) { permissions_.invalidate(); emit permissionsChanged(); @@ -2225,6 +2232,63 @@ TimelineModel::pinnedMessages() const return list; } +QStringList +TimelineModel::widgetLinks() const +{ + auto evs = + cache::client()->getStateEventsWithType(room_id_.toStdString()); + auto evs2 = cache::client()->getStateEventsWithType( + room_id_.toStdString(), mtx::events::EventType::Widget); + evs.insert( + evs.end(), std::make_move_iterator(evs2.begin()), std::make_move_iterator(evs2.end())); + + if (evs.empty()) + return {}; + + QStringList list; + + auto user = utils::localUser(); + auto av = QUrl::toPercentEncoding(avatarUrl(user)); + auto disp = QUrl::toPercentEncoding(displayName(user)); + auto theme = UserSettings::instance()->theme(); + if (theme == QStringLiteral("system")) + theme.clear(); + user = QUrl::toPercentEncoding(user); + + list.reserve(evs.size()); + for (const auto &p : evs) { + auto url = QString::fromStdString(p.content.url); + for (const auto &[k, v] : p.content.data) + url.replace("$" + QString::fromStdString(k), + QUrl::toPercentEncoding(QString::fromStdString(v))); + + url.replace("$matrix_user_id", user); + url.replace("$matrix_room_id", QUrl::toPercentEncoding(room_id_)); + url.replace("$matrix_display_name", disp); + url.replace("$matrix_avatar_url", av); + + url.replace("$matrix_widget_id", + QUrl::toPercentEncoding(QString::fromStdString(p.content.id))); + + // url.replace("$matrix_client_theme", theme); + url.replace("$org.matrix.msc2873.client_theme", theme); + url.replace("$org.matrix.msc2873.client_id", "im.nheko"); + + // compat with some widgets, i.e. FOSDEM + url.replace("$theme", theme); + + url = QUrl::toPercentEncoding(url, "/:?&@=%"); + + list.push_back( + QLatin1String("%2") + .arg(url, + QString::fromStdString(p.content.name.empty() ? p.state_key : p.content.name) + .toHtmlEscaped())); + } + + return list; +} + crypto::Trust TimelineModel::trustlevel() const { diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 556f9f54..b4267e8d 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -91,6 +91,8 @@ enum EventType Sticker, // m.tag Tag, + // m.widget + Widget, /// m.room.message AudioMessage, EmoteMessage, @@ -178,6 +180,7 @@ class TimelineModel : public QAbstractListModel Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl NOTIFY roomAvatarUrlChanged) Q_PROPERTY(QString roomTopic READ roomTopic NOTIFY roomTopicChanged) Q_PROPERTY(QStringList pinnedMessages READ pinnedMessages NOTIFY pinnedMessagesChanged) + Q_PROPERTY(QStringList widgetLinks READ widgetLinks NOTIFY widgetLinksChanged) Q_PROPERTY(int roomMemberCount READ roomMemberCount NOTIFY roomMemberCountChanged) Q_PROPERTY(bool isEncrypted READ isEncrypted NOTIFY encryptionChanged) Q_PROPERTY(bool isSpace READ isSpace CONSTANT) @@ -365,6 +368,7 @@ public slots: QString plainRoomName() const; QString roomTopic() const; QStringList pinnedMessages() const; + QStringList widgetLinks() const; InputBar *input() { return &input_; } Permissions *permissions() { return &permissions_; } QString roomAvatarUrl() const; @@ -407,6 +411,7 @@ signals: void plainRoomNameChanged(); void roomTopicChanged(); void pinnedMessagesChanged(); + void widgetLinksChanged(); void roomAvatarUrlChanged(); void roomMemberCountChanged(); void isDirectChanged();