From 11f1ad34697d27387310b7090910ce08f3e9e580 Mon Sep 17 00:00:00 2001 From: "guillem.cordoba" Date: Tue, 22 Oct 2024 13:58:23 +0200 Subject: [PATCH] UI working again --- flake.nix | 2 + tests/src/multi-device.test.ts | 20 +- tests/src/notification-lifecycle.test.ts | 40 +- ui/demo/index.html | 86 ++-- .../elements/my-notifications-icon-button.ts | 17 +- ui/src/elements/my-notifications-list.ts | 418 ++++++++---------- ui/src/notifications-client.ts | 22 +- ui/src/notifications-store.ts | 12 +- ui/src/types.ts | 1 + workdir/dna.nix | 2 + workdir/dna.yaml | 8 + .../notifications/src/notification.rs | 19 +- 12 files changed, 336 insertions(+), 311 deletions(-) diff --git a/flake.nix b/flake.nix index 8e550f42..0ed2f294 100644 --- a/flake.nix +++ b/flake.nix @@ -27,6 +27,8 @@ imports = [ ./zomes/integrity/notifications/zome.nix ./zomes/coordinator/notifications/zome.nix + ./zomes/integrity/example/zome.nix + ./zomes/coordinator/example/zome.nix # Just for testing purposes ./workdir/dna.nix ./workdir/happ.nix diff --git a/tests/src/multi-device.test.ts b/tests/src/multi-device.test.ts index b71d3010..564a5a28 100644 --- a/tests/src/multi-device.test.ts +++ b/tests/src/multi-device.test.ts @@ -42,9 +42,13 @@ test('notifications and their status get synchronized across devices for the sam // Alice creates a Notification await alice.store.client.sendNotification( - await sampleNotification(alice.store.client, { - recipient_profile_hash: bobProfile.actionHash, - }), + bobProfile.actionHash, + 'example', + 'type1', + 'group1', + { + hello: 'world!', + }, ); // Wait for the created entry to be propagated to the other node. @@ -115,9 +119,13 @@ test('notifications and their status get synchronized across devices for the sam // Alice creates a Notification await alice.store.client.sendNotification( - await sampleNotification(alice.store.client, { - recipient_profile_hash: bobProfile.actionHash, - }), + bobProfile.actionHash, + 'example', + 'type1', + 'group1', + { + hello: 'world!', + }, ); // Wait for the created entry to be propagated to the other node. diff --git a/tests/src/notification-lifecycle.test.ts b/tests/src/notification-lifecycle.test.ts index 4e30c0de..e0a1aa06 100644 --- a/tests/src/notification-lifecycle.test.ts +++ b/tests/src/notification-lifecycle.test.ts @@ -35,14 +35,22 @@ test('create notifications, read it, and dismiss it', async () => { // Alice creates a Notification await alice.store.client.sendNotification( - await sampleNotification(alice.store.client, { - recipient_profile_hash: bobProfile.actionHash, - }), + bobProfile.actionHash, + 'example', + 'type1', + 'group1', + { + hello: 'world!', + }, ); await alice.store.client.sendNotification( - await sampleNotification(alice.store.client, { - recipient_profile_hash: bobProfile.actionHash, - }), + bobProfile.actionHash, + 'example', + 'type1', + 'group1', + { + hello: 'world!', + }, ); // Wait for the created entry to be propagated to the other node. @@ -148,14 +156,22 @@ test('create notifications and dismiss it directly', async () => { // Alice creates a Notification await alice.store.client.sendNotification( - await sampleNotification(alice.store.client, { - recipient_profile_hash: bobProfile.actionHash, - }), + bobProfile.actionHash, + 'example', + 'type1', + 'group1', + { + hello: 'world!', + }, ); await alice.store.client.sendNotification( - await sampleNotification(alice.store.client, { - recipient_profile_hash: bobProfile.actionHash, - }), + bobProfile.actionHash, + 'example', + 'type1', + 'group1', + { + hello: 'world!', + }, ); // Wait for the created entry to be propagated to the other node. diff --git a/ui/demo/index.html b/ui/demo/index.html index bc08ed64..a40bf6e4 100644 --- a/ui/demo/index.html +++ b/ui/demo/index.html @@ -48,7 +48,7 @@ SignalWatcher, joinAsync, } from '@holochain-open-dev/signals'; - import { AppWebsocket } from '@holochain/client'; + import { AppWebsocket, decodeHashFromBase64 } from '@holochain/client'; import { ContextProvider } from '@lit/context'; import { mdiBell } from '@mdi/js'; import { decode, encode } from '@msgpack/msgpack'; @@ -85,36 +85,33 @@ new ContextProvider(this, profilesStoreContext, this.profilesStore); this._notificationsStore = new NotificationsStore( - new NotificationsClient( - appClient, - 'notifications_test', - ), - // { - // types: { - // type1: { - // name: 'Hii', - // description: 'something', - // title(group) { - // return new Signal.State({ - // status: 'completed', - // value: group, - // }); - // }, - // onClick: group => console.log('clicked', group), - // contents: n => { - // const i = decode(n.entry.content); - - // return new Signal.State({ - // status: 'completed', - // value: { - // iconSrc: wrapPathInSvg(mdiBell), - // body: i.body, - // }, - // }); - // }, - // }, - // }, - // }, + new NotificationsClient(appClient, 'notifications_test'), + // { + // types: { + // type1: { + // name: 'Hii', + // description: 'something', + // title(group) { + // return new Signal.State({ + // status: 'completed', + // value: group, + // }); + // }, + // onClick: group => console.log('clicked', group), + // contents: n => { + // const i = decode(n.entry.content); + + // return new Signal.State({ + // status: 'completed', + // value: { + // iconSrc: wrapPathInSvg(mdiBell), + // body: i.body, + // }, + // }); + // }, + // }, + // }, + // }, ); const interval = setInterval(async () => { const allProfilesLinks = @@ -147,8 +144,16 @@ this._notificationsStore.readNotifications.get(), ]); const undismissedNotifications = [ - ...Array.from(result.value ? result.value[0].keys() : []), - ...Array.from(result.value ? result.value[1].keys() : []), + ...Array.from( + result.value + ? Object.keys(result.value[0]).map(decodeHashFromBase64) + : [], + ), + ...Array.from( + result.value + ? Object.keys(result.value[1]).map(decodeHashFromBase64) + : [], + ), ]; return html` @@ -157,14 +162,13 @@
{ - this._notificationsStore.client.sendNotification({ - zome_name: "example" - notification_type: 'type1', - notification_group: - this.shadowRoot.getElementById('group').value, - recipient_profile_hash: this.peer, - content: encode({ body: "what's up?" }), - }); + this._notificationsStore.client.sendNotification( + this.peer, + 'example', + 'type1', + this.shadowRoot.getElementById('group').value, + { body: "what's up?" }, + ); }} >Create notification diff --git a/ui/src/elements/my-notifications-icon-button.ts b/ui/src/elements/my-notifications-icon-button.ts index 445e63fc..895312ca 100644 --- a/ui/src/elements/my-notifications-icon-button.ts +++ b/ui/src/elements/my-notifications-icon-button.ts @@ -6,6 +6,7 @@ import { } from '@holochain-open-dev/profiles'; import '@holochain-open-dev/profiles/dist/elements/agent-avatar.js'; import { SignalWatcher, joinAsync } from '@holochain-open-dev/signals'; +import { decodeHashFromBase64 } from '@holochain/client'; import { consume } from '@lit/context'; import { localized, msg } from '@lit/localize'; import { mdiBell } from '@mdi/js'; @@ -70,7 +71,9 @@ export class MyNotificationsIconButton extends SignalWatcher(LitElement) { hoist @sl-hide=${() => this.notificationsStore.client.markNotificationsAsRead( - Array.from(unreadNotifications.keys()), + Object.keys(unreadNotifications).map(hash => + decodeHashFromBase64(hash), + ), )} >
@@ -81,17 +84,19 @@ export class MyNotificationsIconButton extends SignalWatcher(LitElement) { .src=${wrapPathInSvg(mdiBell)} > - ${unreadNotifications.size + readNotifications.size > 0 + ${Object.keys(unreadNotifications).length + + Object.keys(readNotifications).length > + 0 ? html` 0 + .variant=${Object.keys(unreadNotifications).length > 0 ? 'primary' : 'neutral'} - .pulse=${unreadNotifications.size > 0} - >${unreadNotifications.size + - readNotifications.size} 0} + >${Object.keys(unreadNotifications).length + + Object.keys(readNotifications).length} ` : html``} diff --git a/ui/src/elements/my-notifications-list.ts b/ui/src/elements/my-notifications-list.ts index 89d67fa4..9fed2f4a 100644 --- a/ui/src/elements/my-notifications-list.ts +++ b/ui/src/elements/my-notifications-list.ts @@ -10,7 +10,14 @@ import { joinAsyncMap, } from '@holochain-open-dev/signals'; import { EntryRecord, mapValues } from '@holochain-open-dev/utils'; -import { ActionHash, Delete, SignedActionHashed } from '@holochain/client'; +import { + ActionHash, + Delete, + EntryHash, + EntryHashB64, + SignedActionHashed, + decodeHashFromBase64, +} from '@holochain/client'; import { consume } from '@lit/context'; import { msg } from '@lit/localize'; import { @@ -30,20 +37,13 @@ import { styleMap } from 'lit/directives/style-map.js'; import { notificationsStoreContext } from '../context.js'; import { NotificationsStore } from '../notifications-store.js'; -import { Notification, NotificationContents } from '../types.js'; +import { NotificationContents } from '../types.js'; interface NotificationGroup { group: string; timestamp: number; title: string; - notifications: Array; -} - -interface NotificationInfo { - record: EntryRecord; - deletes: Array>; - title: string; - contents: NotificationContents; + notifications: Array<[EntryHashB64, NotificationContents]>; } /** @@ -64,11 +64,11 @@ export class MyNotifications extends SignalWatcher(LitElement) { renderNotificationGroup( read: boolean, - persistent: boolean, notificationGroup: NotificationGroup, last: boolean, ) { - const singleNotification = notificationGroup.notifications.length === 1; + const singleNotification = + Object.keys(notificationGroup.notifications).length === 1; return html`
${singleNotification ? html`` : html``}
html`
${!singleNotification - ? html`` + ? html`` : html``} - ${n.contents.body} + ${n[1].body}
`, )} @@ -121,26 +120,22 @@ export class MyNotifications extends SignalWatcher(LitElement) { style="align-items: end; flex: 1; align-self: stretch" >
- ${persistent - ? html`` - : html` - { - this.notificationsStore.client.dismissNotifications( - notificationGroup.notifications.map( - n => n.record.actionHash, - ), - ); - e.stopPropagation(); - }} - > - `} + { + this.notificationsStore.client.dismissNotifications( + notificationGroup.notifications.map(([hash, _]) => + decodeHashFromBase64(hash), + ), + ); + e.stopPropagation(); + }} + >
@@ -150,43 +145,6 @@ export class MyNotifications extends SignalWatcher(LitElement) { : html``} `; } - notificationInfo( - notificationHash: ActionHash, - ): AsyncResult { - const record = this.notificationsStore.notifications - .get(notificationHash) - .entry.get(); - const deletes = this.notificationsStore.notifications - .get(notificationHash) - .deletes.get(); - - if (record.status !== 'completed') return record; - if (deletes.status !== 'completed') return deletes; - - const contents = this.notificationsStore.notificationsConfig.types[ - record.value.entry.notification_type - ] - .contents(record.value) - .get(); - const title = this.notificationsStore.notificationsConfig.types[ - record.value.entry.notification_type - ] - .title(record.value.entry.notification_group) - .get(); - if (contents.status !== 'completed') return contents; - if (title.status !== 'completed') return title; - - return { - status: 'completed', - value: { - record: record.value, - deletes: deletes.value, - title: title.value, - contents: contents.value, - }, - }; - } - getNotificationsGroups() { const unreadNotifications = this.notificationsStore.unreadNotifications.get(); @@ -194,115 +152,137 @@ export class MyNotifications extends SignalWatcher(LitElement) { if (unreadNotifications.status !== 'completed') return unreadNotifications; if (readNotifications.status !== 'completed') return readNotifications; - const unreadMapResult = joinAsyncMap( - mapValues(unreadNotifications.value, (_n, key) => - this.notificationInfo(key), - ), - ); - const readMapResult = joinAsyncMap( - mapValues(readNotifications.value, (_n, key) => - this.notificationInfo(key), - ), - ); - if (unreadMapResult.status !== 'completed') return unreadMapResult; - if (readMapResult.status !== 'completed') return readMapResult; + const notificationsContents: Record = + {}; + + for (const [notificationHash, notification] of Object.entries( + unreadNotifications.value, + )) { + const contents = this.notificationsStore + .notificationContents( + decodeHashFromBase64(notificationHash), + notification, + ) + .get(); + + if (contents.status !== 'completed') return contents; + + notificationsContents[notificationHash] = contents.value; + } + + for (const [notificationHash, notification] of Object.entries( + readNotifications.value, + )) { + const contents = this.notificationsStore + .notificationContents( + decodeHashFromBase64(notificationHash), + notification, + ) + .get(); + + if (contents.status !== 'completed') return contents; + + notificationsContents[notificationHash] = contents.value; + } const notifications: Record< string, Record< string, - Array<{ - read: boolean; - notificationInfo: NotificationInfo; - }> + Record< + EntryHashB64, + { + read: boolean; + timestamp: number; + contents: NotificationContents; + } + > > > = {}; - for (const [hash, info] of Array.from(unreadMapResult.value.entries())) { - if (!notifications[info.record.entry.notification_type]) { - notifications[info.record.entry.notification_type] = {}; + for (const [hash, notification] of Object.entries( + unreadNotifications.value, + )) { + const contents = notificationsContents[hash]; + if (!notifications[notification.notification_type]) { + notifications[notification.notification_type] = {}; } if ( - !notifications[info.record.entry.notification_type][ - info.record.entry.notification_group + !notifications[notification.notification_type][ + notification.notification_group ] ) { - notifications[info.record.entry.notification_type][ - info.record.entry.notification_group - ] = []; + notifications[notification.notification_type][ + notification.notification_group + ] = {}; } - notifications[info.record.entry.notification_type][ - info.record.entry.notification_group - ].push({ + notifications[notification.notification_type][ + notification.notification_group + ][hash] = { read: false, - notificationInfo: info, - }); + timestamp: notification.timestamp, + contents, + }; } - for (const [hash, info] of Array.from(readMapResult.value.entries())) { - if (!notifications[info.record.entry.notification_type]) { - notifications[info.record.entry.notification_type] = {}; + for (const [hash, notification] of Object.entries( + readNotifications.value, + )) { + const contents = notificationsContents[hash]; + if (!notifications[notification.notification_type]) { + notifications[notification.notification_type] = {}; } if ( - !notifications[info.record.entry.notification_type][ - info.record.entry.notification_group + !notifications[notification.notification_type][ + notification.notification_group ] ) { - notifications[info.record.entry.notification_type][ - info.record.entry.notification_group - ] = []; + notifications[notification.notification_type][ + notification.notification_group + ] = {}; } - notifications[info.record.entry.notification_type][ - info.record.entry.notification_group - ].push({ + notifications[notification.notification_type][ + notification.notification_group + ][hash] = { read: true, - notificationInfo: info, - }); + timestamp: notification.timestamp, + contents, + }; } - const unreadPersistent: Array = []; - const readPersistent: Array = []; - const unreadNonPersistent: Array = []; - const readNonPersistent: Array = []; + const unreadGroups: Array = []; + const readGroups: Array = []; for (const [notificationType, groups] of Object.entries(notifications)) { for (const [group, notifications] of Object.entries(groups)) { - const persistent = notifications.some( - n => - n.notificationInfo.record.entry.persistent && - n.notificationInfo.deletes.length === 0, - ); - const unread = notifications.some(n => !n.read); - const timestamps = notifications.map( - n => n.notificationInfo.record.action.timestamp, - ); + const unread = Object.values(notifications).some(n => !n.read); + const timestamps = Object.values(notifications).map(n => n.timestamp); timestamps.sort((t1, t2) => t2 - t1); const notificationsGroup: NotificationGroup = { group, - notifications: notifications.map(n => n.notificationInfo), - title: notifications[0].notificationInfo.title, + notifications: Object.entries(notifications) + .sort((t1, t2) => t2[1].timestamp - t1[1].timestamp) + .map( + ([hash, n]) => + [hash, n.contents] as [string, NotificationContents], + ), + title: Object.values(notifications)[0].contents.title, timestamp: timestamps[0], }; - if (persistent && unread) unreadPersistent.push(notificationsGroup); - if (persistent && !unread) readPersistent.push(notificationsGroup); - if (!persistent && unread) unreadNonPersistent.push(notificationsGroup); - if (!persistent && !unread) readNonPersistent.push(notificationsGroup); + if (unread) unreadGroups.push(notificationsGroup); + if (!unread) readGroups.push(notificationsGroup); } } - unreadPersistent.sort((a, b) => b.timestamp - a.timestamp); - unreadNonPersistent.sort((a, b) => b.timestamp - a.timestamp); - readPersistent.sort((a, b) => b.timestamp - a.timestamp); - readNonPersistent.sort((a, b) => b.timestamp - a.timestamp); + unreadGroups.sort((a, b) => b.timestamp - a.timestamp); + readGroups.sort((a, b) => b.timestamp - a.timestamp); return { status: 'completed' as const, value: { - unreadPersistent, - readPersistent, - unreadNonPersistent, - readNonPersistent, + unreadGroups, + readGroups, }, }; } @@ -314,7 +294,9 @@ export class MyNotifications extends SignalWatcher(LitElement) { ]); if (result.status !== 'completed') return 3; - return result.value[0].size + result.value[1].size; + return ( + Object.keys(result.value[0]).length + Object.keys(result.value[1]).length + ); } render() { @@ -338,6 +320,10 @@ export class MyNotifications extends SignalWatcher(LitElement) { effect="pulse" style="height: 16px; min-width: 250px;" > +
${i < count - 1 ? html`` : html``} `, @@ -349,110 +335,64 @@ export class MyNotifications extends SignalWatcher(LitElement) { .error=${result.error} >`; case 'completed': - const { - unreadPersistent, - readPersistent, - unreadNonPersistent, - readNonPersistent, - } = result.value; + const { unreadGroups, readGroups } = result.value; - const nonPersistentNotificationsCount = - unreadNonPersistent.length + readNonPersistent.length; - const persistentNotificationsCount = - unreadPersistent.length + readPersistent.length; + const notificationsCount = unreadGroups.length + readGroups.length; - return nonPersistentNotificationsCount > 0 || - persistentNotificationsCount > 0 + return notificationsCount > 0 ? html`
- ${unreadPersistent.map((n, i) => - this.renderNotificationGroup( - false, - true, - n, - i === unreadPersistent.length - 1, - ), - )} - ${unreadPersistent.length > 0 && readPersistent.length > 0 - ? html`` - : html``} - ${readPersistent.map((n, i) => - this.renderNotificationGroup( - true, - true, - n, - i === readPersistent.length - 1, - ), - )} -
-
- ${nonPersistentNotificationsCount > 0 - ? html` - ${persistentNotificationsCount > 0 - ? html` - - ` - : html``} -
- - this.notificationsStore.client.dismissNotifications( - [ - ...Array.from([] as ActionHash[]).concat( - ...unreadNonPersistent.map(group => - group.notifications.map( - n => n.record.actionHash, - ), - ), - ), - ...Array.from([] as ActionHash[]).concat( - ...readNonPersistent.map(group => - group.notifications.map( - n => n.record.actionHash, - ), - ), - ), - ], - )} - size="small" - > - - ${msg('Dismiss')} -
- - -
- ${unreadNonPersistent.map((n, i) => - this.renderNotificationGroup( - false, - false, - n, - i === unreadNonPersistent.length - 1, +
+ + this.notificationsStore.client.dismissNotifications([ + ...Array.from([] as ActionHash[]).concat( + ...unreadGroups.map(group => + group.notifications.map(([hash, _]) => + decodeHashFromBase64(hash), + ), ), - )} - ${unreadNonPersistent.length > 0 && - readNonPersistent.length > 0 - ? html`` - : html``} - ${readNonPersistent.map((n, i) => - this.renderNotificationGroup( - true, - false, - n, - i === readNonPersistent.length - 1, + ), + ...Array.from([] as ActionHash[]).concat( + ...readGroups.map(group => + group.notifications.map(([hash, _]) => + decodeHashFromBase64(hash), + ), ), - )} -
- ` - : html``} + ), + ])} + size="small" + > + + ${msg('Dismiss')} +
+ + +
+ ${unreadGroups.map((n, i) => + this.renderNotificationGroup( + false, + n, + i === unreadGroups.length - 1, + ), + )} + ${unreadGroups.length > 0 && readGroups.length > 0 + ? html`` + : html``} + ${readGroups.map((n, i) => + this.renderNotificationGroup( + true, + n, + i === readGroups.length - 1, + ), + )} +
` diff --git a/ui/src/notifications-client.ts b/ui/src/notifications-client.ts index 45c31a1d..82c9cbb3 100644 --- a/ui/src/notifications-client.ts +++ b/ui/src/notifications-client.ts @@ -14,6 +14,7 @@ import { SignedActionHashed, encodeHashToBase64, } from '@holochain/client'; +import { encode } from '@msgpack/msgpack'; import { Notification, @@ -32,8 +33,20 @@ export class NotificationsClient extends ZomeClient { } /** Notification */ - async sendNotification(notification: Notification): Promise { - await this.callZome('send_notification', notification); + async sendNotification( + recipientProfileHash: ActionHash, + zomeName: string, + notificationType: string, + notificationGroup: string, + content: any, + ): Promise { + await this.callZome('send_notification', { + zome_name: zomeName, + notification_type: notificationType, + notification_group: notificationGroup, + content: encode(content), + recipient_profile_hash: recipientProfileHash, + }); } async markNotificationsAsRead(notificationsHashes: EntryHash[]) { @@ -71,7 +84,10 @@ export class NotificationsClient extends ZomeClient { role_name: this.roleName, zome_name: notification.zome_name, fn_name: 'get_notification_contents', - payload: notification, + payload: { + locale: 'en', + notification, + }, }); } } diff --git a/ui/src/notifications-store.ts b/ui/src/notifications-store.ts index 196897bb..65d37d2d 100644 --- a/ui/src/notifications-store.ts +++ b/ui/src/notifications-store.ts @@ -1,6 +1,7 @@ import { ProfilesStore } from '@holochain-open-dev/profiles'; import { AsyncComputed, + AsyncSignal, AsyncState, Signal, deletedLinksSignal, @@ -95,13 +96,18 @@ export class NotificationsStore { return signal; } - _notificationContents = new HoloHashMap(); - async notificationContents( + private _notificationContents = new HoloHashMap< + EntryHash, + AsyncSignal + >(); + notificationContents( notificationHash: EntryHash, notification: Notification, ) { if (!this._notificationContents.has(notificationHash)) { - const contents = await this.client.getNotificationContents(notification); + const contents = fromPromise(() => + this.client.getNotificationContents(notification), + ); this._notificationContents.set(notificationHash, contents); } return this._notificationContents.get(notificationHash); diff --git a/ui/src/types.ts b/ui/src/types.ts index f97a7d4e..4fb60a7d 100644 --- a/ui/src/types.ts +++ b/ui/src/types.ts @@ -55,4 +55,5 @@ export interface NotificationContents { title: string; body: string; icon_src: string; + url_path_to_navigate_to_on_click: string; } diff --git a/workdir/dna.nix b/workdir/dna.nix index eccf7a2d..ecdd8947 100644 --- a/workdir/dna.nix +++ b/workdir/dna.nix @@ -12,6 +12,8 @@ # This overrides all the "bundled" properties for the DNA manifest notifications_integrity = self'.packages.notifications_integrity; notifications = self'.packages.notifications; + example_integrity = self'.packages.example_integrity; + example = self'.packages.example; }; }; }; diff --git a/workdir/dna.yaml b/workdir/dna.yaml index 67e3cde0..d70cc311 100644 --- a/workdir/dna.yaml +++ b/workdir/dna.yaml @@ -12,6 +12,9 @@ integrity: - name: profiles_integrity hash: ~ bundled: + - name: example_integrity + hash: ~ + bundled: coordinator: zomes: - name: notifications @@ -24,3 +27,8 @@ coordinator: bundled: dependencies: - name: profiles_integrity + - name: example + hash: ~ + bundled: + dependencies: + - name: example_integrity diff --git a/zomes/coordinator/notifications/src/notification.rs b/zomes/coordinator/notifications/src/notification.rs index c77fa4ab..348e33d2 100644 --- a/zomes/coordinator/notifications/src/notification.rs +++ b/zomes/coordinator/notifications/src/notification.rs @@ -154,8 +154,25 @@ pub fn synchronize_with_other_agents_for_my_profile() -> ExternResult<()> { Ok(()) } +#[derive(Serialize, Deserialize, Debug)] +pub struct SendNotificationInput { + pub zome_name: ZomeName, + pub notification_type: String, + pub notification_group: String, + pub recipient_profile_hash: ActionHash, + pub content: SerializedBytes, +} + #[hdk_extern] -pub fn send_notification(notification: Notification) -> ExternResult<()> { +pub fn send_notification(input: SendNotificationInput) -> ExternResult<()> { + let notification = Notification { + timestamp: sys_time()?, + zome_name: input.zome_name, + notification_type: input.notification_type, + notification_group: input.notification_group, + recipient_profile_hash: input.recipient_profile_hash, + content: input.content, + }; let recipient = notification.recipient_profile_hash.clone(); let agents = get_agents_for_profile(recipient)?;