KUnifiedPush

main.qml
1/*
2 SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
3 SPDX-License-Identifier: LGPL-2.0-or-later
4*/
5
6import QtQuick
7import QtQuick.Layouts
8import QtQuick.Controls as QQC2
9import org.kde.kirigami as Kirigami
10import org.kde.kirigami.delegates as KirigamiDelegates
11import org.kde.kcmutils as KCM
12import org.kde.kunifiedpush.kcm
13
14KCM.ScrollViewKCM {
15 id: root
16 readonly property var pushProviderConfig: kcm.pushProviderConfiguration(pushProviderBox.currentValue)
17
18 headerPaddingEnabled: false
19
20 header: ColumnLayout {
21 spacing: 0
22
23 // type of distributor, and if it is our own one, distributor status information
24 Kirigami.InlineMessage {
25 Layout.fillWidth: true
26 showCloseButton: false
27 type: Kirigami.MessageType.Error
28 text: i18n("There is no push notification service running!")
29 icon.name: "dialog-error"
30 visible: !kcm.hasDistributor
31 position: Kirigami.InlineMessage.Position.Header
32 }
34 Layout.fillWidth: true
35 showCloseButton: false
36 type: Kirigami.MessageType.Information
37 text: i18n("There is a 3rd party push notification service running. Push notifications are available, but cannot be configured here.")
38 icon.name: "dialog-information"
39 visible: !kcm.hasKDEDistributor && kcm.hasDistributor
40 position: Kirigami.InlineMessage.Position.Header
41 }
43 Layout.fillWidth: true
44 showCloseButton: false
45 type: Kirigami.MessageType.Positive
46 text: i18n("<b>Online</b><br>Connected to the push notification server and operational.")
47 icon.name: "media-playback-playing"
48 visible: kcm.hasKDEDistributor && kcm.distributorStatus == DistributorStatus.Connected
49 position: Kirigami.InlineMessage.Position.Header
50 }
52 Layout.fillWidth: true
53 showCloseButton: false
54 type: Kirigami.MessageType.Information
55 text: i18n("<b>Idle</b><br>There are no applications using push notifications.")
56 icon.name: "media-playback-paused"
57 visible: kcm.hasKDEDistributor && kcm.distributorStatus == DistributorStatus.Idle
58 position: Kirigami.InlineMessage.Position.Header
59 }
61 Layout.fillWidth: true
62 showCloseButton: false
63 type: Kirigami.MessageType.Warning
64 text: kcm.distributorErrorMessage ?
65 i18n("<b>Offline</b><br>Network connection to the server could not be established (%1).", kcm.distributorErrorMessage) :
66 i18n("<b>Offline</b><br>Network connection to the server could not be established.")
67 icon.name: "network-disconnect"
68 visible: kcm.hasKDEDistributor && kcm.distributorStatus == DistributorStatus.NoNetwork
69 position: Kirigami.InlineMessage.Position.Header
70 }
72 Layout.fillWidth: true
73 showCloseButton: false
74 type: Kirigami.MessageType.Error
75 text: kcm.distributorErrorMessage ?
76 i18n("<b>Offline</b><br>Could not authenticate at the server (%1).", kcm.distributorErrorMessage) :
77 i18n("<b>Offline</b><br>Could not authenticate at the server.")
78 icon.name: "dialog-error"
79 visible: kcm.hasKDEDistributor && kcm.distributorStatus == DistributorStatus.AuthenticationError
80 position: Kirigami.InlineMessage.Position.Header
81 }
83 Layout.fillWidth: true
84 showCloseButton: false
85 type: Kirigami.MessageType.Warning
86 text: i18n("<b>Offline</b><br>Push notifications are not set up yet.")
87 icon.name: "configure"
88 visible: kcm.hasKDEDistributor && kcm.distributorStatus == DistributorStatus.NoSetup
89 position: Kirigami.InlineMessage.Position.Header
90 }
91
92 QQC2.Control {
93 padding: Kirigami.Units.largeSpacing
94 visible: kcm.hasKDEDistributor
95 Layout.fillWidth: true
96
97 background: Rectangle {
98 Kirigami.Theme.colorSet: Kirigami.Theme.Window
99 Kirigami.Theme.inherit: false
100
101 color: Kirigami.Theme.backgroundColor
102 }
103
104 // push provider configuration
105 contentItem: ColumnLayout {
106 spacing: 0
107
109 id: topForm
110 Layout.fillWidth: true
111 Layout.topMargin: Kirigami.Units.largeSpacing
112 QQC2.ComboBox {
113 id: pushProviderBox
114 Kirigami.FormData.label: i18n("Push provider:")
115 model: ListModel {
116 ListElement { text: "Gotify"; key: "Gotify" }
117 ListElement { text: "Mozilla WebPush"; key: "Autopush" }
118 ListElement { text: "NextPush"; key: "NextPush" }
119 ListElement { text: "Ntfy"; key: "Ntfy" }
120 }
121 textRole: "text"
122 valueRole: "key"
123 currentIndex: indexOfValue(kcm.pushProviderId)
124 Component.onCompleted: currentIndex = indexOfValue(kcm.pushProviderId)
125 }
126 }
127
128 Loader {
129 id: providerFormLoader
130 Layout.fillWidth: true
131 visible: kcm.hasKDEDistributor
132 sourceComponent: {
133 const forms = [gotifyForm, autopushForm, nextpushForm, ntfyForm];
134 return forms[pushProviderBox.currentIndex];
135 }
136 }
137 }
138
139 Component {
140 id: gotifyForm
141 Kirigami.FormLayout {
142 readonly property bool dirty: urlField.text != root.pushProviderConfig['Url'] || tokenField.text != root.pushProviderConfig['ClientToken']
143
144 function config() {
145 let c = root.pushProviderConfig;
146 c['Url'] = urlField.text;
147 c['ClientToken'] = tokenField.text;
148 return c;
149 }
150
151 twinFormLayouts: [topForm]
152 QQC2.TextField {
153 id: urlField
154 Kirigami.FormData.label: i18n("Url:")
155 text: root.pushProviderConfig['Url'] ?? ''
156 }
157 QQC2.TextField {
158 id: tokenField
159 Kirigami.FormData.label: i18n("Client token:")
160 text: root.pushProviderConfig['ClientToken'] ?? ''
161 }
162 }
163 }
164 Component {
165 id: nextpushForm
166 Kirigami.FormLayout {
167 id: nextpushConfig
168 readonly property bool dirty: urlField.text != root.pushProviderConfig['Url'] || userField.text != root.pushProviderConfig['Username'] || appPassword != root.pushProviderConfig['AppPassword']
169 property string appPassword: root.pushProviderConfig['AppPassword'] ?? '';
170 function config() {
171 let c = root.pushProviderConfig;
172 c['Url'] = urlField.text;
173 c['Username'] = userField.text;
174 c['AppPassword'] = appPassword;
175 return c;
176 }
177
178 twinFormLayouts: [topForm]
179 QQC2.TextField {
180 id: urlField
181 Kirigami.FormData.label: i18n("Url:")
182 text: root.pushProviderConfig['Url'] ?? ''
183 }
184 QQC2.Label {
185 id: userField
186 Kirigami.FormData.label: i18n("User name:")
187 text: root.pushProviderConfig['Username'] ?? ''
188 }
189 RowLayout {
190 QQC2.Button {
191 enabled: urlField.text != ""
192 text: i18n("Authenticate")
193 onClicked: {
194 authBusy.running = true;
195 kcm.nextcloudAuthenticate(urlField.text);
196 }
197
198 }
199 QQC2.BusyIndicator {
200 id: authBusy
201 running: false
202 }
203 }
204 Connections {
205 target: kcm
206
207 function onNextcloudAuthenticated(loginName, appPassword) {
208 userField.text = loginName;
209 nextpushConfig.appPassword = appPassword
210 authBusy.running = false;
211 }
212 }
213 }
214 }
215 Component {
216 id: ntfyForm
217 Kirigami.FormLayout {
218 id: ntfyConfig
219 readonly property bool dirty: urlField.text != root.pushProviderConfig['Url']
220
221 function config() {
222 let c = root.pushProviderConfig;
223 c['Url'] = urlField.text;
224 return c;
225 }
226
227 twinFormLayouts: [topForm]
228 QQC2.TextField {
229 id: urlField
230 Kirigami.FormData.label: i18n("Url:")
231 text: root.pushProviderConfig['Url'] ?? ''
232 }
233 }
234 }
235 Component {
236 id: autopushForm
237 Kirigami.FormLayout {
238 readonly property bool dirty: urlField.text != root.pushProviderConfig['Url']
239
240 function config() {
241 let c = root.pushProviderConfig;
242 c['Url'] = urlField.text;
243 return c;
244 }
245
246 twinFormLayouts: [topForm]
247 QQC2.TextField {
248 id: urlField
249 Kirigami.FormData.label: i18n("Url:")
250 text: root.pushProviderConfig['Url'] ?? 'https://push.services.mozilla.com'
251 }
252 }
253 }
254
255 Connections {
256 target: kcm
257
258 function onSaveRequested() {
259 kcm.setPushProviderConfiguration(pushProviderBox.currentValue, providerFormLoader.item.config());
260 }
261 }
262 Binding {
263 target: kcm
264 property: "needsSave"
265 value: providerFormLoader.item.dirty || pushProviderBox.currentValue != kcm.pushProviderId
266 }
267 }
268
269 RowLayout {
270 visible: kcm.hasDistributor
271 enabled: !kcm.needsSave
272 Kirigami.Icon {
273 source: {
274 switch (kcm.selfTest.state) {
275 case SelfTest.Success:
276 return "dialog-positive";
277 case SelfTest.Error:
278 return "dialog-error";
279 case SelfTest.WaitingForEndpoint:
280 return "network-connect";
281 case SelfTest.Submitting:
282 return "cloud-upload";
283 case SelfTest.WaitingForMessage:
284 return "cloud-download";
285 }
286 }
287 }
288 Kirigami.TitleSubtitle {
289 Layout.fillWidth: true
290 title: {
291 switch (kcm.selfTest.state) {
292 case SelfTest.Idle:
293 return "";
294 case SelfTest.Success:
295 return i18n("Push notifications are working correctly.")
296 case SelfTest.Error:
297 return i18n("Push notification test failed.")
298 case SelfTest.WaitingForEndpoint:
299 return i18n("Registering with push server…")
300 case SelfTest.Submitting:
301 return i18n("Sending push notification…")
302 case SelfTest.WaitingForMessage:
303 return i18n("Waiting to receive push notification…")
304 }
305 }
306 subtitle: kcm.selfTest.errorMessage
307 }
308 QQC2.Button {
309 id: testButton
310 text: i18nc("@action:button", "Test")
311 icon.name: "media-playback-start"
312 visible: kcm.selfTest.state === SelfTest.Idle || kcm.selfTest.state === SelfTest.Success || kcm.selfTest.state === SelfTest.Error
313 onClicked: kcm.selfTest.start()
314 }
315 QQC2.BusyIndicator {
316 visible: !testButton.visible
317 running: visible
318 }
319 }
320 }
321
322 // registered clients
323 view: ListView {
324 model: kcm.clientModel
325 header: Kirigami.InlineViewHeader {
326 width: ListView.view.width
327 text: i18n("Applications")
328 }
329 visible: count > 0
330
331 delegate: QQC2.ItemDelegate {
332 width: ListView.view.width
333
334 text: model.display
335
336 down: false
337 highlighted: false
338 hoverEnabled: false
339
340 Kirigami.Theme.useAlternateBackgroundColor: true
341
342 contentItem: RowLayout {
343 spacing: 0
344 KirigamiDelegates.IconTitleSubtitle {
345 title: model.name
346 subtitle: model.description
347 icon.source: model.iconName
348 }
349 Item {
350 Layout.fillWidth: true
351 }
352 QQC2.ToolButton {
353 icon.name: "edit-delete"
354
355 onClicked: removePrompt.open()
356
357 QQC2.ToolTip.text: i18n("Unregister application from push notifications")
358
360 id: removePrompt
361
362 parent: QQC2.Overlay.overlay
363
364 title: i18nc("@title:window", "Unregister Application")
365 subtitle: i18nc("%1 is an application name", "Are you sure you want to unregister '%1'?", model.name)
366 standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
367
368 onAccepted: kcm.forceUnregister(model.token)
369 }
370 }
371 }
372 }
373 }
374}
Push notification self-test state machine.
Definition selftest.h:22
Q_SCRIPTABLE QString start(QString train="")
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_MIME_EXPORT const char Header[]
Type type(const QSqlDatabase &db)
QString name(StandardAction id)
QString label(StandardShortcut id)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 25 2025 12:05:39 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.