use hdk::prelude::*;
use notification::{receive_change_notifications_status, receive_notification};
use notifications_integrity::*;
use notifications_types::{Notification, NotificationsStatusChanges};
pub mod encrypted_message;
pub mod linked_devices;
pub mod notification;
pub mod notifications_settings;
pub mod synchronize;
pub mod utils;
#[hdk_extern]
pub fn init(_: ()) -> ExternResult<InitCallbackResult> {
let mut fns: BTreeSet<GrantedFunction> = BTreeSet::new();
fns.insert((zome_info()?.name, FunctionName::from("recv_remote_signal")));
let functions = GrantedFunctions::Listed(fns);
let cap_grant = ZomeCallCapGrant {
tag: String::from("receive_notifications_calls"),
access: CapAccess::Unrestricted,
functions,
};
create_cap_grant(cap_grant)?;
schedule("scheduled_synchronize_with_linked_devices")?;
Ok(InitCallbackResult::Pass)
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
pub enum Signal {
LinkCreated {
action: SignedActionHashed,
link_type: LinkTypes,
},
LinkDeleted {
action: SignedActionHashed,
create_link_action: SignedActionHashed,
link_type: LinkTypes,
},
EntryCreated {
action: SignedActionHashed,
app_entry: EntryTypes,
},
EntryUpdated {
action: SignedActionHashed,
app_entry: EntryTypes,
original_app_entry: EntryTypes,
},
EntryDeleted {
action: SignedActionHashed,
original_app_entry: EntryTypes,
},
}
#[derive(Serialize, Deserialize, Debug)]
pub enum NotificationsRemoteSignal {
Notification(Notification),
NotificationsStatusChanges(NotificationsStatusChanges),
}
#[hdk_extern]
pub fn recv_remote_signal(signal: NotificationsRemoteSignal) -> ExternResult<()> {
match signal {
NotificationsRemoteSignal::Notification(notification) => receive_notification(notification),
NotificationsRemoteSignal::NotificationsStatusChanges(notifications_status_changes) => {
receive_change_notifications_status(notifications_status_changes)
}
}
}
#[hdk_extern(infallible)]
pub fn post_commit(committed_actions: Vec<SignedActionHashed>) {
for action in committed_actions {
if let Err(err) = signal_action(action) {
error!("Error signaling new action: {:?}", err);
}
}
}
fn signal_action(action: SignedActionHashed) -> ExternResult<()> {
match action.hashed.content.clone() {
Action::CreateLink(create_link) => {
if let Ok(Some(link_type)) =
LinkTypes::from_type(create_link.zome_index, create_link.link_type)
{
emit_signal(Signal::LinkCreated {
action: action.clone(),
link_type,
})?;
}
Ok(())
}
Action::DeleteLink(delete_link) => {
let record = get(delete_link.link_add_address.clone(), GetOptions::default())?.ok_or(
wasm_error!(WasmErrorInner::Guest(
"Failed to fetch CreateLink action".to_string()
)),
)?;
match record.action() {
Action::CreateLink(create_link) => {
if let Ok(Some(link_type)) =
LinkTypes::from_type(create_link.zome_index, create_link.link_type)
{
emit_signal(Signal::LinkDeleted {
action,
link_type,
create_link_action: record.signed_action.clone(),
})?;
}
Ok(())
}
_ => Err(wasm_error!(WasmErrorInner::Guest(
"Create Link should exist".to_string()
))),
}
}
Action::Create(_create) => {
if let Ok(Some(app_entry)) = get_entry_for_action(&action.hashed.hash) {
emit_signal(Signal::EntryCreated {
action: action.clone(),
app_entry: app_entry.clone(),
})?;
}
Ok(())
}
Action::Update(update) => {
if let Ok(Some(app_entry)) = get_entry_for_action(&action.hashed.hash) {
if let Ok(Some(original_app_entry)) =
get_entry_for_action(&update.original_action_address)
{
emit_signal(Signal::EntryUpdated {
action,
app_entry,
original_app_entry,
})?;
}
}
Ok(())
}
Action::Delete(delete) => {
if let Ok(Some(original_app_entry)) = get_entry_for_action(&delete.deletes_address) {
emit_signal(Signal::EntryDeleted {
action,
original_app_entry,
})?;
}
Ok(())
}
_ => Ok(()),
}
}
fn get_entry_for_action(action_hash: &ActionHash) -> ExternResult<Option<EntryTypes>> {
let record = match get_details(action_hash.clone(), GetOptions::default())? {
Some(Details::Record(record_details)) => record_details.record,
_ => {
return Ok(None);
}
};
let entry = match record.entry().as_option() {
Some(entry) => entry,
None => {
return Ok(None);
}
};
let (zome_index, entry_index) = match record.action().entry_type() {
Some(EntryType::App(AppEntryDef {
zome_index,
entry_index,
..
})) => (zome_index, entry_index),
_ => {
return Ok(None);
}
};
EntryTypes::deserialize_from_type(*zome_index, *entry_index, entry)
}