summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoren Burkholder <55629213+LorenDB@users.noreply.github.com>2022-12-10 10:17:15 -0500
committerGitHub <noreply@github.com>2022-12-10 10:17:15 -0500
commitfa0c14b84681b94ff2136d6b7480c8b7283ad96a (patch)
tree483acc16811d00af5d13a5c196fe64f2aa3ff491
parentc2eb2f7ad116f04168e264cad0770e683aa6e6b1 (diff)
confetti (#1243)
* 🎉 (confetti) message support. Thanks @LorenDB !
-rw-r--r--CMakeLists.txt4
-rw-r--r--io.github.NhekoReborn.Nheko.yaml2
-rw-r--r--resources/confettiparticle.pngbin0 -> 4732 bytes
-rw-r--r--resources/confettiparticle.svg49
-rw-r--r--resources/qml/MessageView.qml1
-rw-r--r--resources/qml/TimelineView.qml64
-rw-r--r--resources/qml/delegates/MessageDelegate.qml14
-rw-r--r--resources/res.qrc1
-rw-r--r--src/CommandCompleter.cpp12
-rw-r--r--src/CommandCompleter.h2
-rw-r--r--src/UserSettingsPage.cpp30
-rw-r--r--src/UserSettingsPage.h7
-rw-r--r--src/timeline/InputBar.cpp25
-rw-r--r--src/timeline/InputBar.h1
-rw-r--r--src/timeline/TimelineModel.cpp37
-rw-r--r--src/timeline/TimelineModel.h8
16 files changed, 251 insertions, 6 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 60f16d9a..d7219318 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -594,8 +594,8 @@ if(USE_BUNDLED_MTXCLIENT)
include(FetchContent)
FetchContent_Declare(
MatrixClient
- GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
- GIT_TAG 7155cbb8ed3289f2cc7269da7607b0dd012f471b
+ GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
+ GIT_TAG 13285437739413587a22272865d1e684e1959579
)
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 f41f71b4..c82513e4 100644
--- a/io.github.NhekoReborn.Nheko.yaml
+++ b/io.github.NhekoReborn.Nheko.yaml
@@ -182,7 +182,7 @@ modules:
buildsystem: cmake-ninja
name: mtxclient
sources:
- - commit: 7155cbb8ed3289f2cc7269da7607b0dd012f471b
+ - commit: 13285437739413587a22272865d1e684e1959579
#tag: v0.8.2
type: git
url: https://github.com/Nheko-Reborn/mtxclient.git
diff --git a/resources/confettiparticle.png b/resources/confettiparticle.png
new file mode 100644
index 00000000..808429d8
--- /dev/null
+++ b/resources/confettiparticle.png
Binary files differ
diff --git a/resources/confettiparticle.svg b/resources/confettiparticle.svg
new file mode 100644
index 00000000..80b85629
--- /dev/null
+++ b/resources/confettiparticle.svg
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ version="1.0"
+ width="32.000000pt"
+ height="10.000000pt"
+ viewBox="0 0 32.000000 10.000000"
+ preserveAspectRatio="xMidYMid meet"
+ id="svg4"
+ sodipodi:docname="confettiparticle.svg"
+ inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ id="namedview6"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="pt"
+ showgrid="false"
+ inkscape:zoom="17.7"
+ inkscape:cx="7.1751412"
+ inkscape:cy="0.64971751"
+ inkscape:window-width="1920"
+ inkscape:window-height="1015"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg4" />
+ <g
+ transform="translate(0.000000,10.000000) scale(0.100000,-0.100000)"
+ fill="#000000"
+ stroke="none"
+ id="g2" />
+ <rect
+ style="fill:#ffffff;stroke-width:0.75"
+ id="rect307"
+ width="32.033897"
+ height="10"
+ x="0"
+ y="0" />
+</svg>
diff --git a/resources/qml/MessageView.qml b/resources/qml/MessageView.qml
index 8e6ad8d2..e3e02ee9 100644
--- a/resources/qml/MessageView.qml
+++ b/resources/qml/MessageView.qml
@@ -59,7 +59,6 @@ Item {
onCountChanged: {
// Mark timeline as read
if (atYEnd && room) model.currentIndex = 0;
-
}
ScrollBar.vertical: scrollbar
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index ab1bbc28..dff23700 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -13,6 +13,7 @@ import Qt.labs.platform 1.1 as Platform
import QtQuick 2.15
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
+import QtQuick.Particles 2.15
import QtQuick.Window 2.13
import im.nheko 1.0
import im.nheko.EmojiModel 1.0
@@ -25,6 +26,8 @@ Item {
property bool showBackButton: false
clip: true
+ onRoomChanged: if (room != null) room.triggerSpecialEffects()
+
Shortcut {
sequence: StandardKey.Close
onActivated: Rooms.resetCurrentRoom()
@@ -298,6 +301,58 @@ Item {
onClicked: Rooms.resetCurrentRoom()
}
+ ParticleSystem { id: confettiParticleSystem }
+
+ 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
+ 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: 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 {
anchors.fill: parent
roomid: room ? room.roomId : ""
@@ -321,6 +376,15 @@ Item {
timelineRoot.destroyOnClose(dialog);
}
+ function onConfetti()
+ {
+ if (!Settings.fancyEffects)
+ return
+
+ confettiEmitter.pulse(parent.height * 2)
+ room.markSpecialEffectsDone()
+ }
+
target: room
}
diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml
index a2a44cb2..3725be05 100644
--- a/resources/qml/delegates/MessageDelegate.qml
+++ b/resources/qml/delegates/MessageDelegate.qml
@@ -76,6 +76,20 @@ Item {
}
DelegateChoice {
+ roleValue: MtxEvent.ConfettiMessage
+
+ TextMessage {
+ formatted: d.formattedBody
+ body: d.body
+ isOnlyEmoji: d.isOnlyEmoji
+ isReply: d.isReply
+ keepFullText: d.keepFullText
+ metadataWidth: d.metadataWidth
+ }
+
+ }
+
+ DelegateChoice {
roleValue: MtxEvent.NoticeMessage
NoticeMessage {
diff --git a/resources/res.qrc b/resources/res.qrc
index 595dd5a7..e9320a1b 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -193,6 +193,7 @@
<file>qml/voip/PlaceCall.qml</file>
<file>qml/voip/ScreenShare.qml</file>
<file>qml/voip/VideoCall.qml</file>
+ <file>confettiparticle.svg</file>
</qresource>
<qresource prefix="/media">
<file>media/ring.ogg</file>
diff --git a/src/CommandCompleter.cpp b/src/CommandCompleter.cpp
index 96dfeace..307defa4 100644
--- a/src/CommandCompleter.cpp
+++ b/src/CommandCompleter.cpp
@@ -82,6 +82,10 @@ CommandCompleter::data(const QModelIndex &index, int role) const
return QString("/notice ");
case RainbowNotice:
return QString("/rainbownotice ");
+ case Confetti:
+ return QString("/confetti ");
+ case RainbowConfetti:
+ return QString("/rainbowconfetti ");
case Goto:
return QString("/goto ");
case ConvertToDm:
@@ -145,6 +149,10 @@ CommandCompleter::data(const QModelIndex &index, int role) const
return tr("/notice [message]");
case RainbowNotice:
return tr("/rainbownotice [message]");
+ case Confetti:
+ return tr("/confetti [message]");
+ case RainbowConfetti:
+ return tr("/rainbowconfetti [message]");
case Goto:
return tr("/goto ($eventid|message index|matrix:r/room/e/event)");
case ConvertToDm:
@@ -207,6 +215,10 @@ CommandCompleter::data(const QModelIndex &index, int role) const
return tr("Send a bot message.");
case RainbowNotice:
return tr("Send a bot message in rainbow colors.");
+ case Confetti:
+ return tr("Send a message with confetti.");
+ case RainbowConfetti:
+ return tr("Send a message in rainbow colors with confetti.");
case Goto:
return tr("Go to this event or link.");
case ConvertToDm:
diff --git a/src/CommandCompleter.h b/src/CommandCompleter.h
index 360bff73..08272a19 100644
--- a/src/CommandCompleter.h
+++ b/src/CommandCompleter.h
@@ -44,6 +44,8 @@ public:
RainbowMe,
Notice,
RainbowNotice,
+ Confetti,
+ RainbowConfetti,
Goto,
ConvertToDm,
ConvertToRoom,
diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp
index 7082d4e2..145b3b8f 100644
--- a/src/UserSettingsPage.cpp
+++ b/src/UserSettingsPage.cpp
@@ -92,6 +92,7 @@ UserSettings::load(std::optional<QString> profile)
decryptNotifications_ =
settings.value(QStringLiteral("user/decrypt_notifications"), true).toBool();
spaceNotifications_ = settings.value(QStringLiteral("user/space_notifications"), true).toBool();
+ fancyEffects_ = settings.value(QStringLiteral("user/fancy_effects"), true).toBool();
privacyScreen_ = settings.value(QStringLiteral("user/privacy_screen"), false).toBool();
privacyScreenTimeout_ =
settings.value(QStringLiteral("user/privacy_screen_timeout"), 0).toInt();
@@ -460,6 +461,16 @@ UserSettings::setSpaceNotifications(bool state)
}
void
+UserSettings::setFancyEffects(bool state)
+{
+ if (state == fancyEffects_)
+ return;
+ fancyEffects_ = state;
+ emit fancyEffectsChanged(state);
+ save();
+}
+
+void
UserSettings::setPrivacyScreen(bool state)
{
if (state == privacyScreen_) {
@@ -822,6 +833,7 @@ UserSettings::save()
settings.setValue(QStringLiteral("decrypt_sidebar"), decryptSidebar_);
settings.setValue(QStringLiteral("decrypt_notificatons"), decryptNotifications_);
settings.setValue(QStringLiteral("space_notifications"), spaceNotifications_);
+ settings.setValue(QStringLiteral("fancy_effects"), fancyEffects_);
settings.setValue(QStringLiteral("privacy_screen"), privacyScreen_);
settings.setValue(QStringLiteral("privacy_screen_timeout"), privacyScreenTimeout_);
settings.setValue(QStringLiteral("mobile_mode"), mobileMode_);
@@ -976,6 +988,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
return tr("Decrypt notifications");
case SpaceNotifications:
return tr("Show message counts for communities and tags");
+ case FancyEffects:
+ return tr("Display fancy effects such as confetti");
case PrivacyScreen:
return tr("Privacy Screen");
case PrivacyScreenTimeout:
@@ -1112,6 +1126,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
return i->decryptNotifications();
case SpaceNotifications:
return i->spaceNotifications();
+ case FancyEffects:
+ return i->fancyEffects();
case PrivacyScreen:
return i->privacyScreen();
case PrivacyScreenTimeout:
@@ -1276,6 +1292,9 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
case SpaceNotifications:
return tr("Choose where to show the total number of notifications contained within a "
"community or tag.");
+ case FancyEffects:
+ return tr("Some messages can be sent with fancy effects. For example, messages sent "
+ "with '/confetti' will show confetti on screen.");
case PrivacyScreen:
return tr("When the window loses focus, the timeline will\nbe blurred.");
case MobileMode:
@@ -1388,6 +1407,7 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
case UseOnlineKeyBackup:
case ExposeDBusApi:
case SpaceNotifications:
+ case FancyEffects:
return Toggle;
case Profile:
case UserId:
@@ -1716,6 +1736,13 @@ UserSettingsModel::setData(const QModelIndex &index, const QVariant &value, int
} else
return false;
}
+ case FancyEffects: {
+ if (value.userType() == QMetaType::Bool) {
+ i->setFancyEffects(value.toBool());
+ return true;
+ } else
+ return false;
+ }
case PrivacyScreen: {
if (value.userType() == QMetaType::Bool) {
i->setPrivacyScreen(value.toBool());
@@ -2037,6 +2064,9 @@ UserSettingsModel::UserSettingsModel(QObject *p)
connect(s.get(), &UserSettings::spaceNotificationsChanged, this, [this]() {
emit dataChanged(index(SpaceNotifications), index(SpaceNotifications), {Value});
});
+ connect(s.get(), &UserSettings::fancyEffectsChanged, this, [this]() {
+ emit dataChanged(index(FancyEffects), index(FancyEffects), {Value});
+ });
connect(s.get(), &UserSettings::trayChanged, this, [this]() {
emit dataChanged(index(Tray), index(Tray), {Value});
emit dataChanged(index(StartInTray), index(StartInTray), {Enabled});
diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h
index 3bd0f833..4d805bb7 100644
--- a/src/UserSettingsPage.h
+++ b/src/UserSettingsPage.h
@@ -64,6 +64,7 @@ class UserSettings final : public QObject
NOTIFY decryptNotificationsChanged)
Q_PROPERTY(bool spaceNotifications READ spaceNotifications WRITE setSpaceNotifications NOTIFY
spaceNotificationsChanged)
+ Q_PROPERTY(bool fancyEffects READ fancyEffects WRITE setFancyEffects NOTIFY fancyEffectsChanged)
Q_PROPERTY(
bool privacyScreen READ privacyScreen WRITE setPrivacyScreen NOTIFY privacyScreenChanged)
Q_PROPERTY(int privacyScreenTimeout READ privacyScreenTimeout WRITE setPrivacyScreenTimeout
@@ -171,6 +172,7 @@ public:
void setDecryptSidebar(bool state);
void setDecryptNotifications(bool state);
void setSpaceNotifications(bool state);
+ void setFancyEffects(bool state);
void setPrivacyScreen(bool state);
void setPrivacyScreenTimeout(int state);
void setPresence(Presence state);
@@ -214,6 +216,7 @@ public:
bool decryptSidebar() const { return decryptSidebar_; }
bool decryptNotifications() const { return decryptNotifications_; }
bool spaceNotifications() const { return spaceNotifications_; }
+ bool fancyEffects() const { return fancyEffects_; }
bool privacyScreen() const { return privacyScreen_; }
int privacyScreenTimeout() const { return privacyScreenTimeout_; }
bool markdown() const { return markdown_; }
@@ -295,6 +298,7 @@ signals:
void decryptSidebarChanged(bool state);
void decryptNotificationsChanged(bool state);
void spaceNotificationsChanged(bool state);
+ void fancyEffectsChanged(bool state);
void privacyScreenChanged(bool state);
void privacyScreenTimeoutChanged(int state);
void timelineMaxWidthChanged(int state);
@@ -360,6 +364,7 @@ private:
bool decryptSidebar_;
bool decryptNotifications_;
bool spaceNotifications_;
+ bool fancyEffects_;
bool privacyScreen_;
int privacyScreenTimeout_;
bool shareKeysWithTrustedUsers_;
@@ -442,6 +447,8 @@ class UserSettingsModel final : public QAbstractListModel
InvertEnterKey,
Bubbles,
SmallAvatars,
+ FancyEffects,
+
SidebarSection,
GroupView,
SortByImportance,
diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp
index a9afb01c..94955152 100644
--- a/src/timeline/InputBar.cpp
+++ b/src/timeline/InputBar.cpp
@@ -535,6 +535,27 @@ InputBar::notice(const QString &msg, bool rainbowify)
}
void
+InputBar::confetti(const QString &body, bool rainbowify)
+{
+ auto html = utils::markdownToHtml(body, rainbowify);
+
+ mtx::events::msg::Confetti confetti;
+ confetti.body = body.trimmed().toStdString();
+
+ if (html != body.trimmed().toHtmlEscaped() &&
+ ChatPage::instance()->userSettings()->markdown()) {
+ confetti.formatted_body = html.toStdString();
+ confetti.format = "org.matrix.custom.html";
+ // Remove markdown links by completer
+ confetti.body = replaceMatrixToMarkdownLink(body.trimmed()).toStdString();
+ }
+
+ confetti.relations = generateRelations();
+
+ room->sendMessageEvent(confetti, mtx::events::EventType::RoomMessage);
+}
+
+void
InputBar::image(const QString &filename,
const std::optional<mtx::crypto::EncryptedFile> &file,
const QString &url,
@@ -777,6 +798,10 @@ InputBar::command(const QString &command, QString args)
notice(args, false);
} else if (command == QLatin1String("rainbownotice")) {
notice(args, true);
+ } else if (command == QLatin1String("confetti")) {
+ confetti(args, false);
+ } else if (command == QLatin1String("rainbowconfetti")) {
+ confetti(args, true);
} 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 eced7cb8..125591d4 100644
--- a/src/timeline/InputBar.h
+++ b/src/timeline/InputBar.h
@@ -230,6 +230,7 @@ signals:
private:
void emote(const QString &body, bool rainbowify);
void notice(const QString &body, bool rainbowify);
+ void confetti(const QString &body, bool rainbowify);
void 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 f61214fd..fe8a78ef 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -53,6 +53,10 @@ struct RoomEventType
{
return qml_mtx_events::EventType::AudioMessage;
}
+ qml_mtx_events::EventType operator()(const mtx::events::Event<mtx::events::msg::Confetti> &)
+ {
+ return qml_mtx_events::EventType::ConfettiMessage;
+ }
qml_mtx_events::EventType operator()(const mtx::events::Event<mtx::events::msg::Emote> &)
{
return qml_mtx_events::EventType::EmoteMessage;
@@ -346,6 +350,7 @@ 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::EmoteMessage:
case qml_mtx_events::FileMessage:
case qml_mtx_events::ImageMessage:
@@ -1025,9 +1030,18 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline)
} else if (std::holds_alternative<StateEvent<state::space::Parent>>(e)) {
this->parentChecked = false;
emit parentSpaceChanged();
- }
+ } 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("🎊"))
+ needsSpecialEffects_ = true;
+ } else if (std::holds_alternative<RoomEvent<mtx::events::msg::Confetti>>(e))
+ needsSpecialEffects_ = true;
}
+ if (needsSpecialEffects_)
+ emit confetti();
+
updateLastMessage();
}
@@ -1957,6 +1971,22 @@ TimelineModel::copyLinkToEvent(const QString &eventId) const
QGuiApplication::clipboard()->setText(link);
}
+void
+TimelineModel::triggerSpecialEffects()
+{
+ if (needsSpecialEffects_) {
+ // Note (Loren): Without the timer, this apparently emits before QML is ready
+ QTimer::singleShot(1, this, [this] { emit confetti(); });
+ needsSpecialEffects_ = false;
+ }
+}
+
+void
+TimelineModel::markSpecialEffectsDone()
+{
+ needsSpecialEffects_ = false;
+}
+
QString
TimelineModel::formatTypingUsers(const std::vector<QString> &users, const QColor &bg)
{
@@ -2790,7 +2820,8 @@ TimelineModel::setEdit(const QString &newEdit)
auto msgType = mtx::accessors::msg_type(e);
if (msgType == mtx::events::MessageType::Text ||
msgType == mtx::events::MessageType::Notice ||
- msgType == mtx::events::MessageType::Emote) {
+ msgType == mtx::events::MessageType::Emote ||
+ msgType == mtx::events::MessageType::Confetti) {
auto relInfo = relatedInfo(newEdit);
auto editText = relInfo.quoted_body;
@@ -2811,6 +2842,8 @@ 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
input()->setText(editText);
} else {
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 9daeeb3a..2352be1f 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -100,6 +100,7 @@ enum EventType
Widget,
/// m.room.message
AudioMessage,
+ ConfettiMessage,
EmoteMessage,
FileMessage,
ImageMessage,
@@ -419,6 +420,9 @@ public slots:
QString scrollTarget() const;
+ void triggerSpecialEffects();
+ void markSpecialEffectsDone();
+
private slots:
void addPendingMessage(mtx::events::collections::TimelineEvents event);
void scrollTimerEvent();
@@ -438,6 +442,7 @@ signals:
void paginationInProgressChanged(const bool);
void newCallEvent(const mtx::events::collections::TimelineEvents &event);
void scrollToIndex(int index);
+ void confetti();
void lastMessageChanged();
void notificationsChanged();
@@ -509,6 +514,9 @@ 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;
+
std::unique_ptr<RoomSummary, DeleteLaterDeleter> parentSummary = nullptr;
bool parentChecked = false;
};