summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDeepBlueV7.X <nicolas.werner@hotmail.de>2023-04-10 23:19:44 +0000
committerGitHub <noreply@github.com>2023-04-10 23:19:44 +0000
commit7973fbce8c0db3eb82eafb4cc0f776ba10ec8a79 (patch)
treec5cb9c8eccb061747919ac9c4574f207c62a8b42
parentb0f3d39e1e629d581c19f68ad8efcb78b357502f (diff)
parentbdee662bbd58544b36dab66963858c4fe6ae825d (diff)
Merge pull request #1407 from Nheko-Reborn/ducktyping
Implement unknown msgtype functionality
-rw-r--r--CMakeLists.txt2
-rw-r--r--io.github.NhekoReborn.Nheko.yaml2
-rw-r--r--resources/qml/TimelineView.qml78
-rw-r--r--resources/qml/delegates/MessageDelegate.qml18
-rw-r--r--resources/qml/ui/TimelineEffects.qml112
-rw-r--r--resources/res.qrc1
-rw-r--r--src/CommandCompleter.cpp12
-rw-r--r--src/CommandCompleter.h2
-rw-r--r--src/Utils.cpp33
-rw-r--r--src/Utils.h38
-rw-r--r--src/timeline/InputBar.cpp59
-rw-r--r--src/timeline/InputBar.h2
-rw-r--r--src/timeline/TimelineModel.cpp80
-rw-r--r--src/timeline/TimelineModel.h18
14 files changed, 345 insertions, 112 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 06af4bbe..20fcb029 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -602,7 +602,7 @@ if(USE_BUNDLED_MTXCLIENT)
FetchContent_Declare(
MatrixClient
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
- GIT_TAG c8849cd033bb59bee39f3fb2eaca953853731eb2
+ GIT_TAG dd2bdbd104ae8f70f82da9ff7b4b60007fc105c3
)
set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "")
set(BUILD_LIB_TESTS OFF CACHE INTERNAL "")
diff --git a/io.github.NhekoReborn.Nheko.yaml b/io.github.NhekoReborn.Nheko.yaml
index 3bcfdf52..97d0b770 100644
--- a/io.github.NhekoReborn.Nheko.yaml
+++ b/io.github.NhekoReborn.Nheko.yaml
@@ -213,7 +213,7 @@ modules:
buildsystem: cmake-ninja
name: mtxclient
sources:
- - commit: c8849cd033bb59bee39f3fb2eaca953853731eb2
+ - commit: dd2bdbd104ae8f70f82da9ff7b4b60007fc105c3
#tag: v0.9.2
type: git
url: https://github.com/Nheko-Reborn/mtxclient.git
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index a146a991..30ad9292 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -364,60 +364,10 @@ Item {
onClicked: Rooms.resetCurrentRoom()
}
- ParticleSystem { id: confettiParticleSystem
- Component.onCompleted: pause();
- paused: !shouldEffectsRun
- }
-
- Emitter {
- id: confettiEmitter
-
- width: parent.width * 3/4
- enabled: false
- anchors.horizontalCenter: parent.horizontalCenter
- y: parent.height
- emitRate: Math.min(400 * Math.sqrt(parent.width * parent.height) / 870, 1000)
- lifeSpan: 15000
- system: confettiParticleSystem
- maximumEmitted: 500
- velocityFromMovement: 8
- size: 16
- sizeVariation: 4
- velocity: PointDirection {
- x: 0
- y: -Math.min(450 * parent.height / 700, 1000)
- xVariation: Math.min(4 * parent.width / 7, 450)
- yVariation: 250
- }
- }
+ TimelineEffects {
+ id: timelineEffects
- ImageParticle {
- system: confettiParticleSystem
- source: "qrc:/confettiparticle.svg"
- rotationVelocity: 0
- rotationVelocityVariation: 360
- colorVariation: 1
- color: "white"
- entryEffect: ImageParticle.None
- xVector: PointDirection {
- x: 1
- y: 0
- xVariation: 0.2
- yVariation: 0.2
- }
- yVector: PointDirection {
- x: 0
- y: 0.5
- xVariation: 0.2
- yVariation: 0.2
- }
- }
-
- Gravity {
- system: confettiParticleSystem
anchors.fill: parent
- magnitude: 350
- angle: 90
}
NhekoDropArea {
@@ -428,7 +378,7 @@ Item {
Timer {
id: effectsTimer
onTriggered: shouldEffectsRun = false;
- interval: confettiEmitter.lifeSpan
+ interval: timelineEffects.maxLifespan
repeat: false
running: false
}
@@ -462,7 +412,7 @@ Item {
return
shouldEffectsRun = true;
- confettiEmitter.pulse(parent.height * 2)
+ timelineEffects.pulseConfetti()
room.markSpecialEffectsDone()
}
@@ -471,7 +421,25 @@ Item {
if (!Settings.fancyEffects)
return
- effectsTimer.start();
+ effectsTimer.restart();
+ }
+
+ function onRainfall()
+ {
+ if (!Settings.fancyEffects)
+ return
+
+ shouldEffectsRun = true;
+ timelineEffects.pulseRainfall()
+ room.markSpecialEffectsDone()
+ }
+
+ function onRainfallDone()
+ {
+ if (!Settings.fancyEffects)
+ return
+
+ effectsTimer.restart();
}
target: room
diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml
index d298fa4e..c0bcec0d 100644
--- a/resources/qml/delegates/MessageDelegate.qml
+++ b/resources/qml/delegates/MessageDelegate.qml
@@ -51,7 +51,7 @@ Item {
width: parent.width? parent.width: 0 // this should get rid of "cannot read property 'width' of null"
DelegateChoice {
- roleValue: MtxEvent.UnknownMessage
+ roleValue: MtxEvent.UnknownEvent
Placeholder {
typeString: d.typeString
@@ -102,7 +102,21 @@ Item {
}
DelegateChoice {
- roleValue: MtxEvent.ConfettiMessage
+ roleValue: MtxEvent.UnknownMessage
+
+ TextMessage {
+ formatted: d.formattedBody
+ body: d.body
+ isOnlyEmoji: d.isOnlyEmoji
+ isReply: d.isReply
+ keepFullText: d.keepFullText
+ metadataWidth: d.metadataWidth
+ }
+
+ }
+
+ DelegateChoice {
+ roleValue: MtxEvent.ElementEffectMessage
TextMessage {
formatted: d.formattedBody
diff --git a/resources/qml/ui/TimelineEffects.qml b/resources/qml/ui/TimelineEffects.qml
new file mode 100644
index 00000000..aaff04a0
--- /dev/null
+++ b/resources/qml/ui/TimelineEffects.qml
@@ -0,0 +1,112 @@
+// SPDX-FileCopyrightText: Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import QtQuick 2.15
+import QtQuick.Particles 2.15
+
+Item {
+ readonly property int maxLifespan: Math.max(confettiEmitter.lifeSpan, rainfallEmitter.lifeSpan)
+
+ function pulseConfetti()
+ {
+ confettiEmitter.pulse(parent.height * 2)
+ }
+
+ function pulseRainfall()
+ {
+ rainfallEmitter.pulse(parent.height * 3.3)
+ }
+
+ ParticleSystem {
+ id: particleSystem
+
+ Component.onCompleted: pause();
+ paused: !shouldEffectsRun
+ }
+
+ Emitter {
+ id: confettiEmitter
+
+ group: "confetti"
+ width: parent.width * 3/4
+ enabled: false
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: parent.height
+ emitRate: Math.min(400 * Math.sqrt(parent.width * parent.height) / 870, 1000)
+ lifeSpan: 15000
+ system: particleSystem
+ maximumEmitted: 500
+ velocityFromMovement: 8
+ size: 16
+ sizeVariation: 4
+ velocity: PointDirection {
+ x: 0
+ y: -Math.min(450 * parent.height / 700, 1000)
+ xVariation: Math.min(4 * parent.width / 7, 450)
+ yVariation: 250
+ }
+ }
+
+ ImageParticle {
+ system: particleSystem
+ groups: ["confetti"]
+ source: "qrc:/confettiparticle.svg"
+ rotationVelocity: 0
+ rotationVelocityVariation: 360
+ colorVariation: 1
+ color: "white"
+ entryEffect: ImageParticle.None
+ xVector: PointDirection {
+ x: 1
+ y: 0
+ xVariation: 0.2
+ yVariation: 0.2
+ }
+ yVector: PointDirection {
+ x: 0
+ y: 0.5
+ xVariation: 0.2
+ yVariation: 0.2
+ }
+ }
+
+ Gravity {
+ system: particleSystem
+ groups: ["confetti"]
+ anchors.fill: parent
+ magnitude: 350
+ angle: 90
+ }
+
+ Emitter {
+ id: rainfallEmitter
+
+ group: "rain"
+ width: parent.width
+ enabled: false
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: -60
+ emitRate: parent.width / 50
+ lifeSpan: 10000
+ system: particleSystem
+ velocity: PointDirection {
+ x: 0
+ y: 300
+ xVariation: 0
+ yVariation: 75
+ }
+
+ ItemParticle {
+ system: particleSystem
+ groups: ["rain"]
+ fade: false
+ delegate: Rectangle {
+ width: 2
+ height: 30 + 30 * Math.random()
+ radius: 2
+ color: "#0099ff"
+ }
+ }
+ }
+}
diff --git a/resources/res.qrc b/resources/res.qrc
index faa90495..3f1b2b65 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -197,6 +197,7 @@
<file>qml/voip/VideoCall.qml</file>
<file>confettiparticle.svg</file>
<file>qml/delegates/EncryptionEnabled.qml</file>
+ <file>qml/ui/TimelineEffects.qml</file>
</qresource>
<qresource prefix="/media">
<file>media/ring.ogg</file>
diff --git a/src/CommandCompleter.cpp b/src/CommandCompleter.cpp
index 2ec427d6..8123b8e6 100644
--- a/src/CommandCompleter.cpp
+++ b/src/CommandCompleter.cpp
@@ -87,6 +87,10 @@ CommandCompleter::data(const QModelIndex &index, int role) const
return QStringLiteral("/confetti ");
case RainbowConfetti:
return QStringLiteral("/rainbowconfetti ");
+ case Rainfall:
+ return QStringLiteral("/rainfall ");
+ case Msgtype:
+ return QStringLiteral("/msgtype ");
case Goto:
return QStringLiteral("/goto ");
case ConvertToDm:
@@ -156,6 +160,10 @@ CommandCompleter::data(const QModelIndex &index, int role) const
return tr("/confetti [message]");
case RainbowConfetti:
return tr("/rainbowconfetti [message]");
+ case Rainfall:
+ return tr("/rainfall [message]");
+ case Msgtype:
+ return tr("/msgtype <msgtype> [message]");
case Goto:
return tr("/goto <message reference>");
case ConvertToDm:
@@ -225,6 +233,10 @@ CommandCompleter::data(const QModelIndex &index, int role) const
return tr("Send a message with confetti.");
case RainbowConfetti:
return tr("Send a message in rainbow colors with confetti.");
+ case Rainfall:
+ return tr("Send a message with rain.");
+ case Msgtype:
+ return tr("Send a message with a custom message type.");
case Goto:
return tr("Go to a specific message using an event id, index or matrix: link");
case ConvertToDm:
diff --git a/src/CommandCompleter.h b/src/CommandCompleter.h
index fcbbe3e5..4f27fe29 100644
--- a/src/CommandCompleter.h
+++ b/src/CommandCompleter.h
@@ -46,6 +46,8 @@ public:
RainbowNotice,
Confetti,
RainbowConfetti,
+ Rainfall,
+ Msgtype,
Goto,
ConvertToDm,
ConvertToRoom,
diff --git a/src/Utils.cpp b/src/Utils.cpp
index c5b2abd1..2bf8eb3b 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -215,19 +215,20 @@ utils::getMessageDescription(const TimelineEvent &event,
const QString &localUser,
const QString &displayName)
{
- using Audio = mtx::events::RoomEvent<mtx::events::msg::Audio>;
- using Emote = mtx::events::RoomEvent<mtx::events::msg::Emote>;
- using File = mtx::events::RoomEvent<mtx::events::msg::File>;
- using Image = mtx::events::RoomEvent<mtx::events::msg::Image>;
- using Notice = mtx::events::RoomEvent<mtx::events::msg::Notice>;
- using Text = mtx::events::RoomEvent<mtx::events::msg::Text>;
- using Video = mtx::events::RoomEvent<mtx::events::msg::Video>;
- using Confetti = mtx::events::RoomEvent<mtx::events::msg::Confetti>;
- using CallInvite = mtx::events::RoomEvent<mtx::events::voip::CallInvite>;
- using CallAnswer = mtx::events::RoomEvent<mtx::events::voip::CallAnswer>;
- using CallHangUp = mtx::events::RoomEvent<mtx::events::voip::CallHangUp>;
- using CallReject = mtx::events::RoomEvent<mtx::events::voip::CallReject>;
- using Encrypted = mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>;
+ using Audio = mtx::events::RoomEvent<mtx::events::msg::Audio>;
+ using Emote = mtx::events::RoomEvent<mtx::events::msg::Emote>;
+ using File = mtx::events::RoomEvent<mtx::events::msg::File>;
+ using Image = mtx::events::RoomEvent<mtx::events::msg::Image>;
+ using Notice = mtx::events::RoomEvent<mtx::events::msg::Notice>;
+ using Text = mtx::events::RoomEvent<mtx::events::msg::Text>;
+ using Unknown = mtx::events::RoomEvent<mtx::events::msg::Unknown>;
+ using Video = mtx::events::RoomEvent<mtx::events::msg::Video>;
+ using ElementEffect = mtx::events::RoomEvent<mtx::events::msg::ElementEffect>;
+ using CallInvite = mtx::events::RoomEvent<mtx::events::voip::CallInvite>;
+ using CallAnswer = mtx::events::RoomEvent<mtx::events::voip::CallAnswer>;
+ using CallHangUp = mtx::events::RoomEvent<mtx::events::voip::CallHangUp>;
+ using CallReject = mtx::events::RoomEvent<mtx::events::voip::CallReject>;
+ using Encrypted = mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>;
if (std::holds_alternative<Audio>(event)) {
return createDescriptionInfo<Audio>(event, localUser, displayName);
@@ -241,10 +242,12 @@ utils::getMessageDescription(const TimelineEvent &event,
return createDescriptionInfo<Notice>(event, localUser, displayName);
} else if (std::holds_alternative<Text>(event)) {
return createDescriptionInfo<Text>(event, localUser, displayName);
+ } else if (std::holds_alternative<Unknown>(event)) {
+ return createDescriptionInfo<Unknown>(event, localUser, displayName);
} else if (std::holds_alternative<Video>(event)) {
return createDescriptionInfo<Video>(event, localUser, displayName);
- } else if (std::holds_alternative<Confetti>(event)) {
- return createDescriptionInfo<Confetti>(event, localUser, displayName);
+ } else if (std::holds_alternative<ElementEffect>(event)) {
+ return createDescriptionInfo<ElementEffect>(event, localUser, displayName);
} else if (std::holds_alternative<CallInvite>(event)) {
return createDescriptionInfo<CallInvite>(event, localUser, displayName);
} else if (std::holds_alternative<CallAnswer>(event)) {
diff --git a/src/Utils.h b/src/Utils.h
index 2bf01f84..2c8988e5 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -95,20 +95,21 @@ messageDescription(const QString &username = QString(),
const QString &body = QString(),
const bool isLocal = false)
{
- using Audio = mtx::events::RoomEvent<mtx::events::msg::Audio>;
- using Emote = mtx::events::RoomEvent<mtx::events::msg::Emote>;
- using File = mtx::events::RoomEvent<mtx::events::msg::File>;
- using Image = mtx::events::RoomEvent<mtx::events::msg::Image>;
- using Notice = mtx::events::RoomEvent<mtx::events::msg::Notice>;
- using Sticker = mtx::events::Sticker;
- using Text = mtx::events::RoomEvent<mtx::events::msg::Text>;
- using Video = mtx::events::RoomEvent<mtx::events::msg::Video>;
- using Confetti = mtx::events::RoomEvent<mtx::events::msg::Confetti>;
- using CallInvite = mtx::events::RoomEvent<mtx::events::voip::CallInvite>;
- using CallAnswer = mtx::events::RoomEvent<mtx::events::voip::CallAnswer>;
- using CallHangUp = mtx::events::RoomEvent<mtx::events::voip::CallHangUp>;
- using CallReject = mtx::events::RoomEvent<mtx::events::voip::CallReject>;
- using Encrypted = mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>;
+ using Audio = mtx::events::RoomEvent<mtx::events::msg::Audio>;
+ using Emote = mtx::events::RoomEvent<mtx::events::msg::Emote>;
+ using File = mtx::events::RoomEvent<mtx::events::msg::File>;
+ using Image = mtx::events::RoomEvent<mtx::events::msg::Image>;
+ using Notice = mtx::events::RoomEvent<mtx::events::msg::Notice>;
+ using Sticker = mtx::events::Sticker;
+ using Text = mtx::events::RoomEvent<mtx::events::msg::Text>;
+ using Unknown = mtx::events::RoomEvent<mtx::events::msg::Unknown>;
+ using Video = mtx::events::RoomEvent<mtx::events::msg::Video>;
+ using ElementEffect = mtx::events::RoomEvent<mtx::events::msg::ElementEffect>;
+ using CallInvite = mtx::events::RoomEvent<mtx::events::voip::CallInvite>;
+ using CallAnswer = mtx::events::RoomEvent<mtx::events::voip::CallAnswer>;
+ using CallHangUp = mtx::events::RoomEvent<mtx::events::voip::CallHangUp>;
+ using CallReject = mtx::events::RoomEvent<mtx::events::voip::CallReject>;
+ using Encrypted = mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>;
if (std::is_same<T, Audio>::value) {
if (isLocal)
@@ -149,20 +150,21 @@ messageDescription(const QString &username = QString(),
return QCoreApplication::translate("message-description sent:",
"%1 sent a notification")
.arg(username);
- } else if (std::is_same<T, Text>::value) {
+ } else if (std::is_same<T, Text>::value || std::is_same<T, Unknown>::value) {
if (isLocal)
return QCoreApplication::translate("message-description sent:", "You: %1").arg(body);
else
return QCoreApplication::translate("message-description sent:", "%1: %2")
.arg(username, body);
- } else if (std::is_same<T, Confetti>::value) {
+ } else if (std::is_same<T, ElementEffect>::value) {
if (body.isEmpty()) {
+ // TODO: what is the best way to handle this?
if (isLocal)
return QCoreApplication::translate("message-description sent:",
- "You sent some confetti");
+ "You sent a chat effect");
else
return QCoreApplication::translate("message-description sent:",
- "%1 sent some confetti")
+ "%1 sent a chat effect")
.arg(username);
} else {
if (isLocal)
diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp
index b27128e0..fe8b8e48 100644
--- a/src/timeline/InputBar.cpp
+++ b/src/timeline/InputBar.cpp
@@ -281,6 +281,8 @@ InputBar::updateTextContentProperties(const QString &t)
QStringLiteral("rainbownotice"),
QStringLiteral("confetti"),
QStringLiteral("rainbowconfetti"),
+ QStringLiteral("rainfall"),
+ QStringLiteral("msgtype"),
QStringLiteral("goto"),
QStringLiteral("converttodm"),
QStringLiteral("converttoroom")};
@@ -607,8 +609,9 @@ InputBar::confetti(const QString &body, bool rainbowify)
{
auto html = utils::markdownToHtml(body, rainbowify);
- mtx::events::msg::Confetti confetti;
- confetti.body = body.trimmed().toStdString();
+ mtx::events::msg::ElementEffect confetti;
+ confetti.msgtype = "nic.custom.confetti";
+ confetti.body = body.trimmed().toStdString();
if (html != body.trimmed().toHtmlEscaped() &&
ChatPage::instance()->userSettings()->markdown()) {
@@ -624,6 +627,54 @@ InputBar::confetti(const QString &body, bool rainbowify)
}
void
+InputBar::rainfall(const QString &body)
+{
+ auto html = utils::markdownToHtml(body);
+
+ mtx::events::msg::Unknown rain;
+ rain.msgtype = "io.element.effect.rainfall";
+ rain.body = body.trimmed().toStdString();
+
+ if (html != body.trimmed().toHtmlEscaped() &&
+ ChatPage::instance()->userSettings()->markdown()) {
+ nlohmann::json j;
+ j["formatted_body"] = html.toStdString();
+ j["format"] = "org.matrix.custom.html";
+ rain.content = j.dump();
+ // Remove markdown links by completer
+ rain.body = replaceMatrixToMarkdownLink(body.trimmed()).toStdString();
+ }
+
+ rain.relations = generateRelations();
+
+ room->sendMessageEvent(rain, mtx::events::EventType::RoomMessage);
+}
+
+void
+InputBar::customMsgtype(const QString &msgtype, const QString &body)
+{
+ auto html = utils::markdownToHtml(body);
+
+ mtx::events::msg::Unknown msg;
+ msg.msgtype = msgtype.toStdString();
+ msg.body = body.trimmed().toStdString();
+
+ if (html != body.trimmed().toHtmlEscaped() &&
+ ChatPage::instance()->userSettings()->markdown()) {
+ nlohmann::json j;
+ j["formatted_body"] = html.toStdString();
+ j["format"] = "org.matrix.custom.html";
+ msg.content = j.dump();
+ // Remove markdown links by completer
+ msg.body = replaceMatrixToMarkdownLink(body.trimmed()).toStdString();
+ }
+
+ msg.relations = generateRelations();
+
+ room->sendMessageEvent(msg, mtx::events::EventType::RoomMessage);
+}
+
+void
InputBar::image(const QString &filename,
const std::optional<mtx::crypto::EncryptedFile> &file,
const QString &url,
@@ -890,6 +941,10 @@ InputBar::command(const QString &command, QString args)
confetti(args, false);
} else if (command == QLatin1String("rainbowconfetti")) {
confetti(args, true);
+ } else if (command == QLatin1String("rainfall")) {
+ rainfall(args);
+ } else if (command == QLatin1String("msgtype")) {
+ customMsgtype(args.section(' ', 0, 0), args.section(' ', 1, -1));
} else if (command == QLatin1String("goto")) {
// Goto has three different modes:
// 1 - Going directly to a given event ID
diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h
index acafd964..b2db377f 100644
--- a/src/timeline/InputBar.h
+++ b/src/timeline/InputBar.h
@@ -242,6 +242,8 @@ private:
void emote(const QString &body, bool rainbowify);
void notice(const QString &body, bool rainbowify);
void confetti(const QString &body, bool rainbowify);
+ void rainfall(const QString &body);
+ void customMsgtype(const QString &msgtype, const QString &body);
bool command(const QString &name, QString args);
void image(const QString &filename,
const std::optional<mtx::crypto::EncryptedFile> &file,
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index cb9fb7fa..918d1c0b 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -54,9 +54,9 @@ struct RoomEventType
return qml_mtx_events::EventType::AudioMessage;
}
constexpr qml_mtx_events::EventType
- operator()(const mtx::events::Event<mtx::events::msg::Confetti> &)
+ operator()(const mtx::events::Event<mtx::events::msg::ElementEffect> &)
{
- return qml_mtx_events::EventType::ConfettiMessage;
+ return qml_mtx_events::EventType::ElementEffectMessage;
}
constexpr qml_mtx_events::EventType
operator()(const mtx::events::Event<mtx::events::msg::Emote> &)
@@ -84,6 +84,11 @@ struct RoomEventType
return qml_mtx_events::EventType::TextMessage;
}
constexpr qml_mtx_events::EventType
+ operator()(const mtx::events::Event<mtx::events::msg::Unknown> &)
+ {
+ return qml_mtx_events::EventType::UnknownMessage;
+ }
+ constexpr qml_mtx_events::EventType
operator()(const mtx::events::Event<mtx::events::msg::Video> &)
{
return qml_mtx_events::EventType::VideoMessage;
@@ -203,7 +208,7 @@ qml_mtx_events::toRoomEventType(mtx::events::EventType e)
case EventType::RoomMember:
return qml_mtx_events::EventType::Member;
case EventType::RoomMessage:
- return qml_mtx_events::EventType::UnknownMessage;
+ return qml_mtx_events::EventType::UnknownEvent;
case EventType::RoomName:
return qml_mtx_events::EventType::Name;
case EventType::RoomPowerLevels:
@@ -239,7 +244,7 @@ qml_mtx_events::toRoomEventType(mtx::events::EventType e)
case EventType::Unsupported:
return qml_mtx_events::EventType::Unsupported;
default:
- return qml_mtx_events::EventType::UnknownMessage;
+ return qml_mtx_events::EventType::UnknownEvent;
}
}
@@ -362,16 +367,17 @@ qml_mtx_events::fromRoomEventType(qml_mtx_events::EventType t)
return mtx::events::EventType::SpaceChild;
/// m.room.message
case qml_mtx_events::AudioMessage:
- case qml_mtx_events::ConfettiMessage:
+ case qml_mtx_events::ElementEffectMessage:
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::UnknownMessage:
case qml_mtx_events::VideoMessage:
case qml_mtx_events::Redacted:
- case qml_mtx_events::UnknownMessage:
+ case qml_mtx_events::UnknownEvent:
case qml_mtx_events::KeyVerificationRequest:
case qml_mtx_events::KeyVerificationStart:
case qml_mtx_events::KeyVerificationMac:
@@ -1075,14 +1081,32 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
} else if (std::holds_alternative<RoomEvent<mtx::events::msg::Text>>(e)) {
if (auto msg = QString::fromStdString(
std::get<RoomEvent<mtx::events::msg::Text>>(e).content.body);
- msg.contains("🎉") || msg.contains("🎊"))
+ msg.contains("🎉") || msg.contains("🎊")) {
+ needsSpecialEffects_ = true;
+ specialEffects_.setFlag(Confetti);
+ }
+ } else if (std::holds_alternative<RoomEvent<mtx::events::msg::Unknown>>(e)) {
+ if (auto msg = QString::fromStdString(
+ std::get<RoomEvent<mtx::events::msg::Unknown>>(e).content.body);
+ msg.contains("🎉") || msg.contains("🎊")) {
+ needsSpecialEffects_ = true;
+ specialEffects_.setFlag(Confetti);
+ }
+ } else if (std::holds_alternative<RoomEvent<mtx::events::msg::ElementEffect>>(e)) {
+ if (auto msgtype =
+ std::get<RoomEvent<mtx::events::msg::ElementEffect>>(e).content.msgtype;
+ msgtype == "nic.custom.confetti") {
needsSpecialEffects_ = true;
- } else if (std::holds_alternative<RoomEvent<mtx::events::msg::Confetti>>(e))
- needsSpecialEffects_ = true;
+ specialEffects_.setFlag(Confetti);
+ } else if (msgtype == "io.element.effect.rainfall") {
+ needsSpecialEffects_ = true;
+ specialEffects_.setFlag(Rainfall);
+ }
+ }
}
if (needsSpecialEffects_)
- emit confetti();
+ triggerSpecialEffects();
if (avatarChanged)
emit roomAvatarUrlChanged();
@@ -2045,7 +2069,14 @@ TimelineModel::triggerSpecialEffects()
{
if (needsSpecialEffects_) {
// Note (Loren): Without the timer, this apparently emits before QML is ready
- QTimer::singleShot(1, this, [this] { emit confetti(); });
+ if (specialEffects_.testFlag(Confetti)) {
+ QTimer::singleShot(1, this, [this] { emit confetti(); });
+ specialEffects_.setFlag(Confetti, false);
+ }
+ if (specialEffects_.testFlag(Rainfall)) {
+ QTimer::singleShot(1, this, [this] { emit rainfall(); });
+ specialEffects_.setFlag(Rainfall, false);
+ }
needsSpecialEffects_ = false;
}
}
@@ -2055,6 +2086,10 @@ TimelineModel::markSpecialEffectsDone()
{
needsSpecialEffects_ = false;
emit confettiDone();
+ emit rainfallDone();
+
+ specialEffects_.setFlag(Confetti, false);
+ specialEffects_.setFlag(Rainfall, false);
}
QString
@@ -2917,7 +2952,8 @@ TimelineModel::setEdit(const QString &newEdit)
if (msgType == mtx::events::MessageType::Text ||
msgType == mtx::events::MessageType::Notice ||
msgType == mtx::events::MessageType::Emote ||
- msgType == mtx::events::MessageType::Confetti) {
+ msgType == mtx::events::MessageType::ElementEffect ||
+ msgType == mtx::events::MessageType::Unknown) {
auto relInfo = relatedInfo(newEdit);
auto editText = relInfo.quoted_body;
@@ -2938,9 +2974,23 @@ TimelineModel::setEdit(const QString &newEdit)
if (msgType == mtx::events::MessageType::Emote)
input()->setText("/me " + editText);
- else if (msgType == mtx::events::MessageType::Confetti)
- input()->setText("/confetti" + editText);
- else
+ else if (msgType == mtx::events::MessageType::ElementEffect) {
+ auto u =
+ std::get_if<mtx::events::RoomEvent<mtx::events::msg::ElementEffect>>(&e);
+ auto msgtypeString = u ? u->content.msgtype : "";
+ if (msgtypeString == "io.element.effect.rainfall")
+ input()->setText("/rainfall " + editText);
+ else if (msgtypeString == "nic.custom.confetti")
+ input()->setText("/confetti " + editText);
+ else
+ input()->setText("/msgtype " + QString::fromStdString(msgtypeString) + " " +
+ editText);
+ } else if (msgType == mtx::events::MessageType::Unknown) {
+ auto u = std::get_if<mtx::events::RoomEvent<mtx::events::msg::Unknown>>(&e);
+ input()->setText("/msgtype " +
+ (u ? QString::fromStdString(u->content.msgtype) : "") + " " +
+ editText);
+ } else
input()->setText(editText);
} else {
input()->setText(QLatin1String(""));
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 03cb9ecb..ef845bb5 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -99,16 +99,17 @@ enum EventType
Widget,
/// m.room.message
AudioMessage,
- ConfettiMessag