///|
priv struct NotificationResultBroker {
mut busy : Bool
gate : @async.CondVar
results : Array[@native.NativeNotificationResult]
result_ready : @async.CondVar
}
///|
let notification_result_broker : NotificationResultBroker = NotificationResultBroker::{
busy: false,
gate: @async.CondVar::Cond(),
results: [],
result_ready: @async.CondVar::Cond(),
}
///|
let notification_extension_id = "moonbit-community/proton-notification"
///|
let notification_extension_namespace = "notification"
///|
fn notification_click_payload(payload : String?) -> Json {
Json::object({
"payload": match payload {
Some(payload) => Json(payload)
None => Json::null()
},
})
}
///|
async fn acquire_notification_result_slot() -> Unit {
while notification_result_broker.busy {
notification_result_broker.gate.wait()
}
notification_result_broker.busy = true
}
///|
fn release_notification_result_slot() -> Unit {
notification_result_broker.busy = false
notification_result_broker.gate.broadcast()
}
///|
fn publish_notification_result(
result : @native.NativeNotificationResult,
) -> Unit {
if notification_result_broker.busy {
notification_result_broker.results.push(result)
notification_result_broker.result_ready.broadcast()
}
}
///|
async fn take_notification_result() -> @native.NativeNotificationResult {
while notification_result_broker.results.length() == 0 {
notification_result_broker.result_ready.wait()
}
let result = notification_result_broker.results[0]
ignore(notification_result_broker.results.remove(0))
result
}
///|
fn notification_result_event_supported(features : Array[String]) -> Bool {
features.contains("notification_result")
}
///|
fn native_notification_result_supported() -> Bool raise @native.NativeError {
let info = @native.runtime_info()
notification_result_event_supported(info.features)
}
///|
/// Failures while starting or waiting for native notification delivery.
pub(all) suberror NotificationDeliveryError {
NativeFailure(status~ : Int, detail~ : String)
WaitInterrupted(detail~ : String)
DeliveryFailed(message~ : String)
} derive(Debug, Eq)
///|
pub extend NotificationDeliveryError with Eq::{not_equal, equal}
///|
pub extend NotificationDeliveryError with Debug::{to_repr}
///|
pub fn NotificationDeliveryError::message(
self : NotificationDeliveryError,
) -> String {
match self {
NativeFailure(status~, detail~) =>
"native notification operation failed (" +
status.to_string() +
"): " +
detail
WaitInterrupted(detail~) =>
"notification delivery wait was interrupted: " + detail
DeliveryFailed(message~) => "notification was not delivered: " + message
}
}
///|
/// Starts one native notification and waits for a completion event only on
/// platforms that advertise the `notification_result` runtime capability.
#doc(hidden)
pub async fn notification_show_and_wait(
title : String,
body : String,
payload? : String,
) -> Unit raise NotificationDeliveryError {
let waits_for_result = native_notification_result_supported() catch {
error => raise NativeFailure(status=error.status(), detail=error.message())
}
if waits_for_result {
acquire_notification_result_slot() catch {
error => raise WaitInterrupted(detail=@debug.render(Repr(error)))
}
defer release_notification_result_slot()
}
@native.notification_show(title, body, payload?) catch {
error => raise NativeFailure(status=error.status(), detail=error.message())
}
if waits_for_result {
let result = @async.protect_from_cancel(take_notification_result) catch {
error => raise WaitInterrupted(detail=@debug.render(Repr(error)))
}
match result {
@native.NativeNotificationResult::Delivered => ()
@native.NativeNotificationResult::Failed(message) =>
raise DeliveryFailed(message~)
}
}
}
///|
#doc(hidden)
pub fn notification_supported() -> Bool raise NotificationDeliveryError {
@native.notification_supported() catch {
error => raise NativeFailure(status=error.status(), detail=error.message())
}
}
///|
#doc(hidden)
pub fn notification_cleanup() -> Unit raise NotificationDeliveryError {
@native.notification_cleanup() catch {
error => raise NativeFailure(status=error.status(), detail=error.message())
}
}