@choochmeque/tauri-plugin-notifications-api 0.4.5 → 0.5.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  # Tauri Plugin Notifications
8
8
 
9
- A Tauri v2 plugin for sending notifications on desktop and mobile platforms. Send toast notifications (brief auto-expiring OS window elements) with support for rich content, scheduling, actions, channels, and push delivery via FCM and APNs.
9
+ A Tauri v2 plugin for sending notifications on desktop and mobile platforms. Send toast notifications (brief auto-expiring OS window elements) with support for rich content, scheduling, actions, channels, and push delivery via FCM, APNs, and UnifiedPush.
10
10
 
11
11
  ## Features
12
12
 
@@ -25,7 +25,7 @@ A Tauri v2 plugin for sending notifications on desktop and mobile platforms. Sen
25
25
 
26
26
  - **macOS**: Native notification center integration
27
27
  - **Windows**: Windows notification system
28
- - **Linux**: notify-rust with desktop notification support
28
+ - **Linux**: notify-rust with desktop notification support; push notifications via UnifiedPush
29
29
  - **iOS**: User Notifications framework
30
30
  - **Android**: Android notification system with channels
31
31
 
@@ -59,9 +59,12 @@ tauri-plugin-notifications = { version = "0.4", features = ["push-notifications"
59
59
 
60
60
  This enables:
61
61
  - Firebase Cloud Messaging support on Android
62
- - APNs (Apple Push Notification service) support on iOS
62
+ - APNs (Apple Push Notification service) support on iOS and macOS
63
+ - UnifiedPush support on Linux (D-Bus distributor protocol)
63
64
 
64
- **Note:** Push notifications are currently supported on mobile platforms (iOS and Android) only. macOS and Windows support is not yet available.
65
+ **Note:** Push notifications are currently supported on iOS, Android, macOS, and Linux. Windows support is not yet available.
66
+
67
+ On Linux you also need a UnifiedPush *distributor* app installed (ntfy, NextPush, Conversations, etc. — see the [distributor list](https://unifiedpush.org/users/distributors/)). The plugin itself is stateless: any distributor selection or client token you set lives only for the current process. See [Linux UnifiedPush Setup](#linux-unifiedpush-setup) below for details.
65
68
 
66
69
  Without this feature enabled:
67
70
  - Firebase dependencies are not included in Android builds
@@ -448,9 +451,35 @@ Requests the permission to send notifications.
448
451
  **Returns:** `Promise<'granted' | 'denied' | 'default'>`
449
452
 
450
453
  ### `registerForPushNotifications()`
451
- Registers the app for push notifications (mobile only). On Android, this retrieves the FCM device token. On iOS, this requests permissions and registers for remote notifications.
454
+ Registers the app for push notifications. On Android this retrieves the FCM device token; on iOS this requests permission and registers for remote notifications; on Linux this registers with the selected UnifiedPush distributor.
455
+
456
+ **Returns:** `Promise<string>` — a platform-specific identifier:
457
+ - iOS: APNs device token
458
+ - Android: FCM device token
459
+ - Linux: UnifiedPush endpoint URL (the URL your backend POSTs payloads to)
460
+
461
+ ### `listDistributors()` **(Linux / UnifiedPush only)**
462
+ Lists every running UnifiedPush distributor by its D-Bus bus name (e.g. `org.unifiedpush.Distributor.ntfy`). Returns an empty array when none is installed — that's the signal to ask the user to install one from <https://unifiedpush.org/users/distributors/>.
463
+
464
+ Throws on non-Linux platforms because the underlying Tauri command isn't registered there.
465
+
466
+ **Returns:** `Promise<string[]>`
467
+
468
+ ### `setDistributor(name: string)` **(Linux / UnifiedPush only)**
469
+ Pins the distributor used on the next `registerForPushNotifications()` call. **Must be called before `registerForPushNotifications()`** — calling it after a successful register has no effect on the existing endpoint; to switch distributors, unregister and register again.
452
470
 
453
- **Returns:** `Promise<string>` - The device push token
471
+ The selection is **not persisted** across launches — if the host app wants to remember the user's choice, store it and re-apply on startup. If never called, the first entry from `listDistributors()` is used.
472
+
473
+ Rejects if `name` isn't currently on the bus. Throws on non-Linux platforms.
474
+
475
+ ### `setToken(token: string)` **(Linux / UnifiedPush only)**
476
+ Sets the UnifiedPush client token used on subsequent `registerForPushNotifications()` calls. **Must be called before `registerForPushNotifications()`** — calling it after a successful register has no effect on the existing endpoint.
477
+
478
+ The endpoint URL the distributor returns is derived from `(app_identifier, client_token)`, so passing the same token across launches yields the same endpoint URL. The token is **not persisted** — the host app must store it and re-apply on startup if it wants endpoint stability.
479
+
480
+ If never called, a fresh UUID is generated on each register call (FCM/APNs-style token rotation — the app pushes the new endpoint URL to its backend each time).
481
+
482
+ Rejects if `token` is empty. Throws on non-Linux platforms.
454
483
 
455
484
  ### `sendNotification(options: Options | string)`
456
485
  Sends a notification to the user. Can be called with a simple string for the title or with a detailed options object.
@@ -558,6 +587,7 @@ Listens for notification action performed events.
558
587
  - Actions support varies by platform
559
588
  - Limited scheduling capabilities on some platforms
560
589
  - Channels not applicable (Android-specific)
590
+ - Linux additionally supports server-driven push via UnifiedPush (see [Linux UnifiedPush Setup](#linux-unifiedpush-setup))
561
591
 
562
592
  ### iOS
563
593
  - Requires permission request
@@ -615,6 +645,70 @@ Listens for notification action performed events.
615
645
  ```
616
646
  - The notification plugin already includes the Firebase Cloud Messaging dependency when the `push-notifications` feature is enabled
617
647
 
648
+ ### Linux UnifiedPush Setup
649
+
650
+ UnifiedPush is a federated push protocol where a user-installed *distributor* app delivers messages to your app over D-Bus. The plugin implements the *connector* side and exposes the standard `registerForPushNotifications()` flow.
651
+
652
+ 1. **Enable the `push-notifications` feature** in your `Cargo.toml` (this pulls in the `zbus` / `tokio` / `uuid` deps on Linux).
653
+ 2. **Install a distributor**. The user picks one (or you ship one with your app):
654
+ - [ntfy](https://ntfy.sh/) — minimal, self-hostable, has a Linux desktop client
655
+ - [NextPush](https://nextpush.unifiedpush.org/) — backed by a Nextcloud server
656
+ - [Conversations](https://conversations.im/) — XMPP-based
657
+ - Full list: <https://unifiedpush.org/users/distributors/>
658
+ 3. **Register from JS**:
659
+ ```typescript
660
+ import { registerForPushNotifications, listDistributors, setDistributor, setToken } from '@choochmeque/tauri-plugin-notifications-api';
661
+
662
+ const distributors = await listDistributors();
663
+ if (distributors.length === 0) {
664
+ // prompt the user to install a distributor
665
+ return;
666
+ }
667
+ await setDistributor(distributors[0]);
668
+ const storedToken = localStorage.getItem('up-client-token') ?? crypto.randomUUID();
669
+ localStorage.setItem('up-client-token', storedToken);
670
+ await setToken(storedToken);
671
+ const endpoint = await registerForPushNotifications();
672
+ // POST `endpoint` to your backend so it can deliver pushes
673
+ ```
674
+
675
+ #### Receiving pushes while the app is running
676
+
677
+ Incoming UnifiedPush messages are handled automatically:
678
+
679
+ - A system toast is shown via `notify-rust` (if that feature is enabled — it is by default).
680
+ - The same `onNotificationReceived` listener that handles local notifications fires with `source: "push"`:
681
+
682
+ ```typescript
683
+ import { onNotificationReceived } from '@choochmeque/tauri-plugin-notifications-api';
684
+
685
+ const unlisten = await onNotificationReceived((n) => {
686
+ if (n.source === 'push') {
687
+ console.log('UnifiedPush message:', n.title, n.body, n.extra);
688
+ }
689
+ });
690
+ ```
691
+
692
+ The plugin best-effort-parses the message bytes as JSON and extracts `title`, `body` (or `message`), and `data` (or `extra`). Non-JSON payloads land in `body` as a plain string. Binary payloads end up in `extra` as a `<binary N bytes>` marker.
693
+
694
+ #### Receiving pushes when the app is closed (optional)
695
+
696
+ UnifiedPush delivers messages by D-Bus method call on the app's bus name. If the app isn't running when a push arrives, the distributor relies on D-Bus session activation to launch it.
697
+
698
+ To enable that, ship a `.service` activation file at `~/.local/share/dbus-1/services/<app-identifier>.service`:
699
+
700
+ ```ini
701
+ [D-BUS Service]
702
+ Name=com.example.MyApp
703
+ Exec=/usr/bin/my-app
704
+ ```
705
+
706
+ Replace `Name` with your Tauri app's identifier (the `identifier` field from `tauri.conf.json`) and `Exec` with the absolute path to the installed binary. Without this file, pushes are only delivered while the app is already running.
707
+
708
+ The plugin does **not** install this file for you — it's a packaging/deployment decision. Distros and packagers typically install it from the `.deb` / `.rpm` / Flatpak manifest.
709
+
710
+ **Important caveat about cold-start delivery:** the plugin's UnifiedPush connector (the D-Bus service that owns your app identifier) is **lazily initialized** on the first call to `listDistributors()`, `setDistributor()`, `setToken()`, or `registerForPushNotifications()` from the JS layer. If your app is launched by D-Bus activation purely to handle a push, the distributor's method call may race the lazy init and arrive before the connector is registered — the call then fails silently. To make activation-driven delivery reliable, trigger the init eagerly in your Rust `setup()` (e.g. call `notifications.list_distributors()` once before returning), or call `listDistributors()` from JS as early as your app starts. Until you do, treat the `.service` activation as best-effort.
711
+
618
712
  ## Testing
619
713
 
620
714
  ### Desktop
package/dist-js/index.cjs CHANGED
@@ -131,7 +131,21 @@ async function requestPermission() {
131
131
  return await core.invoke("plugin:notifications|request_permission");
132
132
  }
133
133
  /**
134
- * Registers the app for push notifications (mobile).
134
+ * Registers the app for push notifications.
135
+ *
136
+ * Returns a platform-dependent string identifying this push registration:
137
+ * - **iOS**: APNs device token
138
+ * - **Android**: Firebase Cloud Messaging token
139
+ * - **Linux**: UnifiedPush endpoint URL (the URL your backend POSTs payloads to).
140
+ * The host app may persist this and treat it as the new endpoint on each
141
+ * launch (FCM/APNs style), or call {@link setToken} beforehand with a
142
+ * stored client token to receive the same endpoint URL across launches.
143
+ *
144
+ * On Linux this requires the `push-notifications` feature and at least one
145
+ * UnifiedPush distributor running on the system; see the README for setup.
146
+ * Any {@link setDistributor} or {@link setToken} calls must happen
147
+ * **before** this — they only affect the next register call, and once
148
+ * registered the endpoint URL is fixed until you unregister.
135
149
  *
136
150
  * @example
137
151
  * ```typescript
@@ -140,16 +154,17 @@ async function requestPermission() {
140
154
  * console.log('Push token:', token);
141
155
  * ```
142
156
  *
143
- * @returns A promise resolving to the device push token.
157
+ * @returns A promise resolving to the platform-specific push identifier.
144
158
  */
145
159
  async function registerForPushNotifications() {
146
160
  return await core.invoke("plugin:notifications|register_for_push_notifications");
147
161
  }
148
162
  /**
149
- * Unregisters the app from push notifications (mobile).
163
+ * Unregisters the app from push notifications.
150
164
  *
151
- * This removes the device's push notification token and stops receiving
152
- * remote push notifications.
165
+ * This removes the device's push notification registration and stops
166
+ * receiving remote pushes. On Linux it calls `Unregister` on the active
167
+ * UnifiedPush distributor.
153
168
  *
154
169
  * @example
155
170
  * ```typescript
@@ -161,7 +176,104 @@ async function registerForPushNotifications() {
161
176
  * @returns A promise resolving when unregistration is complete.
162
177
  */
163
178
  async function unregisterForPushNotifications() {
164
- return await core.invoke("plugin:notifications|unregister_for_push_notifications");
179
+ await core.invoke("plugin:notifications|unregister_for_push_notifications");
180
+ }
181
+ /**
182
+ * Lists currently running UnifiedPush distributors by D-Bus bus name
183
+ * (e.g. `org.unifiedpush.Distributor.ntfy`).
184
+ *
185
+ * **Linux only, and only when the plugin was built with the
186
+ * `push-notifications` Rust feature** (the Tauri commands are gated behind
187
+ * `#[cfg(all(target_os = "linux", feature = "push-notifications"))]`).
188
+ * Throws elsewhere because the command isn't registered.
189
+ *
190
+ * Returns an empty array when no UnifiedPush distributor is installed —
191
+ * this is the signal to prompt the user to install one
192
+ * (https://unifiedpush.org/users/distributors/).
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * import { listDistributors } from '@choochmeque/tauri-plugin-notifications-api';
197
+ * const distributors = await listDistributors();
198
+ * if (distributors.length === 0) {
199
+ * alert('Please install a UnifiedPush distributor');
200
+ * }
201
+ * ```
202
+ *
203
+ * @returns A promise resolving to the list of distributor bus names.
204
+ * @platform linux
205
+ */
206
+ async function listDistributors() {
207
+ return await core.invoke("plugin:notifications|list_distributors");
208
+ }
209
+ /**
210
+ * Pins the UnifiedPush distributor used for the next
211
+ * {@link registerForPushNotifications} call.
212
+ *
213
+ * **Linux only, and only when the plugin was built with the
214
+ * `push-notifications` Rust feature.** Throws elsewhere because the
215
+ * underlying Tauri command isn't registered.
216
+ *
217
+ * **Must be called before {@link registerForPushNotifications}.** Calling
218
+ * this after a successful register has no effect on the existing endpoint
219
+ * — to switch distributors, unregister and register again.
220
+ *
221
+ * The selection lives only for the current process — it is **not persisted**
222
+ * across launches. If the host app wants to remember the user's choice, it
223
+ * must store the name itself and re-apply it via this function on startup.
224
+ * If never called, the first distributor returned by {@link listDistributors}
225
+ * is used.
226
+ *
227
+ * @example
228
+ * ```typescript
229
+ * import { setDistributor } from '@choochmeque/tauri-plugin-notifications-api';
230
+ * await setDistributor('org.unifiedpush.Distributor.ntfy');
231
+ * ```
232
+ *
233
+ * @param name The distributor bus name. Must be one of the values returned
234
+ * by {@link listDistributors}; otherwise rejects.
235
+ * @platform linux
236
+ */
237
+ async function setDistributor(name) {
238
+ await core.invoke("plugin:notifications|set_distributor", { name });
239
+ }
240
+ /**
241
+ * Sets the UnifiedPush client token used on subsequent
242
+ * {@link registerForPushNotifications} calls.
243
+ *
244
+ * **Linux only, and only when the plugin was built with the
245
+ * `push-notifications` Rust feature.** Throws elsewhere because the
246
+ * underlying Tauri command isn't registered.
247
+ *
248
+ * **Must be called before {@link registerForPushNotifications}.** Calling
249
+ * this after a successful register has no effect on the existing endpoint
250
+ * — to get a different endpoint URL, unregister and register again.
251
+ *
252
+ * UnifiedPush distributors derive the endpoint URL from
253
+ * `(connector_bus_name, client_token)`, so apps that want endpoint
254
+ * stability across launches should persist a token (any non-empty string,
255
+ * usually a UUID) and call this on startup before registering. If never
256
+ * called, a fresh UUID is generated on each register call and the endpoint
257
+ * URL will be unique per launch — the same model as FCM/APNs token
258
+ * rotation, where the app pushes the new token to its backend each time.
259
+ *
260
+ * The token lives only for the current process — it is **not persisted**
261
+ * across launches.
262
+ *
263
+ * @example
264
+ * ```typescript
265
+ * import { setToken, registerForPushNotifications } from '@choochmeque/tauri-plugin-notifications-api';
266
+ * const storedToken = localStorage.getItem('up-client-token') ?? crypto.randomUUID();
267
+ * localStorage.setItem('up-client-token', storedToken);
268
+ * await setToken(storedToken);
269
+ * const endpoint = await registerForPushNotifications();
270
+ * ```
271
+ *
272
+ * @param token The client token. Must be non-empty.
273
+ * @platform linux
274
+ */
275
+ async function setToken(token) {
276
+ await core.invoke("plugin:notifications|set_token", { token });
165
277
  }
166
278
  /**
167
279
  * Sends a notification to the user.
@@ -290,7 +402,7 @@ async function removeActive(notifications) {
290
402
  * @returns A promise indicating the success or failure of the operation.
291
403
  */
292
404
  async function removeAllActive() {
293
- await core.invoke("plugin:notifications|remove_active", { notifications: [] });
405
+ await core.invoke("plugin:notifications|remove_all");
294
406
  }
295
407
  /**
296
408
  * Creates a notification channel.
@@ -423,6 +535,7 @@ exports.cancelAll = cancelAll;
423
535
  exports.channels = channels;
424
536
  exports.createChannel = createChannel;
425
537
  exports.isPermissionGranted = isPermissionGranted;
538
+ exports.listDistributors = listDistributors;
426
539
  exports.onAction = onAction;
427
540
  exports.onNotificationClicked = onNotificationClicked;
428
541
  exports.onNotificationReceived = onNotificationReceived;
@@ -434,4 +547,6 @@ exports.removeAllActive = removeAllActive;
434
547
  exports.removeChannel = removeChannel;
435
548
  exports.requestPermission = requestPermission;
436
549
  exports.sendNotification = sendNotification;
550
+ exports.setDistributor = setDistributor;
551
+ exports.setToken = setToken;
437
552
  exports.unregisterForPushNotifications = unregisterForPushNotifications;
@@ -381,7 +381,21 @@ declare function isPermissionGranted(): Promise<boolean>;
381
381
  */
382
382
  declare function requestPermission(): Promise<NotificationPermission>;
383
383
  /**
384
- * Registers the app for push notifications (mobile).
384
+ * Registers the app for push notifications.
385
+ *
386
+ * Returns a platform-dependent string identifying this push registration:
387
+ * - **iOS**: APNs device token
388
+ * - **Android**: Firebase Cloud Messaging token
389
+ * - **Linux**: UnifiedPush endpoint URL (the URL your backend POSTs payloads to).
390
+ * The host app may persist this and treat it as the new endpoint on each
391
+ * launch (FCM/APNs style), or call {@link setToken} beforehand with a
392
+ * stored client token to receive the same endpoint URL across launches.
393
+ *
394
+ * On Linux this requires the `push-notifications` feature and at least one
395
+ * UnifiedPush distributor running on the system; see the README for setup.
396
+ * Any {@link setDistributor} or {@link setToken} calls must happen
397
+ * **before** this — they only affect the next register call, and once
398
+ * registered the endpoint URL is fixed until you unregister.
385
399
  *
386
400
  * @example
387
401
  * ```typescript
@@ -390,14 +404,15 @@ declare function requestPermission(): Promise<NotificationPermission>;
390
404
  * console.log('Push token:', token);
391
405
  * ```
392
406
  *
393
- * @returns A promise resolving to the device push token.
407
+ * @returns A promise resolving to the platform-specific push identifier.
394
408
  */
395
409
  declare function registerForPushNotifications(): Promise<string>;
396
410
  /**
397
- * Unregisters the app from push notifications (mobile).
411
+ * Unregisters the app from push notifications.
398
412
  *
399
- * This removes the device's push notification token and stops receiving
400
- * remote push notifications.
413
+ * This removes the device's push notification registration and stops
414
+ * receiving remote pushes. On Linux it calls `Unregister` on the active
415
+ * UnifiedPush distributor.
401
416
  *
402
417
  * @example
403
418
  * ```typescript
@@ -408,7 +423,98 @@ declare function registerForPushNotifications(): Promise<string>;
408
423
  *
409
424
  * @returns A promise resolving when unregistration is complete.
410
425
  */
411
- declare function unregisterForPushNotifications(): Promise<string>;
426
+ declare function unregisterForPushNotifications(): Promise<void>;
427
+ /**
428
+ * Lists currently running UnifiedPush distributors by D-Bus bus name
429
+ * (e.g. `org.unifiedpush.Distributor.ntfy`).
430
+ *
431
+ * **Linux only, and only when the plugin was built with the
432
+ * `push-notifications` Rust feature** (the Tauri commands are gated behind
433
+ * `#[cfg(all(target_os = "linux", feature = "push-notifications"))]`).
434
+ * Throws elsewhere because the command isn't registered.
435
+ *
436
+ * Returns an empty array when no UnifiedPush distributor is installed —
437
+ * this is the signal to prompt the user to install one
438
+ * (https://unifiedpush.org/users/distributors/).
439
+ *
440
+ * @example
441
+ * ```typescript
442
+ * import { listDistributors } from '@choochmeque/tauri-plugin-notifications-api';
443
+ * const distributors = await listDistributors();
444
+ * if (distributors.length === 0) {
445
+ * alert('Please install a UnifiedPush distributor');
446
+ * }
447
+ * ```
448
+ *
449
+ * @returns A promise resolving to the list of distributor bus names.
450
+ * @platform linux
451
+ */
452
+ declare function listDistributors(): Promise<string[]>;
453
+ /**
454
+ * Pins the UnifiedPush distributor used for the next
455
+ * {@link registerForPushNotifications} call.
456
+ *
457
+ * **Linux only, and only when the plugin was built with the
458
+ * `push-notifications` Rust feature.** Throws elsewhere because the
459
+ * underlying Tauri command isn't registered.
460
+ *
461
+ * **Must be called before {@link registerForPushNotifications}.** Calling
462
+ * this after a successful register has no effect on the existing endpoint
463
+ * — to switch distributors, unregister and register again.
464
+ *
465
+ * The selection lives only for the current process — it is **not persisted**
466
+ * across launches. If the host app wants to remember the user's choice, it
467
+ * must store the name itself and re-apply it via this function on startup.
468
+ * If never called, the first distributor returned by {@link listDistributors}
469
+ * is used.
470
+ *
471
+ * @example
472
+ * ```typescript
473
+ * import { setDistributor } from '@choochmeque/tauri-plugin-notifications-api';
474
+ * await setDistributor('org.unifiedpush.Distributor.ntfy');
475
+ * ```
476
+ *
477
+ * @param name The distributor bus name. Must be one of the values returned
478
+ * by {@link listDistributors}; otherwise rejects.
479
+ * @platform linux
480
+ */
481
+ declare function setDistributor(name: string): Promise<void>;
482
+ /**
483
+ * Sets the UnifiedPush client token used on subsequent
484
+ * {@link registerForPushNotifications} calls.
485
+ *
486
+ * **Linux only, and only when the plugin was built with the
487
+ * `push-notifications` Rust feature.** Throws elsewhere because the
488
+ * underlying Tauri command isn't registered.
489
+ *
490
+ * **Must be called before {@link registerForPushNotifications}.** Calling
491
+ * this after a successful register has no effect on the existing endpoint
492
+ * — to get a different endpoint URL, unregister and register again.
493
+ *
494
+ * UnifiedPush distributors derive the endpoint URL from
495
+ * `(connector_bus_name, client_token)`, so apps that want endpoint
496
+ * stability across launches should persist a token (any non-empty string,
497
+ * usually a UUID) and call this on startup before registering. If never
498
+ * called, a fresh UUID is generated on each register call and the endpoint
499
+ * URL will be unique per launch — the same model as FCM/APNs token
500
+ * rotation, where the app pushes the new token to its backend each time.
501
+ *
502
+ * The token lives only for the current process — it is **not persisted**
503
+ * across launches.
504
+ *
505
+ * @example
506
+ * ```typescript
507
+ * import { setToken, registerForPushNotifications } from '@choochmeque/tauri-plugin-notifications-api';
508
+ * const storedToken = localStorage.getItem('up-client-token') ?? crypto.randomUUID();
509
+ * localStorage.setItem('up-client-token', storedToken);
510
+ * await setToken(storedToken);
511
+ * const endpoint = await registerForPushNotifications();
512
+ * ```
513
+ *
514
+ * @param token The client token. Must be non-empty.
515
+ * @platform linux
516
+ */
517
+ declare function setToken(token: string): Promise<void>;
412
518
  /**
413
519
  * Sends a notification to the user.
414
520
  * @example
@@ -627,4 +733,4 @@ interface NotificationClickedData {
627
733
  */
628
734
  declare function onNotificationClicked(cb: (data: NotificationClickedData) => void): Promise<PluginListener>;
629
735
  export type { Attachment, Options, Action, ActionType, PendingNotification, ActiveNotification, Channel, ScheduleInterval, NotificationClickedData, };
630
- export { Importance, Visibility, sendNotification, requestPermission, isPermissionGranted, registerForPushNotifications, unregisterForPushNotifications, registerActionTypes, pending, cancel, cancelAll, active, removeActive, removeAllActive, createChannel, removeChannel, channels, onNotificationReceived, onAction, onNotificationClicked, Schedule, ScheduleEvery, };
736
+ export { Importance, Visibility, sendNotification, requestPermission, isPermissionGranted, registerForPushNotifications, unregisterForPushNotifications, listDistributors, setDistributor, setToken, registerActionTypes, pending, cancel, cancelAll, active, removeActive, removeAllActive, createChannel, removeChannel, channels, onNotificationReceived, onAction, onNotificationClicked, Schedule, ScheduleEvery, };
package/dist-js/index.js CHANGED
@@ -129,7 +129,21 @@ async function requestPermission() {
129
129
  return await invoke("plugin:notifications|request_permission");
130
130
  }
131
131
  /**
132
- * Registers the app for push notifications (mobile).
132
+ * Registers the app for push notifications.
133
+ *
134
+ * Returns a platform-dependent string identifying this push registration:
135
+ * - **iOS**: APNs device token
136
+ * - **Android**: Firebase Cloud Messaging token
137
+ * - **Linux**: UnifiedPush endpoint URL (the URL your backend POSTs payloads to).
138
+ * The host app may persist this and treat it as the new endpoint on each
139
+ * launch (FCM/APNs style), or call {@link setToken} beforehand with a
140
+ * stored client token to receive the same endpoint URL across launches.
141
+ *
142
+ * On Linux this requires the `push-notifications` feature and at least one
143
+ * UnifiedPush distributor running on the system; see the README for setup.
144
+ * Any {@link setDistributor} or {@link setToken} calls must happen
145
+ * **before** this — they only affect the next register call, and once
146
+ * registered the endpoint URL is fixed until you unregister.
133
147
  *
134
148
  * @example
135
149
  * ```typescript
@@ -138,16 +152,17 @@ async function requestPermission() {
138
152
  * console.log('Push token:', token);
139
153
  * ```
140
154
  *
141
- * @returns A promise resolving to the device push token.
155
+ * @returns A promise resolving to the platform-specific push identifier.
142
156
  */
143
157
  async function registerForPushNotifications() {
144
158
  return await invoke("plugin:notifications|register_for_push_notifications");
145
159
  }
146
160
  /**
147
- * Unregisters the app from push notifications (mobile).
161
+ * Unregisters the app from push notifications.
148
162
  *
149
- * This removes the device's push notification token and stops receiving
150
- * remote push notifications.
163
+ * This removes the device's push notification registration and stops
164
+ * receiving remote pushes. On Linux it calls `Unregister` on the active
165
+ * UnifiedPush distributor.
151
166
  *
152
167
  * @example
153
168
  * ```typescript
@@ -159,7 +174,104 @@ async function registerForPushNotifications() {
159
174
  * @returns A promise resolving when unregistration is complete.
160
175
  */
161
176
  async function unregisterForPushNotifications() {
162
- return await invoke("plugin:notifications|unregister_for_push_notifications");
177
+ await invoke("plugin:notifications|unregister_for_push_notifications");
178
+ }
179
+ /**
180
+ * Lists currently running UnifiedPush distributors by D-Bus bus name
181
+ * (e.g. `org.unifiedpush.Distributor.ntfy`).
182
+ *
183
+ * **Linux only, and only when the plugin was built with the
184
+ * `push-notifications` Rust feature** (the Tauri commands are gated behind
185
+ * `#[cfg(all(target_os = "linux", feature = "push-notifications"))]`).
186
+ * Throws elsewhere because the command isn't registered.
187
+ *
188
+ * Returns an empty array when no UnifiedPush distributor is installed —
189
+ * this is the signal to prompt the user to install one
190
+ * (https://unifiedpush.org/users/distributors/).
191
+ *
192
+ * @example
193
+ * ```typescript
194
+ * import { listDistributors } from '@choochmeque/tauri-plugin-notifications-api';
195
+ * const distributors = await listDistributors();
196
+ * if (distributors.length === 0) {
197
+ * alert('Please install a UnifiedPush distributor');
198
+ * }
199
+ * ```
200
+ *
201
+ * @returns A promise resolving to the list of distributor bus names.
202
+ * @platform linux
203
+ */
204
+ async function listDistributors() {
205
+ return await invoke("plugin:notifications|list_distributors");
206
+ }
207
+ /**
208
+ * Pins the UnifiedPush distributor used for the next
209
+ * {@link registerForPushNotifications} call.
210
+ *
211
+ * **Linux only, and only when the plugin was built with the
212
+ * `push-notifications` Rust feature.** Throws elsewhere because the
213
+ * underlying Tauri command isn't registered.
214
+ *
215
+ * **Must be called before {@link registerForPushNotifications}.** Calling
216
+ * this after a successful register has no effect on the existing endpoint
217
+ * — to switch distributors, unregister and register again.
218
+ *
219
+ * The selection lives only for the current process — it is **not persisted**
220
+ * across launches. If the host app wants to remember the user's choice, it
221
+ * must store the name itself and re-apply it via this function on startup.
222
+ * If never called, the first distributor returned by {@link listDistributors}
223
+ * is used.
224
+ *
225
+ * @example
226
+ * ```typescript
227
+ * import { setDistributor } from '@choochmeque/tauri-plugin-notifications-api';
228
+ * await setDistributor('org.unifiedpush.Distributor.ntfy');
229
+ * ```
230
+ *
231
+ * @param name The distributor bus name. Must be one of the values returned
232
+ * by {@link listDistributors}; otherwise rejects.
233
+ * @platform linux
234
+ */
235
+ async function setDistributor(name) {
236
+ await invoke("plugin:notifications|set_distributor", { name });
237
+ }
238
+ /**
239
+ * Sets the UnifiedPush client token used on subsequent
240
+ * {@link registerForPushNotifications} calls.
241
+ *
242
+ * **Linux only, and only when the plugin was built with the
243
+ * `push-notifications` Rust feature.** Throws elsewhere because the
244
+ * underlying Tauri command isn't registered.
245
+ *
246
+ * **Must be called before {@link registerForPushNotifications}.** Calling
247
+ * this after a successful register has no effect on the existing endpoint
248
+ * — to get a different endpoint URL, unregister and register again.
249
+ *
250
+ * UnifiedPush distributors derive the endpoint URL from
251
+ * `(connector_bus_name, client_token)`, so apps that want endpoint
252
+ * stability across launches should persist a token (any non-empty string,
253
+ * usually a UUID) and call this on startup before registering. If never
254
+ * called, a fresh UUID is generated on each register call and the endpoint
255
+ * URL will be unique per launch — the same model as FCM/APNs token
256
+ * rotation, where the app pushes the new token to its backend each time.
257
+ *
258
+ * The token lives only for the current process — it is **not persisted**
259
+ * across launches.
260
+ *
261
+ * @example
262
+ * ```typescript
263
+ * import { setToken, registerForPushNotifications } from '@choochmeque/tauri-plugin-notifications-api';
264
+ * const storedToken = localStorage.getItem('up-client-token') ?? crypto.randomUUID();
265
+ * localStorage.setItem('up-client-token', storedToken);
266
+ * await setToken(storedToken);
267
+ * const endpoint = await registerForPushNotifications();
268
+ * ```
269
+ *
270
+ * @param token The client token. Must be non-empty.
271
+ * @platform linux
272
+ */
273
+ async function setToken(token) {
274
+ await invoke("plugin:notifications|set_token", { token });
163
275
  }
164
276
  /**
165
277
  * Sends a notification to the user.
@@ -288,7 +400,7 @@ async function removeActive(notifications) {
288
400
  * @returns A promise indicating the success or failure of the operation.
289
401
  */
290
402
  async function removeAllActive() {
291
- await invoke("plugin:notifications|remove_active", { notifications: [] });
403
+ await invoke("plugin:notifications|remove_all");
292
404
  }
293
405
  /**
294
406
  * Creates a notification channel.
@@ -414,4 +526,4 @@ async function onNotificationClicked(cb) {
414
526
  };
415
527
  }
416
528
 
417
- export { Importance, Schedule, ScheduleEvery, Visibility, active, cancel, cancelAll, channels, createChannel, isPermissionGranted, onAction, onNotificationClicked, onNotificationReceived, pending, registerActionTypes, registerForPushNotifications, removeActive, removeAllActive, removeChannel, requestPermission, sendNotification, unregisterForPushNotifications };
529
+ export { Importance, Schedule, ScheduleEvery, Visibility, active, cancel, cancelAll, channels, createChannel, isPermissionGranted, listDistributors, onAction, onNotificationClicked, onNotificationReceived, pending, registerActionTypes, registerForPushNotifications, removeActive, removeAllActive, removeChannel, requestPermission, sendNotification, setDistributor, setToken, unregisterForPushNotifications };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@choochmeque/tauri-plugin-notifications-api",
3
- "version": "0.4.5",
3
+ "version": "0.5.0-rc.3",
4
4
  "license": "MIT",
5
5
  "author": "You",
6
6
  "description": "A Tauri v2 plugin for sending notifications on desktop and mobile platforms with support for system notifications and push delivery via FCM and APNs.",