Skip to content

Commit c40074d

Browse files
ipg0outfoxxed
authored andcommitted
service/notifications: add inline-reply action support
Signed-off-by: ipg0 <pyromancy00@gmail.com>
1 parent 3dfb7d8 commit c40074d

File tree

7 files changed

+79
-3
lines changed

7 files changed

+79
-3
lines changed

src/services/notifications/notification.cpp

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,29 @@ void Notification::close(NotificationCloseReason::Enum reason) {
7878
}
7979
}
8080

81+
void Notification::sendInlineReply(const QString& replyText) {
82+
if (!NotificationServer::instance()->support.inlineReply) {
83+
qCritical() << "Inline reply support disabled on server";
84+
return;
85+
}
86+
87+
if (!this->bHasInlineReply) {
88+
qCritical() << "Cannot send reply to notification without inline-reply action";
89+
return;
90+
}
91+
92+
if (this->isRetained()) {
93+
qCritical() << "Cannot send reply to destroyed notification" << this;
94+
return;
95+
}
96+
97+
NotificationServer::instance()->NotificationReplied(this->id(), replyText);
98+
99+
if (!this->bindableResident().value()) {
100+
this->close(NotificationCloseReason::Dismissed);
101+
}
102+
}
103+
81104
void Notification::updateProperties(
82105
const QString& appName,
83106
QString appIcon,
@@ -147,17 +170,27 @@ void Notification::updateProperties(
147170
this->bImage = imagePath;
148171
this->bHints = hints;
149172

150-
Qt::endPropertyUpdateGroup();
151-
152173
bool actionsChanged = false;
153174
auto deletedActions = QVector<NotificationAction*>();
154175

155176
if (actions.length() % 2 == 0) {
156177
int ai = 0;
157178
for (auto i = 0; i != actions.length(); i += 2) {
158-
ai = i / 2;
159179
const auto& identifier = actions.at(i);
160180
const auto& text = actions.at(i + 1);
181+
182+
if (identifier == "inline-reply" && NotificationServer::instance()->support.inlineReply) {
183+
if (this->bHasInlineReply) {
184+
qCWarning(logNotifications) << this << '(' << appName << ')'
185+
<< "sent an action set with duplicate inline-reply actions.";
186+
} else {
187+
this->bHasInlineReply = true;
188+
this->bInlineReplyPlaceholder = text;
189+
}
190+
// skip inserting this action into action list
191+
continue;
192+
}
193+
161194
auto* action = ai < this->mActions.length() ? this->mActions.at(ai) : nullptr;
162195

163196
if (action && identifier == action->identifier()) {
@@ -188,6 +221,8 @@ void Notification::updateProperties(
188221
<< "sent an action set of an invalid length.";
189222
}
190223

224+
Qt::endPropertyUpdateGroup();
225+
191226
if (actionsChanged) emit this->actionsChanged();
192227

193228
for (auto* action: deletedActions) {

src/services/notifications/notification.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ class Notification
107107
///
108108
/// This image is often something like a profile picture in instant messaging applications.
109109
Q_PROPERTY(QString image READ default NOTIFY imageChanged BINDABLE bindableImage);
110+
/// If true, the notification has an inline reply action.
111+
///
112+
/// A quick reply text field should be displayed and the reply can be sent using @@sendInlineReply().
113+
Q_PROPERTY(bool hasInlineReply READ default NOTIFY hasInlineReplyChanged BINDABLE bindableHasInlineReply);
114+
/// The placeholder text/button caption for the inline reply.
115+
Q_PROPERTY(QString inlineReplyPlaceholder READ default NOTIFY inlineReplyPlaceholderChanged BINDABLE bindableInlineReplyPlaceholder);
110116
/// All hints sent by the client application as a javascript object.
111117
/// Many common hints are exposed via other properties.
112118
Q_PROPERTY(QVariantMap hints READ default NOTIFY hintsChanged BINDABLE bindableHints);
@@ -124,6 +130,12 @@ class Notification
124130
/// explicitly closed by the user.
125131
Q_INVOKABLE void dismiss();
126132

133+
/// Send an inline reply to the notification with an inline reply action.
134+
/// > [!WARNING] This method can only be called if
135+
/// > @@hasInlineReply is true
136+
/// > and the server has @@NotificationServer.inlineReplySupported set to true.
137+
Q_INVOKABLE void sendInlineReply(const QString& replyText);
138+
127139
void updateProperties(
128140
const QString& appName,
129141
QString appIcon,
@@ -158,6 +170,8 @@ class Notification
158170
[[nodiscard]] QBindable<bool> bindableTransient() const { return &this->bTransient; };
159171
[[nodiscard]] QBindable<QString> bindableDesktopEntry() const { return &this->bDesktopEntry; };
160172
[[nodiscard]] QBindable<QString> bindableImage() const { return &this->bImage; };
173+
[[nodiscard]] QBindable<bool> bindableHasInlineReply() const { return &this->bHasInlineReply; };
174+
[[nodiscard]] QBindable<QString> bindableInlineReplyPlaceholder() const { return &this->bInlineReplyPlaceholder; };
161175
[[nodiscard]] QBindable<QVariantMap> bindableHints() const { return &this->bHints; };
162176

163177
[[nodiscard]] NotificationCloseReason::Enum closeReason() const;
@@ -182,6 +196,8 @@ class Notification
182196
void transientChanged();
183197
void desktopEntryChanged();
184198
void imageChanged();
199+
void hasInlineReplyChanged();
200+
void inlineReplyPlaceholderChanged();
185201
void hintsChanged();
186202

187203
private:
@@ -202,6 +218,8 @@ class Notification
202218
Q_OBJECT_BINDABLE_PROPERTY(Notification, bool, bTransient, &Notification::transientChanged);
203219
Q_OBJECT_BINDABLE_PROPERTY(Notification, QString, bDesktopEntry, &Notification::desktopEntryChanged);
204220
Q_OBJECT_BINDABLE_PROPERTY(Notification, QString, bImage, &Notification::imageChanged);
221+
Q_OBJECT_BINDABLE_PROPERTY(Notification, bool, bHasInlineReply, &Notification::hasInlineReplyChanged);
222+
Q_OBJECT_BINDABLE_PROPERTY(Notification, QString, bInlineReplyPlaceholder, &Notification::inlineReplyPlaceholderChanged);
205223
Q_OBJECT_BINDABLE_PROPERTY(Notification, QVariantMap, bHints, &Notification::hintsChanged);
206224
// clang-format on
207225

src/services/notifications/org.freedesktop.Notifications.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
<arg name="actionKey" type="s" direction="out"/>
3939
</signal>
4040

41+
<signal name="NotificationReplied">
42+
<arg name="id" type="u" direction="out"/>
43+
<arg name="replyText" type="s" direction="out"/>
44+
</signal>
45+
4146
<signal name="ActivationToken">
4247
<arg name="id" type="u" direction="out"/>
4348
<arg name="activationToken" type="s" direction="out"/>

src/services/notifications/qml.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@ void NotificationServerQml::setImageSupported(bool imageSupported) {
115115
emit this->imageSupportedChanged();
116116
}
117117

118+
bool NotificationServerQml::inlineReplySupported() const { return this->support.inlineReply; }
119+
120+
void NotificationServerQml::setInlineReplySupported(bool inlineReplySupported) {
121+
if (inlineReplySupported == this->support.inlineReply) return;
122+
this->support.inlineReply = inlineReplySupported;
123+
this->updateSupported();
124+
emit this->inlineReplySupportedChanged();
125+
}
126+
118127
QVector<QString> NotificationServerQml::extraHints() const { return this->support.extraHints; }
119128

120129
void NotificationServerQml::setExtraHints(QVector<QString> extraHints) {

src/services/notifications/qml.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ class NotificationServerQml: public PostReloadHook {
6565
Q_PROPERTY(bool actionIconsSupported READ actionIconsSupported WRITE setActionIconsSupported NOTIFY actionIconsSupportedChanged);
6666
/// If the notification server should advertise that it supports images. Defaults to false.
6767
Q_PROPERTY(bool imageSupported READ imageSupported WRITE setImageSupported NOTIFY imageSupportedChanged);
68+
/// If the notification server should advertise that it supports inline replies. Defaults to false.
69+
Q_PROPERTY(bool inlineReplySupported READ inlineReplySupported WRITE setInlineReplySupported NOTIFY inlineReplySupportedChanged);
6870
/// All notifications currently tracked by the server.
6971
QSDOC_TYPE_OVERRIDE(ObjectModel<qs::service::notifications::Notification>*);
7072
Q_PROPERTY(UntypedObjectModel* trackedNotifications READ trackedNotifications NOTIFY trackedNotificationsChanged);
@@ -103,6 +105,9 @@ class NotificationServerQml: public PostReloadHook {
103105
[[nodiscard]] bool imageSupported() const;
104106
void setImageSupported(bool imageSupported);
105107

108+
[[nodiscard]] bool inlineReplySupported() const;
109+
void setInlineReplySupported(bool inlineReplySupported);
110+
106111
[[nodiscard]] QVector<QString> extraHints() const;
107112
void setExtraHints(QVector<QString> extraHints);
108113

@@ -123,6 +128,7 @@ class NotificationServerQml: public PostReloadHook {
123128
void actionsSupportedChanged();
124129
void actionIconsSupportedChanged();
125130
void imageSupportedChanged();
131+
void inlineReplySupportedChanged();
126132
void extraHintsChanged();
127133
void trackedNotificationsChanged();
128134

src/services/notifications/server.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ QStringList NotificationServer::GetCapabilities() const {
155155
}
156156

157157
if (this->support.image) capabilities += "icon-static";
158+
if (this->support.inlineReply) capabilities += "inline-reply";
158159

159160
capabilities += this->support.extraHints;
160161

src/services/notifications/server.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct NotificationServerSupport {
2323
bool actions = false;
2424
bool actionIcons = false;
2525
bool image = false;
26+
bool inlineReply = false;
2627
QVector<QString> extraHints;
2728
};
2829

@@ -60,6 +61,7 @@ class NotificationServer: public QObject {
6061
// NOLINTBEGIN
6162
void NotificationClosed(quint32 id, quint32 reason);
6263
void ActionInvoked(quint32 id, QString action);
64+
void NotificationReplied(quint32 id, QString replyText);
6365
// NOLINTEND
6466

6567
private slots:

0 commit comments

Comments
 (0)