summary refs log tree commit diff
diff options
context:
space:
mode:
authorNicolas Werner <nicolas.werner@hotmail.de>2022-02-04 18:47:17 +0100
committerNicolas Werner <nicolas.werner@hotmail.de>2022-02-04 20:48:14 +0100
commit7b00411dc44d188ec1deab302fbcdb0bd2a399e7 (patch)
tree32934f5fc2c32fc4b4b0f5b6f4134baa78e1a265
parentMerge pull request #906 from LorenDB/offlineIndicator (diff)
downloadnheko-7b00411dc44d188ec1deab302fbcdb0bd2a399e7.tar.xz
Show widgets as links
-rw-r--r--CMakeLists.txt2
-rw-r--r--io.github.NhekoReborn.Nheko.yaml2
-rw-r--r--resources/qml/TopBar.qml40
-rw-r--r--src/Cache_p.h14
-rw-r--r--src/UserSettingsPage.cpp10
-rw-r--r--src/UserSettingsPage.h6
-rw-r--r--src/timeline/TimelineModel.cpp64
-rw-r--r--src/timeline/TimelineModel.h5
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;
             }
@@ -307,11 +307,47 @@ Pane {
                     }
                 }
             }
+
+            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
+                        enabled: !Settings.mobileMode
+                    }
+                }
+            }
         }
 
         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<T>(txn, room_id, state_key);
     }
     template<typename T>
-    std::vector<mtx::events::StateEvent<T>> getStateEventsWithType(const std::string &room_id)
+    std::vector<mtx::events::StateEvent<T>>
+    getStateEventsWithType(const std::string &room_id,
+                           mtx::events::EventType type = mtx::events::state_content_to_type<T>)
     {
         auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY);
-        return getStateEventsWithType<T>(txn, room_id);
+        return getStateEventsWithType<T>(txn, room_id, type);
     }
 
     //! retrieve a specific event from account data
@@ -494,13 +496,11 @@ private:
 
     template<typename T>
     std::vector<mtx::events::StateEvent<T>>
-    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<T>)
 
     {
-        constexpr auto type = mtx::events::state_content_to_type<T>;
-        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<QString> 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();
 
@@ -218,6 +219,14 @@ UserSettings::setHiddenPins(QStringList hiddenTags)
 }
 
 void
+UserSettings::setHiddenWidgets(QStringList hiddenTags)
+{
+    hiddenWidgets_ = hiddenTags;
+    save();
+    emit hiddenWidgetsChanged();
+}
+
+void
 UserSettings::setRecentReactions(QStringList recent)
 {
     recentReactions_ = 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<QStringList> 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<QStringList> 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<QStringList> 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<StateEvent<state::PinnedEvents>>(e))
             emit pinnedMessagesChanged();
+        else if (std::holds_alternative<StateEvent<state::Widget>>(e))
+            emit widgetLinksChanged();
         else if (std::holds_alternative<StateEvent<state::PowerLevels>>(e)) {
             permissions_.invalidate();
             emit permissionsChanged();
@@ -916,6 +921,8 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
             emit roomTopicChanged();
         else if (std::holds_alternative<StateEvent<state::PinnedEvents>>(e))
             emit pinnedMessagesChanged();
+        else if (std::holds_alternative<StateEvent<state::Widget>>(e))
+            emit widgetLinksChanged();
         else if (std::holds_alternative<StateEvent<state::PowerLevels>>(e)) {
             permissions_.invalidate();
             emit permissionsChanged();
@@ -2225,6 +2232,63 @@ TimelineModel::pinnedMessages() const
     return list;
 }
 
+QStringList
+TimelineModel::widgetLinks() const
+{
+    auto evs =
+      cache::client()->getStateEventsWithType<mtx::events::state::Widget>(room_id_.toStdString());
+    auto evs2 = cache::client()->getStateEventsWithType<mtx::events::state::Widget>(
+      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("<a href='%1'>%2</a>")
+            .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();