Remaining events apart from verification

This commit is contained in:
Nicolas Werner 2023-08-13 11:30:41 +02:00
parent 718a58d388
commit 2360dfd80a
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
9 changed files with 286 additions and 88 deletions

View file

@ -203,7 +203,7 @@ Item {
color: type == MtxEvent.NoticeMessage ? palette.buttonText : palette.text
font.italic: type == MtxEvent.NoticeMessage
formatted: formattedBody + "a"
formatted: formattedBody
Layout.fillWidth: true
//Layout.maximumWidth: implicitWidth
@ -262,6 +262,7 @@ Item {
text: formattedStateEvent
formatted: ''
body: ''
horizontalAlignment: Text.AlignHCenter
color: palette.buttonText
font.italic: true
@ -272,6 +273,79 @@ Item {
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.CallInvite,
]
TextMessage {
keepFullText: true
required property string userId
required property string userName
required property string callType
isOnlyEmoji: false
body: formatted
formatted: {
switch (callType) {
case "voice":
return qsTr("%1 placed a voice call.").arg(TimelineManager.escapeEmoji(userName));
case "video":
return qsTr("%1 placed a video call.").arg(TimelineManager.escapeEmoji(userName));
default:
return qsTr("%1 placed a call.").arg(TimelineManager.escapeEmoji(userName));
}
}
color: palette.buttonText
font.italic: true
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.CallAnswer,
MtxEvent.CallReject,
MtxEvent.CallSelectAnswer,
MtxEvent.CallHangUp,
MtxEvent.CallCandidates,
MtxEvent.CallNegotiate,
]
TextMessage {
keepFullText: true
required property string userId
required property string userName
required property int type
isOnlyEmoji: false
body: formatted
formatted: {
switch (type) {
case MtxEvent.CallAnswer:
return qsTr("%1 answered the call.").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallReject:
return qsTr("%1 rejected the call.").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallSelectAnswer:
return qsTr("%1 selected answer.").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallHangUp:
return qsTr("%1 ended the call.").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallCandidates:
return qsTr("%1 is negotiating the call...").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallNegotiate:
return qsTr("%1 is negotiating the call...").arg(TimelineManager.escapeEmoji(userName));
}
}
color: palette.buttonText
font.italic: true
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.ImageMessage,
@ -285,6 +359,44 @@ Item {
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.FileMessage,
]
FileMessage {
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.VideoMessage,
MtxEvent.AudioMessage,
]
PlayableMediaMessage {
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.Encrypted,
]
Encrypted {
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.Encryption,
]
EncryptionEnabled {
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.Redacted
@ -298,6 +410,76 @@ Item {
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.Member
]
ColumnLayout {
id: member
required property string userId
required property string userName
required property bool isReply
required property Room room
required property string formattedStateEvent
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: tombstone.isReply
keepFullText: true
isStateEvent: true
Layout.fillWidth: true
formatted: member.formattedStateEvent
}
Button {
visible: room.showAcceptKnockButton(eventId)
Layout.alignment: Qt.AlignHCenter
text: qsTr("Allow them in")
onClicked: room.acceptKnock(member.eventId)
}
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.Tombstone
]
ColumnLayout {
id: tombstone
required property string userId
required property string userName
required property string body
required property bool isReply
required property Room room
required property string eventId
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: tombstone.isReply
keepFullText: true
isStateEvent: true
Layout.fillWidth: true
formatted: qsTr("This room was replaced for the following reason: %1").arg(tombstone.body)
}
Button {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Go to replacement room")
onClicked: tombstone.room.joinReplacementRoom(tombstone.eventId)
}
}
}
EventDelegateChoice {
roleValues: [
]

View file

@ -8,37 +8,34 @@ import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import im.nheko 1.0
Rectangle {
Control {
id: r
required property int encryptionError
required property string eventId
radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium
width: parent.width? parent.width : 0
implicitWidth: encryptedText.implicitWidth+24+Nheko.paddingMedium*3 // Column doesn't provide a useful implicitWidth, should be replaced by ColumnLayout
height: contents.implicitHeight + Nheko.paddingMedium * 2
color: palette.alternateBase
padding: Nheko.paddingMedium
implicitHeight: contents.implicitHeight + Nheko.paddingMedium * 2
Layout.maximumWidth: contents.Layout.maximumWidth + padding * 2
Layout.fillWidth: true
RowLayout {
contentItem: RowLayout {
id: contents
anchors.fill: parent
anchors.margins: Nheko.paddingMedium
spacing: Nheko.paddingMedium
Image {
source: "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?" + Nheko.theme.error
Layout.alignment: Qt.AlignVCenter
width: 24
height: width
Layout.preferredWidth: 24
Layout.preferredHeight: 24
}
Column {
ColumnLayout {
spacing: Nheko.paddingSmall
Layout.fillWidth: true
MatrixText {
Label {
id: encryptedText
text: {
switch (encryptionError) {
@ -58,8 +55,11 @@ Rectangle {
return qsTr("Unknown decryption error");
}
}
textFormat: Text.PlainText
wrapMode: Label.WordWrap
color: palette.text
width: parent.width
Layout.fillWidth: true
Layout.maximumWidth: implicitWidth + 1
}
Button {
@ -72,4 +72,9 @@ Rectangle {
}
background: Rectangle {
color: palette.alternateBase
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingMedium
visible: !Settings.bubbles // the bubble in a bubble looks odd
}
}

View file

@ -3,27 +3,24 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import ".."
import QtQuick 2.15
import QtQuick.Layouts 1.15
import im.nheko 1.0
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import im.nheko
Rectangle {
Control {
id: r
required property string username
required property string userName
radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium
width: parent.width ? Math.min(parent.width, 700) : 0
height: contents.implicitHeight + Nheko.paddingMedium * 2
color: palette.alternateBase
border.color: Nheko.theme.green
border.width: 2
padding: Nheko.paddingMedium
//implicitHeight: contents.implicitHeight + padd * 2
Layout.maximumWidth: contents.Layout.maximumWidth + padding * 2
Layout.fillWidth: true
RowLayout {
contentItem: RowLayout {
id: contents
anchors.fill: parent
anchors.margins: Nheko.paddingMedium
spacing: Nheko.paddingMedium
Image {
@ -33,26 +30,36 @@ Rectangle {
Layout.preferredHeight: 24
}
Column {
ColumnLayout {
spacing: Nheko.paddingSmall
Layout.fillWidth: true
MatrixText {
text: qsTr("%1 enabled end-to-end encryption").arg(r.username)
text: qsTr("%1 enabled end-to-end encryption").arg(r.userName)
font.bold: true
font.pointSize: 14
color: palette.text
width: parent.width
Layout.fillWidth: true
Layout.maximumWidth: implicitWidth + 1
}
MatrixText {
Label {
text: qsTr("Encryption keeps your messages safe by only allowing the people you sent the message to to read it. For extra security, if you want to make sure you are talking to the right people, you can verify them in real life.")
color: palette.text
width: parent.width
textFormat: Text.PlainText
wrapMode: Label.WordWrap
Layout.fillWidth: true
Layout.maximumWidth: implicitWidth + 1
}
}
}
background: Rectangle {
radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium
height: contents.implicitHeight + Nheko.paddingMedium * 2
color: palette.alternateBase
border.color: Nheko.theme.green
border.width: 2
}
}

View file

@ -2,26 +2,30 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.12
import QtQuick.Layouts 1.2
import im.nheko 1.0
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import im.nheko
Control {
id: evRoot
Item {
required property string eventId
required property string filename
required property string filesize
height: rowa.height + (Settings.bubbles? 16: 24)
implicitWidth: rowa.implicitWidth + metadataWidth
property int metadataWidth
property bool fitsMetadata: true
padding: Settings.bubbles? 8 : 12
//Layout.preferredHeight: rowa.implicitHeight + padding
//Layout.maximumWidth: rowa.Layout.maximumWidth + metadataWidth + padding
property int metadataWidth: 0
property bool fitsMetadata: false
RowLayout {
Layout.maximumWidth: rowa.Layout.maximumWidth + padding * 2
contentItem: RowLayout {
id: rowa
anchors.centerIn: parent
width: parent.width - (Settings.bubbles? 16 : 24)
spacing: 15
spacing: 16
Rectangle {
id: button
@ -63,6 +67,7 @@ Item {
id: filename_
Layout.fillWidth: true
Layout.maximumWidth: implicitWidth + 1
text: filename
textFormat: Text.PlainText
elide: Text.ElideRight
@ -73,6 +78,7 @@ Item {
id: filesize_
Layout.fillWidth: true
Layout.maximumWidth: implicitWidth + 1
text: filesize
textFormat: Text.PlainText
elide: Text.ElideRight
@ -83,11 +89,9 @@ Item {
}
Rectangle {
background: Rectangle {
color: palette.alternateBase
z: -1
radius: 10
anchors.fill: parent
radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall
visible: !Settings.bubbles // the bubble in a bubble looks odd
}

View file

@ -36,7 +36,6 @@ Control {
property var redactedPair: room.formatRedactedEvent(msgRoot.eventId)
text: redactedPair["first"]
wrapMode: Label.WordWrap
color: palette.text
ToolTip.text: redactedPair["second"]
ToolTip.visible: hh.hovered

View file

@ -195,7 +195,8 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
if (!forReply) {
auto row = chooser.room_->idToIndex(currentId);
connect(chooser.room_,
auto connection = connect(
chooser.room_,
&QAbstractItemModel::dataChanged,
obj,
[row, update](const QModelIndex &topLeft,
@ -205,6 +206,10 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
return;
update(changedRoles);
},
Qt::QueuedConnection);
connect(&this->chooser, &EventDelegateChooser::destroyed, obj, [connection]() {
QObject::disconnect(connection);
});
}
}

View file

@ -757,6 +757,8 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
return formatHistoryVisibilityEvent(e);
else if constexpr (t == mtx::events::EventType::RoomGuestAccess)
return formatGuestAccessEvent(e);
else if constexpr (t == mtx::events::EventType::RoomMember)
return formatMemberEvent(e);
return tr("%1 changed unknown state event %2.")
.arg(displayName(QString::fromStdString(e.sender)))
@ -2958,34 +2960,27 @@ TimelineModel::joinReplacementRoom(const QString &id)
}
QString
TimelineModel::formatMemberEvent(const QString &id)
TimelineModel::formatMemberEvent(
const mtx::events::StateEvent<mtx::events::state::Member> &event) const
{
auto e = events.get(id.toStdString(), "");
if (!e)
return {};
auto event = std::get_if<mtx::events::StateEvent<mtx::events::state::Member>>(e);
if (!event)
return {};
mtx::events::StateEvent<mtx::events::state::Member> 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<mtx::events::StateEvent<mtx::events::state::Member>>(tempPrevEvent);
}
}
QString user = QString::fromStdString(event->state_key);
QString user = QString::fromStdString(event.state_key);
QString name = utils::replaceEmoji(displayName(user));
QString rendered;
QString sender = QString::fromStdString(event->sender);
QString sender = QString::fromStdString(event.sender);
QString senderName = utils::replaceEmoji(displayName(sender));
// see table https://matrix.org/docs/spec/client_server/latest#m-room-member
using namespace mtx::events::state;
switch (event->content.membership) {
switch (event.content.membership) {
case Membership::Invite:
rendered = tr("%1 invited %2.").arg(senderName, name);
break;
@ -2994,9 +2989,8 @@ TimelineModel::formatMemberEvent(const QString &id)
QString oldName = utils::replaceEmoji(
QString::fromStdString(prevEvent->content.display_name).toHtmlEscaped());
bool displayNameChanged =
prevEvent->content.display_name != event->content.display_name;
bool avatarChanged = prevEvent->content.avatar_url != event->content.avatar_url;
bool displayNameChanged = prevEvent->content.display_name != event.content.display_name;
bool avatarChanged = prevEvent->content.avatar_url != event.content.avatar_url;
if (displayNameChanged && avatarChanged)
rendered = tr("%1 has changed their avatar and changed their "
@ -3011,30 +3005,30 @@ TimelineModel::formatMemberEvent(const QString &id)
// the case of nothing changed but join follows join shouldn't happen, so
// just show it as join
} else {
if (event->content.join_authorised_via_users_server.empty())
if (event.content.join_authorised_via_users_server.empty())
rendered = tr("%1 joined.").arg(name);
else
rendered =
tr("%1 joined via authorisation from %2's server.")
.arg(name,
QString::fromStdString(event->content.join_authorised_via_users_server));
QString::fromStdString(event.content.join_authorised_via_users_server));
}
break;
case Membership::Leave:
if (!prevEvent || prevEvent->content.membership == Membership::Join) {
if (event->state_key == event->sender)
if (event.state_key == event.sender)
rendered = tr("%1 left the room.").arg(name);
else
rendered = tr("%2 kicked %1.").arg(name, senderName);
} else if (prevEvent->content.membership == Membership::Invite) {
if (event->state_key == event->sender)
if (event.state_key == event.sender)
rendered = tr("%1 rejected their invite.").arg(name);
else
rendered = tr("%2 revoked the invite to %1.").arg(name, senderName);
} else if (prevEvent->content.membership == Membership::Ban) {
rendered = tr("%2 unbanned %1.").arg(name, senderName);
} else if (prevEvent->content.membership == Membership::Knock) {
if (event->state_key == event->sender)
if (event.state_key == event.sender)
rendered = tr("%1 redacted their knock.").arg(name);
else
rendered = tr("%2 rejected the knock from %1.").arg(name, senderName);
@ -3053,8 +3047,8 @@ TimelineModel::formatMemberEvent(const QString &id)
break;
}
if (event->content.reason != "") {
rendered += " " + tr("Reason: %1").arg(QString::fromStdString(event->content.reason));
if (event.content.reason != "") {
rendered += " " + tr("Reason: %1").arg(QString::fromStdString(event.content.reason));
}
return rendered;

View file

@ -310,7 +310,8 @@ public:
Q_INVOKABLE bool showAcceptKnockButton(const QString &id);
Q_INVOKABLE void acceptKnock(const QString &id);
Q_INVOKABLE void joinReplacementRoom(const QString &id);
Q_INVOKABLE QString formatMemberEvent(const QString &id);
Q_INVOKABLE QString
formatMemberEvent(const mtx::events::StateEvent<mtx::events::state::Member> &event) const;
Q_INVOKABLE QString formatJoinRuleEvent(const QString &id);
QString formatHistoryVisibilityEvent(
const mtx::events::StateEvent<mtx::events::state::HistoryVisibility> &event) const;

View file

@ -92,6 +92,7 @@ CallManager::CallManager(QObject *parent)
if (QGuiApplication::platformName() != QStringLiteral("wayland")) {
// Selected by default
screenShareType_ = ScreenShareType::X11;
if (screenShareTypes_.size() >= 2)
std::swap(screenShareTypes_[0], screenShareTypes_[1]);
}
}