Show widgets as links

This commit is contained in:
Nicolas Werner 2022-02-04 18:47:17 +01:00
parent 00116e8128
commit 7b00411dc4
8 changed files with 132 additions and 11 deletions

View File

@ -402,7 +402,7 @@ if(USE_BUNDLED_MTXCLIENT)
FetchContent_Declare( FetchContent_Declare(
MatrixClient MatrixClient
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git 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_EXAMPLES OFF CACHE INTERNAL "")
set(BUILD_LIB_TESTS OFF CACHE INTERNAL "") set(BUILD_LIB_TESTS OFF CACHE INTERNAL "")

View File

@ -191,7 +191,7 @@ modules:
buildsystem: cmake-ninja buildsystem: cmake-ninja
name: mtxclient name: mtxclient
sources: sources:
- commit: dc55f64862aeaf02d75383fbdadcbf64ea585506 - commit: 29b46c872576d88797178d3c147e8b12b1ac8693
#tag: v0.6.1 #tag: v0.6.1
type: git type: git
url: https://github.com/Nheko-Reborn/mtxclient.git url: https://github.com/Nheko-Reborn/mtxclient.git

View File

@ -36,7 +36,7 @@ Pane {
TapHandler { TapHandler {
onSingleTapped: { 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 eventPoint.accepted = true
return; 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 { ScrollHelper {
flickable: parent flickable: parent
anchors.fill: parent anchors.fill: parent
@ -311,7 +347,7 @@ Pane {
CursorShape { CursorShape {
anchors.fill: parent 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 cursorShape: Qt.PointingHandCursor
} }
} }

View File

@ -96,10 +96,12 @@ public:
return getStateEvent<T>(txn, room_id, state_key); return getStateEvent<T>(txn, room_id, state_key);
} }
template<typename T> 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); 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 //! retrieve a specific event from account data
@ -494,13 +496,11 @@ private:
template<typename T> template<typename T>
std::vector<mtx::events::StateEvent<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()) if (room_id.empty())
return {}; return {};

View File

@ -124,6 +124,7 @@ UserSettings::load(std::optional<QString> profile)
deviceId_ = settings.value(prefix + "auth/device_id", "").toString(); deviceId_ = settings.value(prefix + "auth/device_id", "").toString();
hiddenTags_ = settings.value(prefix + "user/hidden_tags", QStringList{}).toStringList(); hiddenTags_ = settings.value(prefix + "user/hidden_tags", QStringList{}).toStringList();
hiddenPins_ = settings.value(prefix + "user/hidden_pins", QStringList{}).toStringList(); hiddenPins_ = settings.value(prefix + "user/hidden_pins", QStringList{}).toStringList();
hiddenWidgets_ = settings.value(prefix + "user/hidden_widgets", QStringList{}).toStringList();
recentReactions_ = recentReactions_ =
settings.value(prefix + "user/recent_reactions", QStringList{}).toStringList(); settings.value(prefix + "user/recent_reactions", QStringList{}).toStringList();
@ -217,6 +218,14 @@ UserSettings::setHiddenPins(QStringList hiddenTags)
emit hiddenPinsChanged(); emit hiddenPinsChanged();
} }
void
UserSettings::setHiddenWidgets(QStringList hiddenTags)
{
hiddenWidgets_ = hiddenTags;
save();
emit hiddenWidgetsChanged();
}
void void
UserSettings::setRecentReactions(QStringList recent) UserSettings::setRecentReactions(QStringList recent)
{ {
@ -735,6 +744,7 @@ UserSettings::save()
settings.setValue(prefix + "user/online_key_backup", useOnlineKeyBackup_); settings.setValue(prefix + "user/online_key_backup", useOnlineKeyBackup_);
settings.setValue(prefix + "user/hidden_tags", hiddenTags_); settings.setValue(prefix + "user/hidden_tags", hiddenTags_);
settings.setValue(prefix + "user/hidden_pins", hiddenPins_); settings.setValue(prefix + "user/hidden_pins", hiddenPins_);
settings.setValue(prefix + "user/hidden_widgets", hiddenWidgets_);
settings.setValue(prefix + "user/recent_reactions", recentReactions_); settings.setValue(prefix + "user/recent_reactions", recentReactions_);
QVariantList v; QVariantList v;

View File

@ -107,6 +107,8 @@ class UserSettings : public QObject
Q_PROPERTY(QStringList hiddenPins READ hiddenPins WRITE setHiddenPins NOTIFY hiddenPinsChanged) Q_PROPERTY(QStringList hiddenPins READ hiddenPins WRITE setHiddenPins NOTIFY hiddenPinsChanged)
Q_PROPERTY(QStringList recentReactions READ recentReactions WRITE setRecentReactions NOTIFY Q_PROPERTY(QStringList recentReactions READ recentReactions WRITE setRecentReactions NOTIFY
recentReactionsChanged) recentReactionsChanged)
Q_PROPERTY(QStringList hiddenWidgets READ hiddenWidgets WRITE setHiddenWidgets NOTIFY
hiddenWidgetsChanged)
UserSettings(); UserSettings();
@ -175,6 +177,7 @@ public:
void setDisableCertificateValidation(bool disabled); void setDisableCertificateValidation(bool disabled);
void setHiddenTags(QStringList hiddenTags); void setHiddenTags(QStringList hiddenTags);
void setHiddenPins(QStringList hiddenTags); void setHiddenPins(QStringList hiddenTags);
void setHiddenWidgets(QStringList hiddenTags);
void setRecentReactions(QStringList recent); void setRecentReactions(QStringList recent);
void setUseIdenticon(bool state); void setUseIdenticon(bool state);
void setCollapsedSpaces(QList<QStringList> spaces); void setCollapsedSpaces(QList<QStringList> spaces);
@ -234,6 +237,7 @@ public:
bool disableCertificateValidation() const { return disableCertificateValidation_; } bool disableCertificateValidation() const { return disableCertificateValidation_; }
QStringList hiddenTags() const { return hiddenTags_; } QStringList hiddenTags() const { return hiddenTags_; }
QStringList hiddenPins() const { return hiddenPins_; } QStringList hiddenPins() const { return hiddenPins_; }
QStringList hiddenWidgets() const { return hiddenWidgets_; }
QStringList recentReactions() const { return recentReactions_; } QStringList recentReactions() const { return recentReactions_; }
bool useIdenticon() const { return useIdenticon_ && JdenticonProvider::isAvailable(); } bool useIdenticon() const { return useIdenticon_ && JdenticonProvider::isAvailable(); }
QList<QStringList> collapsedSpaces() const { return collapsedSpaces_; } QList<QStringList> collapsedSpaces() const { return collapsedSpaces_; }
@ -286,6 +290,7 @@ signals:
void disableCertificateValidationChanged(bool disabled); void disableCertificateValidationChanged(bool disabled);
void useIdenticonChanged(bool state); void useIdenticonChanged(bool state);
void hiddenPinsChanged(); void hiddenPinsChanged();
void hiddenWidgetsChanged();
void recentReactionsChanged(); void recentReactionsChanged();
private: private:
@ -342,6 +347,7 @@ private:
QString homeserver_; QString homeserver_;
QStringList hiddenTags_; QStringList hiddenTags_;
QStringList hiddenPins_; QStringList hiddenPins_;
QStringList hiddenWidgets_;
QStringList recentReactions_; QStringList recentReactions_;
QList<QStringList> collapsedSpaces_; QList<QStringList> collapsedSpaces_;
bool useIdenticon_; bool useIdenticon_;

View File

@ -285,6 +285,9 @@ qml_mtx_events::fromRoomEventType(qml_mtx_events::EventType t)
/// m.room.pinned_events /// m.room.pinned_events
case qml_mtx_events::PinnedEvents: case qml_mtx_events::PinnedEvents:
return mtx::events::EventType::RoomPinnedEvents; return mtx::events::EventType::RoomPinnedEvents;
/// m.widget
case qml_mtx_events::Widget:
return mtx::events::EventType::Widget;
// m.sticker // m.sticker
case qml_mtx_events::Sticker: case qml_mtx_events::Sticker:
return mtx::events::EventType::Sticker; return mtx::events::EventType::Sticker;
@ -852,6 +855,8 @@ TimelineModel::syncState(const mtx::responses::State &s)
emit roomTopicChanged(); emit roomTopicChanged();
else if (std::holds_alternative<StateEvent<state::PinnedEvents>>(e)) else if (std::holds_alternative<StateEvent<state::PinnedEvents>>(e))
emit pinnedMessagesChanged(); emit pinnedMessagesChanged();
else if (std::holds_alternative<StateEvent<state::Widget>>(e))
emit widgetLinksChanged();
else if (std::holds_alternative<StateEvent<state::PowerLevels>>(e)) { else if (std::holds_alternative<StateEvent<state::PowerLevels>>(e)) {
permissions_.invalidate(); permissions_.invalidate();
emit permissionsChanged(); emit permissionsChanged();
@ -916,6 +921,8 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
emit roomTopicChanged(); emit roomTopicChanged();
else if (std::holds_alternative<StateEvent<state::PinnedEvents>>(e)) else if (std::holds_alternative<StateEvent<state::PinnedEvents>>(e))
emit pinnedMessagesChanged(); emit pinnedMessagesChanged();
else if (std::holds_alternative<StateEvent<state::Widget>>(e))
emit widgetLinksChanged();
else if (std::holds_alternative<StateEvent<state::PowerLevels>>(e)) { else if (std::holds_alternative<StateEvent<state::PowerLevels>>(e)) {
permissions_.invalidate(); permissions_.invalidate();
emit permissionsChanged(); emit permissionsChanged();
@ -2225,6 +2232,63 @@ TimelineModel::pinnedMessages() const
return list; 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 crypto::Trust
TimelineModel::trustlevel() const TimelineModel::trustlevel() const
{ {

View File

@ -91,6 +91,8 @@ enum EventType
Sticker, Sticker,
// m.tag // m.tag
Tag, Tag,
// m.widget
Widget,
/// m.room.message /// m.room.message
AudioMessage, AudioMessage,
EmoteMessage, EmoteMessage,
@ -178,6 +180,7 @@ class TimelineModel : public QAbstractListModel
Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl NOTIFY roomAvatarUrlChanged) Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl NOTIFY roomAvatarUrlChanged)
Q_PROPERTY(QString roomTopic READ roomTopic NOTIFY roomTopicChanged) Q_PROPERTY(QString roomTopic READ roomTopic NOTIFY roomTopicChanged)
Q_PROPERTY(QStringList pinnedMessages READ pinnedMessages NOTIFY pinnedMessagesChanged) 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(int roomMemberCount READ roomMemberCount NOTIFY roomMemberCountChanged)
Q_PROPERTY(bool isEncrypted READ isEncrypted NOTIFY encryptionChanged) Q_PROPERTY(bool isEncrypted READ isEncrypted NOTIFY encryptionChanged)
Q_PROPERTY(bool isSpace READ isSpace CONSTANT) Q_PROPERTY(bool isSpace READ isSpace CONSTANT)
@ -365,6 +368,7 @@ public slots:
QString plainRoomName() const; QString plainRoomName() const;
QString roomTopic() const; QString roomTopic() const;
QStringList pinnedMessages() const; QStringList pinnedMessages() const;
QStringList widgetLinks() const;
InputBar *input() { return &input_; } InputBar *input() { return &input_; }
Permissions *permissions() { return &permissions_; } Permissions *permissions() { return &permissions_; }
QString roomAvatarUrl() const; QString roomAvatarUrl() const;
@ -407,6 +411,7 @@ signals:
void plainRoomNameChanged(); void plainRoomNameChanged();
void roomTopicChanged(); void roomTopicChanged();
void pinnedMessagesChanged(); void pinnedMessagesChanged();
void widgetLinksChanged();
void roomAvatarUrlChanged(); void roomAvatarUrlChanged();
void roomMemberCountChanged(); void roomMemberCountChanged();
void isDirectChanged(); void isDirectChanged();