Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85831508
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
16 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/contents/ui/event-types/Simple.qml b/src/contents/ui/event-types/Simple.qml
index ddea1ef..f7506eb 100644
--- a/src/contents/ui/event-types/Simple.qml
+++ b/src/contents/ui/event-types/Simple.qml
@@ -1,95 +1,95 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2021 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import org.kde.kirigami 2.13 as Kirigami
ColumnLayout {
property var event
property var sender
default property var children
property var localEchoIndicator: Kirigami.Icon {
objectName: 'localEchoIndicator'
source: event.isSending ? 'state-sync' : 'state-warning'
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
Accessible.role: event.isSending ? Accessible.StaticText : Accessible.Button
Accessible.name: event.isSending ? l10n.get('event-sending') : l10n.get('event-resend')
ToolTip.text: event.isSending ? l10n.get('event-sending') : l10n.get('event-send-failed')
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hoverHandler.hovered
HoverHandler { id: hoverHandler }
TapHandler {
objectName: 'resendEventButton'
onTapped: {
- console.log('tapped');
+ room.resendMessage(event.txnId);
}
enabled: event.isFailed
}
}
property var contentMaxWidth: {
(parent.width
// avatar size and margins
- iconSize
- 2 * Kirigami.Units.largeSpacing
- 2 * Kirigami.Units.largeSpacing
// local echo indicator
- (event.isLocalEcho ? Kirigami.Units.iconSizes.medium + Kirigami.Units.largeSpacing : 0)
)
}
id: layout
RowLayout {
width: parent.width
Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.topMargin: Kirigami.Units.largeSpacing
Kirigami.Avatar {
//width: iconSize
//height: iconSize
id: avatar
Layout.alignment: Qt.AlignTop
sourceSize.width: iconSize
sourceSize.height: iconSize
source: sender.avatarMxcUri ? matrixSdk.mxcUriToHttp(sender.avatarMxcUri) : ''
name: sender.name || sender.userId
TapHandler {
onTapped: {
console.log('avatar tapped');
pageStack.push(Qt.resolvedUrl("qrc:///UserPage.qml"), {
userId: sender.userId,
user: sender,
})
}
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.largeSpacing
Label {
text: sender.name || sender.userId
}
RowLayout {
id: container
Layout.fillWidth: true
data: (event.isLocalEcho ? [layout.localEchoIndicator] : []).concat(layout.children)
}
}
}
}
diff --git a/src/matrix-event.cpp b/src/matrix-event.cpp
index 2749b33..6bc9729 100644
--- a/src/matrix-event.cpp
+++ b/src/matrix-event.cpp
@@ -1,63 +1,66 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2021 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#include "matrix-event.hpp"
#include "helper.hpp"
using namespace Kazv;
static std::optional<LocalEchoDesc> getLocalEcho(std::variant<Event, LocalEchoDesc> event)
{
if (std::holds_alternative<LocalEchoDesc>(event)) {
return std::get<LocalEchoDesc>(event);
} else {
return std::nullopt;
}
}
static Event getEvent(std::variant<Event, LocalEchoDesc> event)
{
if (std::holds_alternative<LocalEchoDesc>(event)) {
return std::get<LocalEchoDesc>(event).event;
} else {
return std::get<Event>(event);
}
}
MatrixEvent::MatrixEvent(lager::reader<std::variant<Event, LocalEchoDesc>> event, QObject *parent)
: QObject(parent)
, m_localEcho(event.map(getLocalEcho))
, m_event(event.map(getEvent))
, LAGER_QT(eventId)(m_event.xform(zug::map([](Event e) { return e.id(); }) | strToQt))
, LAGER_QT(sender)(m_event.xform(zug::map([](Event e) { return e.sender(); }) | strToQt))
, LAGER_QT(type)(m_event.xform(zug::map([](Event e) { return e.type(); }) | strToQt))
, LAGER_QT(stateKey)(m_event.xform(zug::map([](Event e) { return e.stateKey(); }) | strToQt))
, LAGER_QT(content)(m_event.xform(zug::map([](Event e) { return e.content().get().get<QJsonObject>(); })))
, LAGER_QT(encrypted)(m_event.xform(zug::map([](Event e) { return e.encrypted(); })))
, LAGER_QT(isState)(m_event.map([](Event e) { return e.isState(); }))
, LAGER_QT(unsignedData)(m_event.map([](Event e) {
auto j = e.raw();
if (j.get().contains("unsigned")) {
return j.get()["unsigned"].template get<QJsonObject>();
} else {
return QJsonObject();
}
}))
, LAGER_QT(isLocalEcho)(m_localEcho.map([](const auto &maybe) { return maybe.has_value(); }))
, LAGER_QT(isSending)(m_localEcho.map([](const auto &maybe) {
return maybe.has_value() && maybe.value().status == LocalEchoDesc::Sending;
}))
, LAGER_QT(isFailed)(m_localEcho.map([](const auto &maybe) {
return maybe.has_value() && maybe.value().status == LocalEchoDesc::Failed;
}))
+ , LAGER_QT(txnId)(m_localEcho.map([](const auto &maybe) {
+ return maybe.has_value() ? QString::fromStdString(maybe.value().txnId) : QStringLiteral("");
+ }))
{
}
MatrixEvent::~MatrixEvent() = default;
diff --git a/src/matrix-event.hpp b/src/matrix-event.hpp
index eeefa05..c433c75 100644
--- a/src/matrix-event.hpp
+++ b/src/matrix-event.hpp
@@ -1,45 +1,46 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2021 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <libkazv-config.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#include <QObject>
#include <QQmlEngine>
#include <lager/extra/qt.hpp>
#include <client/room/room.hpp>
#include "qt-json.hpp"
class MatrixEvent : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
lager::reader<std::optional<Kazv::LocalEchoDesc>> m_localEcho;
lager::reader<Kazv::Event> m_event;
public:
explicit MatrixEvent(lager::reader<std::variant<Kazv::Event, Kazv::LocalEchoDesc>> event, QObject *parent = 0);
~MatrixEvent() override;
LAGER_QT_READER(QString, eventId);
LAGER_QT_READER(QString, sender);
LAGER_QT_READER(QString, type);
LAGER_QT_READER(QString, stateKey);
LAGER_QT_READER(QJsonObject, content);
LAGER_QT_READER(bool, encrypted);
LAGER_QT_READER(bool, isState);
LAGER_QT_READER(QJsonObject, unsignedData);
LAGER_QT_READER(bool, isLocalEcho);
LAGER_QT_READER(bool, isSending);
LAGER_QT_READER(bool, isFailed);
+ LAGER_QT_READER(QString, txnId);
};
diff --git a/src/matrix-room.cpp b/src/matrix-room.cpp
index 42039c6..9625d7d 100644
--- a/src/matrix-room.cpp
+++ b/src/matrix-room.cpp
@@ -1,94 +1,99 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#include <lager/setter.hpp>
#include <nlohmann/json.hpp>
#include <event.hpp>
#include <QFile>
#include <QMimeDatabase>
#include "matrix-room.hpp"
#include "matrix-room-timeline.hpp"
#include "matrix-room-member.hpp"
#include "helper.hpp"
MatrixRoom::MatrixRoom(Kazv::Room room, QObject *parent)
: QObject(parent)
, m_room(room)
, m_memberNames(m_room.members())
, LAGER_QT(roomId)(m_room.roomId().xform(strToQt))
, LAGER_QT(name)(m_room.nameOpt()[lager::lenses::or_default].xform(strToQt))
, LAGER_QT(heroNames)(m_room.heroDisplayNames().xform(strListToQt))
, LAGER_QT(avatarMxcUri)(m_room.avatarMxcUri().xform(strToQt))
, LAGER_QT(localDraft)(lager::with_setter(
m_room.localDraft().xform(strToQt).make(),
[this](QString localDraft) {
m_room.setLocalDraft(localDraft.toStdString());
},
lager::automatic_tag{}))
, LAGER_QT(memberNames)(m_memberNames.xform(strListToQt))
{
}
MatrixRoom::~MatrixRoom() = default;
MatrixRoomTimeline *MatrixRoom::timeline() const
{
return new MatrixRoomTimeline(m_room);
}
void MatrixRoom::sendTextMessage(QString text) const
{
m_room.sendTextMessage(text.toStdString());
}
void MatrixRoom::sendMediaFileMessage(QString fileName, QString mimeType, qint64 fileSize, QString mxcUri) const
{
auto j = makeMediaFileMessageJson(fileName, mimeType, fileSize, mxcUri);
Kazv::Event e{j};
m_room.sendMessage(e);
}
+void MatrixRoom::resendMessage(QString txnId) const
+{
+ m_room.resendMessage(txnId.toStdString());
+}
+
nlohmann::json MatrixRoom::makeMediaFileMessageJson(QString fileName, QString mimeType, qint64 fileSize, QString mxcUri) const
{
static auto available_msgtype = std::array<std::string, 3>{"m.audio", "m.video", "m.image"};
auto try_msgtype = std::find(available_msgtype.begin(), available_msgtype.end(),
QStringLiteral("m.").append(mimeType.split(QChar('/'))[0]).toStdString());
std::string msgtype;
if (try_msgtype == available_msgtype.end()) {
msgtype = "m.file";
} else {
msgtype = *try_msgtype;
}
return nlohmann::json {
{"type", "m.room.message"},
{"content", {
{"msgtype", msgtype},
{"body", fileName.toStdString()},
{"url", mxcUri.toStdString()},
{"info", {
{"size", fileSize},
{"mimetype", mimeType.toStdString()}
}}
}}
};
}
MatrixRoomMember *MatrixRoom::memberAt(int index) const
{
return new MatrixRoomMember(m_room.memberEventByCursor(
m_memberNames[index][lager::lenses::or_default]));
}
MatrixRoomMember *MatrixRoom::member(QString userId) const
{
return new MatrixRoomMember(m_room.memberEventFor(userId.toStdString()));
}
diff --git a/src/matrix-room.hpp b/src/matrix-room.hpp
index 827052a..e8d5202 100644
--- a/src/matrix-room.hpp
+++ b/src/matrix-room.hpp
@@ -1,58 +1,60 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <libkazv-config.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#include <QObject>
#include <QQmlEngine>
#include <QUrl>
#include <lager/extra/qt.hpp>
#include <client/room/room.hpp>
class MatrixRoomTimeline;
class MatrixRoomMember;
class MatrixRoom : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
Kazv::Room m_room;
lager::reader<immer::flex_vector<std::string>> m_memberNames;
public:
explicit MatrixRoom(Kazv::Room room, QObject *parent = 0);
~MatrixRoom() override;
LAGER_QT_READER(QString, roomId);
LAGER_QT_READER(QString, name);
LAGER_QT_READER(QStringList, heroNames);
LAGER_QT_READER(QString, avatarMxcUri);
LAGER_QT_CURSOR(QString, localDraft);
LAGER_QT_READER(QStringList, memberNames);
Q_INVOKABLE MatrixRoomMember *memberAt(int index) const;
Q_INVOKABLE MatrixRoomMember *member(QString userId) const;
Q_INVOKABLE MatrixRoomTimeline *timeline() const;
Q_INVOKABLE void sendTextMessage(QString text) const;
Q_INVOKABLE void sendMediaFileMessage(QString fileName, QString mimeType,
qint64 fileSize, QString mxcUri) const;
+ Q_INVOKABLE void resendMessage(QString txnId) const;
+
protected:
nlohmann::json makeMediaFileMessageJson(QString fileName, QString mimeType,
qint64 fileSize, QString mxcUri) const;
};
diff --git a/src/tests/matrix-room-timeline-test.cpp b/src/tests/matrix-room-timeline-test.cpp
index 5b8e861..495cbe3 100644
--- a/src/tests/matrix-room-timeline-test.cpp
+++ b/src/tests/matrix-room-timeline-test.cpp
@@ -1,54 +1,56 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#include <memory>
#include <QtTest>
#include <matrix-room-timeline.hpp>
#include <matrix-sdk.hpp>
#include <matrix-room-list.hpp>
#include <matrix-room.hpp>
#include <matrix-event.hpp>
#include "test-model.hpp"
#include "test-utils.hpp"
using namespace Kazv;
class MatrixRoomTimelineTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testLocalEcho();
};
void MatrixRoomTimelineTest::testLocalEcho()
{
auto model = makeTestModel();
std::unique_ptr<MatrixSdk> sdk{makeTestSdk(model)};
auto roomList = toUniquePtr(sdk->roomList());
auto room = toUniquePtr(roomList->room("!foo:tusooa.xyz"));
auto timeline = toUniquePtr(room->timeline());
QCOMPARE(timeline->count(), 2);
auto first = toUniquePtr(timeline->at(0));
QCOMPARE(first->isSending(), true);
QCOMPARE(first->isLocalEcho(), true);
QCOMPARE(first->isFailed(), false);
+ QCOMPARE(first->txnId(), "some-txn-id");
auto second = toUniquePtr(timeline->at(1));
QCOMPARE(second->isSending(), false);
QCOMPARE(second->isLocalEcho(), true);
QCOMPARE(second->isFailed(), true);
+ QCOMPARE(second->txnId(), "some-other-txn-id");
}
QTEST_MAIN(MatrixRoomTimelineTest)
#include "matrix-room-timeline-test.moc"
diff --git a/src/tests/quick-tests/tst_EventView.qml b/src/tests/quick-tests/tst_EventView.qml
index f9f7234..c8475bb 100644
--- a/src/tests/quick-tests/tst_EventView.qml
+++ b/src/tests/quick-tests/tst_EventView.qml
@@ -1,83 +1,98 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.3
import QtTest 1.0
import '../../contents/ui' as Kazv
import 'test-helpers.js' as Helpers
Item {
id: item
width: 800
height: 600
+ property var room: ({
+ test_resendMessageCalled: 0,
+ test_resendMessageLastTxnId: '',
+ resendMessage: (txnId) => {
+ ++item.room.test_resendMessageCalled;
+ item.room.test_resendMessageLastTxnId = txnId;
+ }
+ })
property var l10n: Helpers.fluentMock
property var localEcho: ({
eventId: '',
sender: '',
type: 'm.room.message',
stateKey: '',
content: {
msgtype: 'm.text',
body: 'some body',
},
encrypted: false,
isState: false,
unsignedData: {},
isLocalEcho: true,
isSending: true,
isFailed: false,
+ txnId: 'some-txn-id',
})
property var sender: ({
membership: 'join',
userId: '@foo:tusooa.xyz',
name: 'foo',
avatarMxcUri: '',
})
Kazv.EventView {
id: eventView
event: item.localEcho
sender: item.sender
}
TestCase {
id: eventViewTest
name: 'EventViewTest'
when: windowShown
function init() {
eventView.event = item.localEcho;
+ item.room.test_resendMessageCalled = 0;
+ item.room.test_resendMessageLastTxnId = '';
}
function test_localEcho() {
const indicator = findChild(eventView, 'localEchoIndicator');
verify(indicator);
verify(indicator.source === 'state-sync');
verify(indicator.Accessible.name === l10n.get('event-sending'));
const resendButton = findChild(eventView, 'resendEventButton');
verify(resendButton);
verify(!resendButton.enabled);
}
function test_localEchoFailed() {
const failedLocalEcho = Object.assign({}, item.localEcho, { isSending: false, isFailed: true, });
eventView.event = failedLocalEcho;
const indicator = findChild(eventView, 'localEchoIndicator');
verify(indicator);
verify(indicator.source === 'state-warning');
verify(indicator.Accessible.name === l10n.get('event-resend'));
const resendButton = findChild(eventView, 'resendEventButton');
verify(resendButton);
verify(resendButton.enabled);
+
+ mouseClick(indicator);
+ tryVerify(() => item.room.test_resendMessageCalled == 1);
+ tryVerify(() => item.room.test_resendMessageLastTxnId == 'some-txn-id');
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Nov 1, 4:49 PM (12 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1803182
Default Alt Text
(16 KB)
Attached To
Mode
rK kazv
Attached
Detach File
Event Timeline