Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
16 KB
Referenced Files
None
Subscribers
None
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

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)

Event Timeline