mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-25 12:38:48 +03:00
Only show actions, when you have permissions to do them
This commit is contained in:
parent
1321d9bcca
commit
ab0baf5d9e
15 changed files with 272 additions and 22 deletions
|
@ -271,6 +271,7 @@ set(SRC_FILES
|
||||||
src/timeline/TimelineViewManager.cpp
|
src/timeline/TimelineViewManager.cpp
|
||||||
src/timeline/TimelineModel.cpp
|
src/timeline/TimelineModel.cpp
|
||||||
src/timeline/DelegateChooser.cpp
|
src/timeline/DelegateChooser.cpp
|
||||||
|
src/timeline/Permissions.cpp
|
||||||
|
|
||||||
# UI components
|
# UI components
|
||||||
src/ui/Avatar.cpp
|
src/ui/Avatar.cpp
|
||||||
|
@ -494,6 +495,7 @@ qt5_wrap_cpp(MOC_HEADERS
|
||||||
src/timeline/TimelineViewManager.h
|
src/timeline/TimelineViewManager.h
|
||||||
src/timeline/TimelineModel.h
|
src/timeline/TimelineModel.h
|
||||||
src/timeline/DelegateChooser.h
|
src/timeline/DelegateChooser.h
|
||||||
|
src/timeline/Permissions.h
|
||||||
|
|
||||||
# UI components
|
# UI components
|
||||||
src/ui/Avatar.h
|
src/ui/Avatar.h
|
||||||
|
|
|
@ -28,6 +28,7 @@ Rectangle {
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: row
|
id: row
|
||||||
|
|
||||||
|
visible: (TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false) || messageContextMenu.isSender
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
|
@ -352,4 +353,11 @@ Rectangle {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: TimelineManager.timeline ? (!TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage)) : false
|
||||||
|
text: qsTr("You don't have permission to send messages in this room")
|
||||||
|
color: colors.text
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,7 @@ ScrollView {
|
||||||
EmojiButton {
|
EmojiButton {
|
||||||
id: reactButton
|
id: reactButton
|
||||||
|
|
||||||
|
visible: chat.model ? chat.model.permissions.canSend(MtxEvent.Reaction) : false
|
||||||
width: 16
|
width: 16
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
|
@ -101,6 +102,7 @@ ScrollView {
|
||||||
ImageButton {
|
ImageButton {
|
||||||
id: replyButton
|
id: replyButton
|
||||||
|
|
||||||
|
visible: chat.model ? chat.model.permissions.canSend(MtxEvent.TextMessage) : false
|
||||||
width: 16
|
width: 16
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
image: ":/icons/icons/ui/mail-reply.png"
|
image: ":/icons/icons/ui/mail-reply.png"
|
||||||
|
@ -117,7 +119,7 @@ ScrollView {
|
||||||
image: ":/icons/icons/ui/vertical-ellipsis.png"
|
image: ":/icons/icons/ui/vertical-ellipsis.png"
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.text: qsTr("Options")
|
ToolTip.text: qsTr("Options")
|
||||||
onClicked: messageContextMenu.show(row.model.id, row.model.type, row.model.isEncrypted, row.model.isEditable, "", row.model.body, optionsButton)
|
onClicked: messageContextMenu.show(row.model.id, row.model.type, row.model.isSender, row.model.isEncrypted, row.model.isEditable, "", row.model.body, optionsButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,12 +28,12 @@ Item {
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.RightButton
|
||||||
onSingleTapped: messageContextMenu.show(model.id, model.type, model.isEncrypted, model.isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
|
onSingleTapped: messageContextMenu.show(model.id, model.type, model.isSender, model.isEncrypted, model.isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onLongPressed: messageContextMenu.show(model.id, model.type, model.isEncrypted, model.isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
|
onLongPressed: messageContextMenu.show(model.id, model.type, model.isSender, model.isEncrypted, model.isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
|
||||||
onDoubleTapped: chat.model.reply = model.id
|
onDoubleTapped: chat.model.reply = model.id
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,12 +97,14 @@ Page {
|
||||||
property int eventType
|
property int eventType
|
||||||
property bool isEncrypted
|
property bool isEncrypted
|
||||||
property bool isEditable
|
property bool isEditable
|
||||||
|
property bool isSender
|
||||||
|
|
||||||
function show(eventId_, eventType_, isEncrypted_, isEditable_, link_, text_, showAt_) {
|
function show(eventId_, eventType_, isSender_, isEncrypted_, isEditable_, link_, text_, showAt_) {
|
||||||
eventId = eventId_;
|
eventId = eventId_;
|
||||||
eventType = eventType_;
|
eventType = eventType_;
|
||||||
isEncrypted = isEncrypted_;
|
isEncrypted = isEncrypted_;
|
||||||
isEditable = isEditable_;
|
isEditable = isEditable_;
|
||||||
|
isSender = isSender_;
|
||||||
if (text_)
|
if (text_)
|
||||||
text = text_;
|
text = text_;
|
||||||
else
|
else
|
||||||
|
@ -134,6 +136,7 @@ Page {
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
id: reactionOption
|
id: reactionOption
|
||||||
|
|
||||||
|
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.Reaction) : false
|
||||||
text: qsTr("React")
|
text: qsTr("React")
|
||||||
onTriggered: emojiPopup.show(null, function(emoji) {
|
onTriggered: emojiPopup.show(null, function(emoji) {
|
||||||
TimelineManager.queueReactionMessage(messageContextMenu.eventId, emoji);
|
TimelineManager.queueReactionMessage(messageContextMenu.eventId, emoji);
|
||||||
|
@ -141,12 +144,13 @@ Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
|
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false
|
||||||
text: qsTr("Reply")
|
text: qsTr("Reply")
|
||||||
onTriggered: TimelineManager.timeline.replyAction(messageContextMenu.eventId)
|
onTriggered: TimelineManager.timeline.replyAction(messageContextMenu.eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
visible: messageContextMenu.isEditable
|
visible: messageContextMenu.isEditable && (TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false)
|
||||||
enabled: visible
|
enabled: visible
|
||||||
text: qsTr("Edit")
|
text: qsTr("Edit")
|
||||||
onTriggered: TimelineManager.timeline.editAction(messageContextMenu.eventId)
|
onTriggered: TimelineManager.timeline.editAction(messageContextMenu.eventId)
|
||||||
|
@ -185,6 +189,7 @@ Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
|
visible: (TimelineManager.timeline ? TimelineManager.timeline.permissions.canRedact() : false) || messageContextMenu.isSender
|
||||||
text: qsTr("Remove message")
|
text: qsTr("Remove message")
|
||||||
onTriggered: TimelineManager.timeline.redactEvent(messageContextMenu.eventId)
|
onTriggered: TimelineManager.timeline.redactEvent(messageContextMenu.eventId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,7 @@ Rectangle {
|
||||||
id: roomOptionsMenu
|
id: roomOptionsMenu
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
|
visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canInvite() : false
|
||||||
text: qsTr("Invite users")
|
text: qsTr("Invite users")
|
||||||
onTriggered: TimelineManager.openInviteUsersDialog()
|
onTriggered: TimelineManager.openInviteUsersDialog()
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ ApplicationWindow {
|
||||||
displayName: profile.displayName
|
displayName: profile.displayName
|
||||||
userid: profile.userid
|
userid: profile.userid
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
onClicked: profile.isSelf ? profile.changeAvatar() : TimelineManager.openImageOverlay(TimelineManager.timeline.avatarUrl(userid), TimelineManager.timeline.data.id)
|
onClicked: profile.isSelf ? profile.changeAvatar() : TimelineManager.openImageOverlay(profile.avatarUrl, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
BusyIndicator {
|
BusyIndicator {
|
||||||
|
@ -151,18 +151,7 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
ImageButton {
|
|
||||||
image: ":/icons/icons/ui/do-not-disturb-rounded-sign.png"
|
|
||||||
hoverEnabled: true
|
|
||||||
ToolTip.visible: hovered
|
|
||||||
ToolTip.text: qsTr("Ban the user")
|
|
||||||
onClicked: profile.banUser()
|
|
||||||
}
|
|
||||||
// ImageButton{
|
// ImageButton{
|
||||||
|
|
||||||
// image:":/icons/icons/ui/volume-off-indicator.png"
|
// image:":/icons/icons/ui/volume-off-indicator.png"
|
||||||
// Layout.margins: {
|
// Layout.margins: {
|
||||||
// left: 5
|
// left: 5
|
||||||
|
@ -174,6 +163,10 @@ ApplicationWindow {
|
||||||
// profile.ignoreUser()
|
// profile.ignoreUser()
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
image: ":/icons/icons/ui/black-bubble-speech.png"
|
image: ":/icons/icons/ui/black-bubble-speech.png"
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
@ -188,6 +181,16 @@ ApplicationWindow {
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.text: qsTr("Kick the user")
|
ToolTip.text: qsTr("Kick the user")
|
||||||
onClicked: profile.kickUser()
|
onClicked: profile.kickUser()
|
||||||
|
visible: profile.room ? profile.room.permissions.canKick() : false
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageButton {
|
||||||
|
image: ":/icons/icons/ui/do-not-disturb-rounded-sign.png"
|
||||||
|
hoverEnabled: true
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.text: qsTr("Ban the user")
|
||||||
|
onClicked: profile.banUser()
|
||||||
|
visible: profile.room ? profile.room.permissions.canBan() : false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,7 @@ getTimelineMentions();
|
||||||
std::vector<std::string>
|
std::vector<std::string>
|
||||||
roomMembers(const std::string &room_id);
|
roomMembers(const std::string &room_id);
|
||||||
|
|
||||||
//! Check if the given user has power leve greater than than
|
//! Check if the given user has power level greater than than
|
||||||
//! lowest power level of the given events.
|
//! lowest power level of the given events.
|
||||||
bool
|
bool
|
||||||
hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes,
|
hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes,
|
||||||
|
|
|
@ -84,6 +84,15 @@ public:
|
||||||
//! Retrieve the version of the room if any.
|
//! Retrieve the version of the room if any.
|
||||||
QString getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb);
|
QString getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb);
|
||||||
|
|
||||||
|
//! Get a specific state event
|
||||||
|
template<typename T>
|
||||||
|
std::optional<mtx::events::StateEvent<T>> getStateEvent(const std::string &room_id,
|
||||||
|
std::string_view state_key = "")
|
||||||
|
{
|
||||||
|
auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY);
|
||||||
|
return getStateEvent<T>(txn, room_id, state_key);
|
||||||
|
}
|
||||||
|
|
||||||
//! Retrieve member info from a room.
|
//! Retrieve member info from a room.
|
||||||
std::vector<RoomMember> getMembers(const std::string &room_id,
|
std::vector<RoomMember> getMembers(const std::string &room_id,
|
||||||
std::size_t startIndex = 0,
|
std::size_t startIndex = 0,
|
||||||
|
@ -406,7 +415,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::optional<mtx::events::StateEvent<T>> getStateEvent(lmdb::txn txn,
|
std::optional<mtx::events::StateEvent<T>> getStateEvent(lmdb::txn &txn,
|
||||||
const std::string &room_id,
|
const std::string &room_id,
|
||||||
std::string_view state_key = "")
|
std::string_view state_key = "")
|
||||||
{
|
{
|
||||||
|
|
|
@ -59,7 +59,7 @@ const QRegularExpression url_regex(
|
||||||
// match an URL, that is not quoted, i.e.
|
// match an URL, that is not quoted, i.e.
|
||||||
// vvvvvv match quote via negative lookahead/lookbehind vv
|
// vvvvvv match quote via negative lookahead/lookbehind vv
|
||||||
// vvvv atomic match url -> fail if there is a " before or after vvv
|
// vvvv atomic match url -> fail if there is a " before or after vvv
|
||||||
R"(\b(?<!["'])(?>((www\.(?!\.)|[a-z][a-z0-9+.-]*://)[^\s'"]+[^!,\.\s'"\]\)\:]))(?!["'])\b)");
|
R"((?<!["'])(?>((www\.(?!\.)|[a-z][a-z0-9+.-]*://)[^\s<>'"]+[^!,\.\s<>'"\]\)\:]))(?!["']))");
|
||||||
// match any markdown matrix.to link. Capture group 1 is the link name, group 2 is the target.
|
// match any markdown matrix.to link. Capture group 1 is the link name, group 2 is the target.
|
||||||
static const QRegularExpression matrixToMarkdownLink(
|
static const QRegularExpression matrixToMarkdownLink(
|
||||||
R"(\[(.*?)(?<!\\)\]\((https://matrix.to/#/.*?\)))");
|
R"(\[(.*?)(?<!\\)\]\((https://matrix.to/#/.*?\)))");
|
||||||
|
|
63
src/timeline/Permissions.cpp
Normal file
63
src/timeline/Permissions.cpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "Permissions.h"
|
||||||
|
|
||||||
|
#include "Cache_p.h"
|
||||||
|
#include "MatrixClient.h"
|
||||||
|
#include "TimelineModel.h"
|
||||||
|
|
||||||
|
Permissions::Permissions(TimelineModel *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, room(parent)
|
||||||
|
{
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Permissions::invalidate()
|
||||||
|
{
|
||||||
|
pl = cache::client()
|
||||||
|
->getStateEvent<mtx::events::state::PowerLevels>(room->roomId().toStdString())
|
||||||
|
.value_or(mtx::events::StateEvent<mtx::events::state::PowerLevels>{})
|
||||||
|
.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Permissions::canInvite()
|
||||||
|
{
|
||||||
|
return pl.user_level(http::client()->user_id().to_string()) >= pl.invite;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Permissions::canBan()
|
||||||
|
{
|
||||||
|
return pl.user_level(http::client()->user_id().to_string()) >= pl.ban;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Permissions::canKick()
|
||||||
|
{
|
||||||
|
return pl.user_level(http::client()->user_id().to_string()) >= pl.kick;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Permissions::canRedact()
|
||||||
|
{
|
||||||
|
return pl.user_level(http::client()->user_id().to_string()) >= pl.redact;
|
||||||
|
}
|
||||||
|
bool
|
||||||
|
Permissions::canChange(int eventType)
|
||||||
|
{
|
||||||
|
return pl.user_level(http::client()->user_id().to_string()) >=
|
||||||
|
pl.state_level(to_string(qml_mtx_events::fromRoomEventType(
|
||||||
|
static_cast<qml_mtx_events::EventType>(eventType))));
|
||||||
|
}
|
||||||
|
bool
|
||||||
|
Permissions::canSend(int eventType)
|
||||||
|
{
|
||||||
|
return pl.user_level(http::client()->user_id().to_string()) >=
|
||||||
|
pl.event_level(to_string(qml_mtx_events::fromRoomEventType(
|
||||||
|
static_cast<qml_mtx_events::EventType>(eventType))));
|
||||||
|
}
|
33
src/timeline/Permissions.h
Normal file
33
src/timeline/Permissions.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <mtx/events/power_levels.hpp>
|
||||||
|
|
||||||
|
class TimelineModel;
|
||||||
|
|
||||||
|
class Permissions : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Permissions(TimelineModel *parent);
|
||||||
|
|
||||||
|
Q_INVOKABLE bool canInvite();
|
||||||
|
Q_INVOKABLE bool canBan();
|
||||||
|
Q_INVOKABLE bool canKick();
|
||||||
|
|
||||||
|
Q_INVOKABLE bool canRedact();
|
||||||
|
Q_INVOKABLE bool canChange(int eventType);
|
||||||
|
Q_INVOKABLE bool canSend(int eventType);
|
||||||
|
|
||||||
|
void invalidate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TimelineModel *room;
|
||||||
|
mtx::events::state::PowerLevels pl;
|
||||||
|
};
|
|
@ -207,6 +207,111 @@ toRoomEventTypeString(const mtx::events::collections::TimelineEvents &event)
|
||||||
event);
|
event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mtx::events::EventType
|
||||||
|
qml_mtx_events::fromRoomEventType(qml_mtx_events::EventType t)
|
||||||
|
{
|
||||||
|
switch (t) {
|
||||||
|
// Unsupported event
|
||||||
|
case qml_mtx_events::Unsupported:
|
||||||
|
return mtx::events::EventType::Unsupported;
|
||||||
|
|
||||||
|
/// m.room_key_request
|
||||||
|
case qml_mtx_events::KeyRequest:
|
||||||
|
return mtx::events::EventType::RoomKeyRequest;
|
||||||
|
/// m.reaction:
|
||||||
|
case qml_mtx_events::Reaction:
|
||||||
|
return mtx::events::EventType::Reaction;
|
||||||
|
/// m.room.aliases
|
||||||
|
case qml_mtx_events::Aliases:
|
||||||
|
return mtx::events::EventType::RoomAliases;
|
||||||
|
/// m.room.avatar
|
||||||
|
case qml_mtx_events::Avatar:
|
||||||
|
return mtx::events::EventType::RoomAvatar;
|
||||||
|
/// m.call.invite
|
||||||
|
case qml_mtx_events::CallInvite:
|
||||||
|
return mtx::events::EventType::CallInvite;
|
||||||
|
/// m.call.answer
|
||||||
|
case qml_mtx_events::CallAnswer:
|
||||||
|
return mtx::events::EventType::CallAnswer;
|
||||||
|
/// m.call.hangup
|
||||||
|
case qml_mtx_events::CallHangUp:
|
||||||
|
return mtx::events::EventType::CallHangUp;
|
||||||
|
/// m.call.candidates
|
||||||
|
case qml_mtx_events::CallCandidates:
|
||||||
|
return mtx::events::EventType::CallCandidates;
|
||||||
|
/// m.room.canonical_alias
|
||||||
|
case qml_mtx_events::CanonicalAlias:
|
||||||
|
return mtx::events::EventType::RoomCanonicalAlias;
|
||||||
|
/// m.room.create
|
||||||
|
case qml_mtx_events::RoomCreate:
|
||||||
|
return mtx::events::EventType::RoomCreate;
|
||||||
|
/// m.room.encrypted.
|
||||||
|
case qml_mtx_events::Encrypted:
|
||||||
|
return mtx::events::EventType::RoomEncrypted;
|
||||||
|
/// m.room.encryption.
|
||||||
|
case qml_mtx_events::Encryption:
|
||||||
|
return mtx::events::EventType::RoomEncryption;
|
||||||
|
/// m.room.guest_access
|
||||||
|
case qml_mtx_events::RoomGuestAccess:
|
||||||
|
return mtx::events::EventType::RoomGuestAccess;
|
||||||
|
/// m.room.history_visibility
|
||||||
|
case qml_mtx_events::RoomHistoryVisibility:
|
||||||
|
return mtx::events::EventType::RoomHistoryVisibility;
|
||||||
|
/// m.room.join_rules
|
||||||
|
case qml_mtx_events::RoomJoinRules:
|
||||||
|
return mtx::events::EventType::RoomJoinRules;
|
||||||
|
/// m.room.member
|
||||||
|
case qml_mtx_events::Member:
|
||||||
|
return mtx::events::EventType::RoomMember;
|
||||||
|
/// m.room.name
|
||||||
|
case qml_mtx_events::Name:
|
||||||
|
return mtx::events::EventType::RoomName;
|
||||||
|
/// m.room.power_levels
|
||||||
|
case qml_mtx_events::PowerLevels:
|
||||||
|
return mtx::events::EventType::RoomPowerLevels;
|
||||||
|
/// m.room.tombstone
|
||||||
|
case qml_mtx_events::Tombstone:
|
||||||
|
return mtx::events::EventType::RoomTombstone;
|
||||||
|
/// m.room.topic
|
||||||
|
case qml_mtx_events::Topic:
|
||||||
|
return mtx::events::EventType::RoomTopic;
|
||||||
|
/// m.room.redaction
|
||||||
|
case qml_mtx_events::Redaction:
|
||||||
|
return mtx::events::EventType::RoomRedaction;
|
||||||
|
/// m.room.pinned_events
|
||||||
|
case qml_mtx_events::PinnedEvents:
|
||||||
|
return mtx::events::EventType::RoomPinnedEvents;
|
||||||
|
// m.sticker
|
||||||
|
case qml_mtx_events::Sticker:
|
||||||
|
return mtx::events::EventType::Sticker;
|
||||||
|
// m.tag
|
||||||
|
case qml_mtx_events::Tag:
|
||||||
|
return mtx::events::EventType::Tag;
|
||||||
|
/// m.room.message
|
||||||
|
case qml_mtx_events::AudioMessage:
|
||||||
|
case qml_mtx_events::EmoteMessage:
|
||||||
|
case qml_mtx_events::FileMessage:
|
||||||
|
case qml_mtx_events::ImageMessage:
|
||||||
|
case qml_mtx_events::LocationMessage:
|
||||||
|
case qml_mtx_events::NoticeMessage:
|
||||||
|
case qml_mtx_events::TextMessage:
|
||||||
|
case qml_mtx_events::VideoMessage:
|
||||||
|
case qml_mtx_events::Redacted:
|
||||||
|
case qml_mtx_events::UnknownMessage:
|
||||||
|
case qml_mtx_events::KeyVerificationRequest:
|
||||||
|
case qml_mtx_events::KeyVerificationStart:
|
||||||
|
case qml_mtx_events::KeyVerificationMac:
|
||||||
|
case qml_mtx_events::KeyVerificationAccept:
|
||||||
|
case qml_mtx_events::KeyVerificationCancel:
|
||||||
|
case qml_mtx_events::KeyVerificationKey:
|
||||||
|
case qml_mtx_events::KeyVerificationDone:
|
||||||
|
case qml_mtx_events::KeyVerificationReady:
|
||||||
|
return mtx::events::EventType::RoomMessage;
|
||||||
|
default:
|
||||||
|
return mtx::events::EventType::Unsupported;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObject *parent)
|
TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObject *parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
, events(room_id.toStdString(), this)
|
, events(room_id.toStdString(), this)
|
||||||
|
@ -282,6 +387,7 @@ TimelineModel::roleNames() const
|
||||||
{Body, "body"},
|
{Body, "body"},
|
||||||
{FormattedBody, "formattedBody"},
|
{FormattedBody, "formattedBody"},
|
||||||
{PreviousMessageUserId, "previousMessageUserId"},
|
{PreviousMessageUserId, "previousMessageUserId"},
|
||||||
|
{IsSender, "isSender"},
|
||||||
{UserId, "userId"},
|
{UserId, "userId"},
|
||||||
{UserName, "userName"},
|
{UserName, "userName"},
|
||||||
{PreviousMessageDay, "previousMessageDay"},
|
{PreviousMessageDay, "previousMessageDay"},
|
||||||
|
@ -333,6 +439,8 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
|
||||||
namespace acc = mtx::accessors;
|
namespace acc = mtx::accessors;
|
||||||
|
|
||||||
switch (role) {
|
switch (role) {
|
||||||
|
case IsSender:
|
||||||
|
return QVariant(acc::sender(event) == http::client()->user_id().to_string());
|
||||||
case UserId:
|
case UserId:
|
||||||
return QVariant(QString::fromStdString(acc::sender(event)));
|
return QVariant(QString::fromStdString(acc::sender(event)));
|
||||||
case UserName:
|
case UserName:
|
||||||
|
@ -497,6 +605,7 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
|
||||||
m.insert(names[IsOnlyEmoji], data(event, static_cast<int>(IsOnlyEmoji)));
|
m.insert(names[IsOnlyEmoji], data(event, static_cast<int>(IsOnlyEmoji)));
|
||||||
m.insert(names[Body], data(event, static_cast<int>(Body)));
|
m.insert(names[Body], data(event, static_cast<int>(Body)));
|
||||||
m.insert(names[FormattedBody], data(event, static_cast<int>(FormattedBody)));
|
m.insert(names[FormattedBody], data(event, static_cast<int>(FormattedBody)));
|
||||||
|
m.insert(names[IsSender], data(event, static_cast<int>(IsSender)));
|
||||||
m.insert(names[UserId], data(event, static_cast<int>(UserId)));
|
m.insert(names[UserId], data(event, static_cast<int>(UserId)));
|
||||||
m.insert(names[UserName], data(event, static_cast<int>(UserName)));
|
m.insert(names[UserName], data(event, static_cast<int>(UserName)));
|
||||||
m.insert(names[Day], data(event, static_cast<int>(Day)));
|
m.insert(names[Day], data(event, static_cast<int>(Day)));
|
||||||
|
@ -608,7 +717,10 @@ TimelineModel::syncState(const mtx::responses::State &s)
|
||||||
emit roomNameChanged();
|
emit roomNameChanged();
|
||||||
else if (std::holds_alternative<StateEvent<state::Topic>>(e))
|
else if (std::holds_alternative<StateEvent<state::Topic>>(e))
|
||||||
emit roomTopicChanged();
|
emit roomTopicChanged();
|
||||||
else if (std::holds_alternative<StateEvent<state::Member>>(e)) {
|
else if (std::holds_alternative<StateEvent<state::Topic>>(e)) {
|
||||||
|
permissions_.invalidate();
|
||||||
|
emit permissionsChanged();
|
||||||
|
} else if (std::holds_alternative<StateEvent<state::Member>>(e)) {
|
||||||
emit roomAvatarUrlChanged();
|
emit roomAvatarUrlChanged();
|
||||||
emit roomNameChanged();
|
emit roomNameChanged();
|
||||||
}
|
}
|
||||||
|
@ -661,7 +773,10 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
|
||||||
emit roomNameChanged();
|
emit roomNameChanged();
|
||||||
else if (std::holds_alternative<StateEvent<state::Topic>>(e))
|
else if (std::holds_alternative<StateEvent<state::Topic>>(e))
|
||||||
emit roomTopicChanged();
|
emit roomTopicChanged();
|
||||||
else if (std::holds_alternative<StateEvent<state::Member>>(e)) {
|
else if (std::holds_alternative<StateEvent<state::PowerLevels>>(e)) {
|
||||||
|
permissions_.invalidate();
|
||||||
|
emit permissionsChanged();
|
||||||
|
} else if (std::holds_alternative<StateEvent<state::Member>>(e)) {
|
||||||
emit roomAvatarUrlChanged();
|
emit roomAvatarUrlChanged();
|
||||||
emit roomNameChanged();
|
emit roomNameChanged();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "CacheCryptoStructs.h"
|
#include "CacheCryptoStructs.h"
|
||||||
#include "EventStore.h"
|
#include "EventStore.h"
|
||||||
#include "InputBar.h"
|
#include "InputBar.h"
|
||||||
|
#include "Permissions.h"
|
||||||
#include "ui/RoomSettings.h"
|
#include "ui/RoomSettings.h"
|
||||||
#include "ui/UserProfile.h"
|
#include "ui/UserProfile.h"
|
||||||
|
|
||||||
|
@ -105,6 +106,7 @@ enum EventType
|
||||||
KeyVerificationReady
|
KeyVerificationReady
|
||||||
};
|
};
|
||||||
Q_ENUM_NS(EventType)
|
Q_ENUM_NS(EventType)
|
||||||
|
mtx::events::EventType fromRoomEventType(qml_mtx_events::EventType);
|
||||||
|
|
||||||
enum EventState
|
enum EventState
|
||||||
{
|
{
|
||||||
|
@ -159,6 +161,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(InputBar *input READ input CONSTANT)
|
Q_PROPERTY(InputBar *input READ input CONSTANT)
|
||||||
|
Q_PROPERTY(Permissions *permissions READ permissions NOTIFY permissionsChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TimelineModel(TimelineViewManager *manager,
|
explicit TimelineModel(TimelineViewManager *manager,
|
||||||
|
@ -173,6 +176,7 @@ public:
|
||||||
Body,
|
Body,
|
||||||
FormattedBody,
|
FormattedBody,
|
||||||
PreviousMessageUserId,
|
PreviousMessageUserId,
|
||||||
|
IsSender,
|
||||||
UserId,
|
UserId,
|
||||||
UserName,
|
UserName,
|
||||||
PreviousMessageDay,
|
PreviousMessageDay,
|
||||||
|
@ -300,6 +304,7 @@ public slots:
|
||||||
QString roomName() const;
|
QString roomName() const;
|
||||||
QString roomTopic() const;
|
QString roomTopic() const;
|
||||||
InputBar *input() { return &input_; }
|
InputBar *input() { return &input_; }
|
||||||
|
Permissions *permissions() { return &permissions_; }
|
||||||
QString roomAvatarUrl() const;
|
QString roomAvatarUrl() const;
|
||||||
QString roomId() const { return room_id_; }
|
QString roomId() const { return room_id_; }
|
||||||
|
|
||||||
|
@ -331,6 +336,7 @@ signals:
|
||||||
void roomNameChanged();
|
void roomNameChanged();
|
||||||
void roomTopicChanged();
|
void roomTopicChanged();
|
||||||
void roomAvatarUrlChanged();
|
void roomAvatarUrlChanged();
|
||||||
|
void permissionsChanged();
|
||||||
void forwardToRoom(mtx::events::collections::TimelineEvents *e, QString roomId);
|
void forwardToRoom(mtx::events::collections::TimelineEvents *e, QString roomId);
|
||||||
|
|
||||||
void scrollTargetChanged();
|
void scrollTargetChanged();
|
||||||
|
@ -359,6 +365,7 @@ private:
|
||||||
TimelineViewManager *manager_;
|
TimelineViewManager *manager_;
|
||||||
|
|
||||||
InputBar input_{this};
|
InputBar input_{this};
|
||||||
|
Permissions permissions_{this};
|
||||||
|
|
||||||
QTimer showEventTimer{this};
|
QTimer showEventTimer{this};
|
||||||
QString eventIdToShow;
|
QString eventIdToShow;
|
||||||
|
|
|
@ -95,6 +95,7 @@ class UserProfile : public QObject
|
||||||
Q_PROPERTY(
|
Q_PROPERTY(
|
||||||
bool userVerificationEnabled READ userVerificationEnabled NOTIFY userStatusChanged)
|
bool userVerificationEnabled READ userVerificationEnabled NOTIFY userStatusChanged)
|
||||||
Q_PROPERTY(bool isSelf READ isSelf CONSTANT)
|
Q_PROPERTY(bool isSelf READ isSelf CONSTANT)
|
||||||
|
Q_PROPERTY(TimelineModel *room READ room CONSTANT)
|
||||||
public:
|
public:
|
||||||
UserProfile(QString roomid,
|
UserProfile(QString roomid,
|
||||||
QString userid,
|
QString userid,
|
||||||
|
@ -111,6 +112,7 @@ public:
|
||||||
bool userVerificationEnabled() const;
|
bool userVerificationEnabled() const;
|
||||||
bool isSelf() const;
|
bool isSelf() const;
|
||||||
bool isLoading() const;
|
bool isLoading() const;
|
||||||
|
TimelineModel *room() const { return model; }
|
||||||
|
|
||||||
Q_INVOKABLE void verify(QString device = "");
|
Q_INVOKABLE void verify(QString device = "");
|
||||||
Q_INVOKABLE void unverify(QString device = "");
|
Q_INVOKABLE void unverify(QString device = "");
|
||||||
|
|
Loading…
Reference in a new issue