summary refs log tree commit diff
path: root/src
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 /src
parentTranslated using Weblate (German) (diff)
parentUpdate mtxclient commit hash (diff)
downloadnheko-7973fbce8c0db3eb82eafb4cc0f776ba10ec8a79.tar.xz
Merge pull request #1407 from Nheko-Reborn/ducktyping
Implement unknown msgtype functionality
Diffstat (limited to 'src')
-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
8 files changed, 191 insertions, 53 deletions
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, - ConfettiMessage, + ElementEffectMessage, EmoteMessage, FileMessage, ImageMessage, LocationMessage, NoticeMessage, TextMessage, + UnknownMessage, VideoMessage, Redacted, - UnknownMessage, + UnknownEvent, KeyVerificationRequest, KeyVerificationStart, KeyVerificationMac, @@ -266,6 +267,13 @@ public: }; Q_ENUM(Roles); + enum SpecialEffect + { + Confetti, + Rainfall, + }; + Q_DECLARE_FLAGS(SpecialEffects, SpecialEffect) + QHash<int, QByteArray> roleNames() const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; @@ -450,6 +458,8 @@ signals: void scrollToIndex(int index); void confetti(); void confettiDone(); + void rainfall(); + void rainfallDone(); void lastMessageChanged(); void notificationsChanged(); @@ -521,8 +531,8 @@ private: std::string last_event_id; std::string fullyReadEventId_; - // TODO (Loren): This should hopefully handle more than just confetti in the future bool needsSpecialEffects_ = false; + QFlags<SpecialEffect> specialEffects_; std::unique_ptr<RoomSummary, DeleteLaterDeleter> parentSummary = nullptr; bool parentChecked = false; @@ -530,6 +540,8 @@ private: friend void EventStore::refetchOnlineKeyBackupKeys(TimelineModel *room); }; +Q_DECLARE_OPERATORS_FOR_FLAGS(TimelineModel::SpecialEffects) + template<class T> void TimelineModel::sendMessageEvent(const T &content, mtx::events::EventType eventType)