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 color: type == MtxEvent.NoticeMessage ? palette.buttonText : palette.text
font.italic: type == MtxEvent.NoticeMessage font.italic: type == MtxEvent.NoticeMessage
formatted: formattedBody + "a" formatted: formattedBody
Layout.fillWidth: true Layout.fillWidth: true
//Layout.maximumWidth: implicitWidth //Layout.maximumWidth: implicitWidth
@ -262,6 +262,7 @@ Item {
text: formattedStateEvent text: formattedStateEvent
formatted: '' formatted: ''
body: '' body: ''
horizontalAlignment: Text.AlignHCenter
color: palette.buttonText color: palette.buttonText
font.italic: true 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 { EventDelegateChoice {
roleValues: [ roleValues: [
MtxEvent.ImageMessage, 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 { EventDelegateChoice {
roleValues: [ roleValues: [
MtxEvent.Redacted 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 { EventDelegateChoice {
roleValues: [ roleValues: [
] ]

View file

@ -8,37 +8,34 @@ import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import im.nheko 1.0 import im.nheko 1.0
Rectangle { Control {
id: r id: r
required property int encryptionError required property int encryptionError
required property string eventId required property string eventId
radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium padding: Nheko.paddingMedium
width: parent.width? parent.width : 0 implicitHeight: contents.implicitHeight + Nheko.paddingMedium * 2
implicitWidth: encryptedText.implicitWidth+24+Nheko.paddingMedium*3 // Column doesn't provide a useful implicitWidth, should be replaced by ColumnLayout Layout.maximumWidth: contents.Layout.maximumWidth + padding * 2
height: contents.implicitHeight + Nheko.paddingMedium * 2 Layout.fillWidth: true
color: palette.alternateBase
RowLayout { contentItem: RowLayout {
id: contents id: contents
anchors.fill: parent
anchors.margins: Nheko.paddingMedium
spacing: Nheko.paddingMedium spacing: Nheko.paddingMedium
Image { Image {
source: "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?" + Nheko.theme.error source: "image://colorimage/:/icons/icons/ui/shield-filled-cross.svg?" + Nheko.theme.error
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
width: 24 Layout.preferredWidth: 24
height: width Layout.preferredHeight: 24
} }
Column { ColumnLayout {
spacing: Nheko.paddingSmall spacing: Nheko.paddingSmall
Layout.fillWidth: true Layout.fillWidth: true
MatrixText { Label {
id: encryptedText id: encryptedText
text: { text: {
switch (encryptionError) { switch (encryptionError) {
@ -58,8 +55,11 @@ Rectangle {
return qsTr("Unknown decryption error"); return qsTr("Unknown decryption error");
} }
} }
textFormat: Text.PlainText
wrapMode: Label.WordWrap
color: palette.text color: palette.text
width: parent.width Layout.fillWidth: true
Layout.maximumWidth: implicitWidth + 1
} }
Button { 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 // SPDX-License-Identifier: GPL-3.0-or-later
import ".." import ".."
import QtQuick 2.15 import QtQuick
import QtQuick.Layouts 1.15 import QtQuick.Controls
import im.nheko 1.0 import QtQuick.Layouts
import im.nheko
Rectangle { Control {
id: r id: r
required property string username required property string userName
radius: fontMetrics.lineSpacing / 2 + Nheko.paddingMedium padding: Nheko.paddingMedium
width: parent.width ? Math.min(parent.width, 700) : 0 //implicitHeight: contents.implicitHeight + padd * 2
height: contents.implicitHeight + Nheko.paddingMedium * 2 Layout.maximumWidth: contents.Layout.maximumWidth + padding * 2
color: palette.alternateBase Layout.fillWidth: true
border.color: Nheko.theme.green
border.width: 2
RowLayout { contentItem: RowLayout {
id: contents id: contents
anchors.fill: parent
anchors.margins: Nheko.paddingMedium
spacing: Nheko.paddingMedium spacing: Nheko.paddingMedium
Image { Image {
@ -33,26 +30,36 @@ Rectangle {
Layout.preferredHeight: 24 Layout.preferredHeight: 24
} }
Column { ColumnLayout {
spacing: Nheko.paddingSmall spacing: Nheko.paddingSmall
Layout.fillWidth: true Layout.fillWidth: true
MatrixText { 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.bold: true
font.pointSize: 14 font.pointSize: 14
color: palette.text 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.") 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 textFormat: Text.PlainText
width: parent.width 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 // SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.12 import QtQuick
import QtQuick.Layouts 1.2 import QtQuick.Layouts
import im.nheko 1.0 import QtQuick.Controls
import im.nheko
Control {
id: evRoot
Item {
required property string eventId required property string eventId
required property string filename required property string filename
required property string filesize required property string filesize
height: rowa.height + (Settings.bubbles? 16: 24) padding: Settings.bubbles? 8 : 12
implicitWidth: rowa.implicitWidth + metadataWidth //Layout.preferredHeight: rowa.implicitHeight + padding
property int metadataWidth //Layout.maximumWidth: rowa.Layout.maximumWidth + metadataWidth + padding
property bool fitsMetadata: true property int metadataWidth: 0
property bool fitsMetadata: false
RowLayout { Layout.maximumWidth: rowa.Layout.maximumWidth + padding * 2
contentItem: RowLayout {
id: rowa id: rowa
anchors.centerIn: parent spacing: 16
width: parent.width - (Settings.bubbles? 16 : 24)
spacing: 15
Rectangle { Rectangle {
id: button id: button
@ -63,6 +67,7 @@ Item {
id: filename_ id: filename_
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumWidth: implicitWidth + 1
text: filename text: filename
textFormat: Text.PlainText textFormat: Text.PlainText
elide: Text.ElideRight elide: Text.ElideRight
@ -73,6 +78,7 @@ Item {
id: filesize_ id: filesize_
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumWidth: implicitWidth + 1
text: filesize text: filesize
textFormat: Text.PlainText textFormat: Text.PlainText
elide: Text.ElideRight elide: Text.ElideRight
@ -83,11 +89,9 @@ Item {
} }
Rectangle { background: Rectangle {
color: palette.alternateBase color: palette.alternateBase
z: -1 radius: fontMetrics.lineSpacing / 2 + 2 * Nheko.paddingSmall
radius: 10
anchors.fill: parent
visible: !Settings.bubbles // the bubble in a bubble looks odd visible: !Settings.bubbles // the bubble in a bubble looks odd
} }

View file

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

View file

@ -195,7 +195,8 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
if (!forReply) { if (!forReply) {
auto row = chooser.room_->idToIndex(currentId); auto row = chooser.room_->idToIndex(currentId);
connect(chooser.room_, auto connection = connect(
chooser.room_,
&QAbstractItemModel::dataChanged, &QAbstractItemModel::dataChanged,
obj, obj,
[row, update](const QModelIndex &topLeft, [row, update](const QModelIndex &topLeft,
@ -205,6 +206,10 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
return; return;
update(changedRoles); 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); return formatHistoryVisibilityEvent(e);
else if constexpr (t == mtx::events::EventType::RoomGuestAccess) else if constexpr (t == mtx::events::EventType::RoomGuestAccess)
return formatGuestAccessEvent(e); return formatGuestAccessEvent(e);
else if constexpr (t == mtx::events::EventType::RoomMember)
return formatMemberEvent(e);
return tr("%1 changed unknown state event %2.") return tr("%1 changed unknown state event %2.")
.arg(displayName(QString::fromStdString(e.sender))) .arg(displayName(QString::fromStdString(e.sender)))
@ -2958,34 +2960,27 @@ TimelineModel::joinReplacementRoom(const QString &id)
} }
QString 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; mtx::events::StateEvent<mtx::events::state::Member> const *prevEvent = nullptr;
if (!event->unsigned_data.replaces_state.empty()) { if (!event.unsigned_data.replaces_state.empty()) {
auto tempPrevEvent = events.get(event->unsigned_data.replaces_state, event->event_id); auto tempPrevEvent = events.get(event.unsigned_data.replaces_state, event.event_id);
if (tempPrevEvent) { if (tempPrevEvent) {
prevEvent = prevEvent =
std::get_if<mtx::events::StateEvent<mtx::events::state::Member>>(tempPrevEvent); 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 name = utils::replaceEmoji(displayName(user));
QString rendered; QString rendered;
QString sender = QString::fromStdString(event->sender); QString sender = QString::fromStdString(event.sender);
QString senderName = utils::replaceEmoji(displayName(sender)); QString senderName = utils::replaceEmoji(displayName(sender));
// see table https://matrix.org/docs/spec/client_server/latest#m-room-member // see table https://matrix.org/docs/spec/client_server/latest#m-room-member
using namespace mtx::events::state; using namespace mtx::events::state;
switch (event->content.membership) { switch (event.content.membership) {
case Membership::Invite: case Membership::Invite:
rendered = tr("%1 invited %2.").arg(senderName, name); rendered = tr("%1 invited %2.").arg(senderName, name);
break; break;
@ -2994,9 +2989,8 @@ TimelineModel::formatMemberEvent(const QString &id)
QString oldName = utils::replaceEmoji( QString oldName = utils::replaceEmoji(
QString::fromStdString(prevEvent->content.display_name).toHtmlEscaped()); QString::fromStdString(prevEvent->content.display_name).toHtmlEscaped());
bool displayNameChanged = bool displayNameChanged = prevEvent->content.display_name != event.content.display_name;
prevEvent->content.display_name != event->content.display_name; bool avatarChanged = prevEvent->content.avatar_url != event.content.avatar_url;
bool avatarChanged = prevEvent->content.avatar_url != event->content.avatar_url;
if (displayNameChanged && avatarChanged) if (displayNameChanged && avatarChanged)
rendered = tr("%1 has changed their avatar and changed their " 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 // the case of nothing changed but join follows join shouldn't happen, so
// just show it as join // just show it as join
} else { } 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); rendered = tr("%1 joined.").arg(name);
else else
rendered = rendered =
tr("%1 joined via authorisation from %2's server.") tr("%1 joined via authorisation from %2's server.")
.arg(name, .arg(name,
QString::fromStdString(event->content.join_authorised_via_users_server)); QString::fromStdString(event.content.join_authorised_via_users_server));
} }
break; break;
case Membership::Leave: case Membership::Leave:
if (!prevEvent || prevEvent->content.membership == Membership::Join) { 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); rendered = tr("%1 left the room.").arg(name);
else else
rendered = tr("%2 kicked %1.").arg(name, senderName); rendered = tr("%2 kicked %1.").arg(name, senderName);
} else if (prevEvent->content.membership == Membership::Invite) { } 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); rendered = tr("%1 rejected their invite.").arg(name);
else else
rendered = tr("%2 revoked the invite to %1.").arg(name, senderName); rendered = tr("%2 revoked the invite to %1.").arg(name, senderName);
} else if (prevEvent->content.membership == Membership::Ban) { } else if (prevEvent->content.membership == Membership::Ban) {
rendered = tr("%2 unbanned %1.").arg(name, senderName); rendered = tr("%2 unbanned %1.").arg(name, senderName);
} else if (prevEvent->content.membership == Membership::Knock) { } 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); rendered = tr("%1 redacted their knock.").arg(name);
else else
rendered = tr("%2 rejected the knock from %1.").arg(name, senderName); rendered = tr("%2 rejected the knock from %1.").arg(name, senderName);
@ -3053,8 +3047,8 @@ TimelineModel::formatMemberEvent(const QString &id)
break; break;
} }
if (event->content.reason != "") { if (event.content.reason != "") {
rendered += " " + tr("Reason: %1").arg(QString::fromStdString(event->content.reason)); rendered += " " + tr("Reason: %1").arg(QString::fromStdString(event.content.reason));
} }
return rendered; return rendered;

View file

@ -310,7 +310,8 @@ public:
Q_INVOKABLE bool showAcceptKnockButton(const QString &id); Q_INVOKABLE bool showAcceptKnockButton(const QString &id);
Q_INVOKABLE void acceptKnock(const QString &id); Q_INVOKABLE void acceptKnock(const QString &id);
Q_INVOKABLE void joinReplacementRoom(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); Q_INVOKABLE QString formatJoinRuleEvent(const QString &id);
QString formatHistoryVisibilityEvent( QString formatHistoryVisibilityEvent(
const mtx::events::StateEvent<mtx::events::state::HistoryVisibility> &event) const; 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")) { if (QGuiApplication::platformName() != QStringLiteral("wayland")) {
// Selected by default // Selected by default
screenShareType_ = ScreenShareType::X11; screenShareType_ = ScreenShareType::X11;
if (screenShareTypes_.size() >= 2)
std::swap(screenShareTypes_[0], screenShareTypes_[1]); std::swap(screenShareTypes_[0], screenShareTypes_[1]);
} }
} }