mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
notification hook
This commit is contained in:
1 parent
77ec1fb43a
commit
de4a70009d
14 files changed
+136
-8
No files matched your search
+57
@@ -0,0 +1,57 @@
|
||||
use crate::core::commands::enqueue::generate_error::{BillingIssueReason, BillingProvider, GenerateError, ProviderFailureReason};
|
||||
use crate::core::events::functional_events::show_provider_billing_modal_event::ShowProviderBillingModalEvent;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use log::warn;
|
||||
use storyteller_client::error::api_error::ApiError;
|
||||
use storyteller_client::error::storyteller_error::StorytellerError;
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn maybe_notify_frontend_of_billing_errors(
|
||||
app: &AppHandle,
|
||||
error: &GenerateError,
|
||||
) {
|
||||
match error {
|
||||
GenerateError::BillingIssue(reason) => {
|
||||
billing_error(app, reason);
|
||||
}
|
||||
GenerateError::ProviderFailure(reason) => {
|
||||
provider_billing_error(app, reason);
|
||||
}
|
||||
_ => {
|
||||
// Do nothing for other types of errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn billing_error(
|
||||
app: &AppHandle,
|
||||
reason: &BillingIssueReason,
|
||||
) {
|
||||
let provider = match reason.provider {
|
||||
BillingProvider::Fal => GenerationProvider::Fal,
|
||||
BillingProvider::Midjourney => GenerationProvider::Midjourney,
|
||||
BillingProvider::Sora => GenerationProvider::Sora,
|
||||
BillingProvider::Storyteller => GenerationProvider::Artcraft,
|
||||
};
|
||||
warn!("Billing issue with: {:?}", provider);
|
||||
ShowProviderBillingModalEvent::send_for_provider(provider, app);
|
||||
}
|
||||
|
||||
fn provider_billing_error(
|
||||
app: &AppHandle,
|
||||
error: &ProviderFailureReason,
|
||||
) {
|
||||
let provider;
|
||||
|
||||
match error {
|
||||
ProviderFailureReason::StorytellerError(StorytellerError::Api(ApiError::PaymentRequired(reason))) => {
|
||||
warn!("Billing issue with Artcraft/Storyteller: {}", reason);
|
||||
provider = GenerationProvider::Artcraft;
|
||||
}
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ShowProviderBillingModalEvent::send_for_provider(provider, app);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod notify_frontend_of_errors;
|
||||
mod maybe_notify_frontend_of_billing_errors;
|
||||
@@ -0,0 +1,10 @@
|
||||
use crate::core::commands::enqueue::common::maybe_notify_frontend_of_billing_errors::maybe_notify_frontend_of_billing_errors;
|
||||
use crate::core::commands::enqueue::generate_error::GenerateError;
|
||||
use tauri::AppHandle;
|
||||
|
||||
pub async fn notify_frontend_of_errors(
|
||||
app: &AppHandle,
|
||||
errors: &GenerateError,
|
||||
) {
|
||||
maybe_notify_frontend_of_billing_errors(app, errors).await;
|
||||
}
|
||||
+3
@@ -1,3 +1,4 @@
|
||||
use crate::core::commands::enqueue::common::notify_frontend_of_errors::notify_frontend_of_errors;
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_bg_removal::generic::handle_generic_bg_removal::handle_generic_bg_removal;
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit::handle_gpt_image_1_edit;
|
||||
@@ -123,6 +124,8 @@ pub async fn enqueue_image_bg_removal_command(
|
||||
match result {
|
||||
Err(err) => {
|
||||
error!("Error removing background: {:?}", err);
|
||||
|
||||
notify_frontend_of_errors(&app, &err).await;
|
||||
|
||||
// TODO: Derive from err. Make service provider optional.
|
||||
let event = GenerationEnqueueFailureEvent {
|
||||
|
||||
+3
@@ -1,3 +1,4 @@
|
||||
use crate::core::commands::enqueue::common::notify_frontend_of_errors::notify_frontend_of_errors;
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_edit::flux_kontext::handle_flux_kontext_edit::handle_flux_kontext_edit;
|
||||
use crate::core::commands::enqueue::image_edit::gemini_25_flash::handle_gemini_25_flash_edit::handle_gemini_25_flash_edit;
|
||||
@@ -194,6 +195,8 @@ pub async fn enqueue_contextual_edit_image_command(
|
||||
match result {
|
||||
Err(err) => {
|
||||
error!("Error enqueuing contextual edit image: {:?}", err);
|
||||
|
||||
notify_frontend_of_errors(&app, &err).await;
|
||||
|
||||
// TODO: Derive from err. Make service provider optional.
|
||||
let event = GenerationEnqueueFailureEvent {
|
||||
|
||||
+3
@@ -1,3 +1,4 @@
|
||||
use crate::core::commands::enqueue::common::notify_frontend_of_errors::notify_frontend_of_errors;
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError};
|
||||
use crate::core::commands::enqueue::image_edit::gpt_image_1::handle_gpt_image_1_edit::handle_gpt_image_1_edit;
|
||||
use crate::core::commands::enqueue::image_inpaint::flux_dev_juggernaut_inpaint::handle_flux_dev_juggernaut_inpaint::handle_flux_dev_juggernaut_inpaint;
|
||||
@@ -183,6 +184,8 @@ pub async fn enqueue_image_inpaint_command(
|
||||
match result {
|
||||
Err(err) => {
|
||||
error!("Error enqueuing inpaint image: {:?}", err);
|
||||
|
||||
notify_frontend_of_errors(&app, &err).await;
|
||||
|
||||
// TODO: Derive from err. Make service provider optional.
|
||||
let event = GenerationEnqueueFailureEvent {
|
||||
|
||||
+3
@@ -1,3 +1,4 @@
|
||||
use crate::core::commands::enqueue::common::notify_frontend_of_errors::notify_frontend_of_errors;
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError, MissingCredentialsReason};
|
||||
use crate::core::commands::enqueue::image_to_object::generic::handle_object::handle_object;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
@@ -96,6 +97,8 @@ pub async fn enqueue_image_to_3d_object_command(
|
||||
match result {
|
||||
Err(err) => {
|
||||
error!("error: {:?}", err);
|
||||
|
||||
notify_frontend_of_errors(&app, &err).await;
|
||||
|
||||
let mut status = CommandErrorStatus::ServerError;
|
||||
let mut error_type = EnqueueImageTo3dObjectErrorType::ServerError;
|
||||
|
||||
+4
-1
@@ -1,3 +1,4 @@
|
||||
use crate::core::commands::enqueue::common::notify_frontend_of_errors::notify_frontend_of_errors;
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError, MissingCredentialsReason, ProviderFailureReason};
|
||||
use crate::core::commands::enqueue::image_to_video::generic::handle_video::handle_video;
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
@@ -20,7 +21,7 @@ use serde_derive::{Deserialize, Serialize};
|
||||
use tauri::{AppHandle, State};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
/// This is used in the Tauri command bridge.
|
||||
/// This is used in the Tauri command bridge.
|
||||
/// Don't change the serializations without coordinating with the frontend.
|
||||
#[derive(Deserialize, Debug, Copy, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -116,6 +117,8 @@ pub async fn enqueue_image_to_video_command(
|
||||
match result {
|
||||
Err(err) => {
|
||||
error!("error: {:?}", err);
|
||||
|
||||
notify_frontend_of_errors(&app, &err).await;
|
||||
|
||||
let mut status = CommandErrorStatus::ServerError;
|
||||
let mut error_type = EnqueueImageToVideoErrorType::ServerError;
|
||||
|
||||
+3
@@ -1,3 +1,4 @@
|
||||
use crate::core::commands::enqueue::common::notify_frontend_of_errors::notify_frontend_of_errors;
|
||||
use crate::core::commands::enqueue::generate_error::{BadInputReason, GenerateError, MissingCredentialsReason};
|
||||
use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess;
|
||||
use crate::core::commands::enqueue::text_to_image::gemini_25_flash::handle_gemini_25_flash::handle_gemini_25_flash;
|
||||
@@ -173,6 +174,8 @@ pub async fn enqueue_text_to_image_command(
|
||||
Err(err) => {
|
||||
error!("error: {:?}", err);
|
||||
|
||||
notify_frontend_of_errors(&app, &err).await;
|
||||
|
||||
let mut status = CommandErrorStatus::ServerError;
|
||||
let mut error_type = EnqueueTextToImageErrorType::ServerError;
|
||||
let mut error_message = "A server error occurred. Please try again. If it continues, please tell our staff about the problem.";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::core::events::sendable_event_error::SendableEventError;
|
||||
use enums::tauri::ux::tauri_event_name::TauriEventName;
|
||||
use log::info;
|
||||
use log::{error, info};
|
||||
use serde::Serialize;
|
||||
use std::fmt::Debug;
|
||||
use tauri::{AppHandle, Emitter};
|
||||
@@ -49,4 +49,11 @@ pub trait BasicSendableEvent : Clone + Debug + Serialize {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_infallible(&self, app: &AppHandle) {
|
||||
if let Err(err) = self.send(app) {
|
||||
// Fail open
|
||||
error!("Failed to send event `{}`: {:?}", Self::FRONTEND_EVENT_NAME.to_str(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod canvas_background_removal_complete_event;
|
||||
pub mod image_edit_complete_event;
|
||||
pub mod refresh_account_state_event;
|
||||
pub mod show_provider_billing_modal_event;
|
||||
pub mod show_provider_login_modal_event;
|
||||
pub mod text_to_image_generation_complete_event;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
use crate::core::events::basic_sendable_event_trait::{BasicEventStatus, BasicSendableEvent};
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::ux::tauri_event_name::TauriEventName;
|
||||
use serde_derive::Serialize;
|
||||
use tauri::AppHandle;
|
||||
|
||||
/// Send a signal to the frontend to show a modal that suggests fixing or setting up
|
||||
/// service billing requirements.
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct ShowProviderBillingModalEvent {
|
||||
pub provider: GenerationProvider,
|
||||
}
|
||||
|
||||
impl ShowProviderBillingModalEvent {
|
||||
pub fn send_for_provider(provider: GenerationProvider, app: &AppHandle) {
|
||||
let event = Self { provider };
|
||||
event.send_infallible(&app);
|
||||
}
|
||||
}
|
||||
|
||||
impl BasicSendableEvent for ShowProviderBillingModalEvent {
|
||||
const FRONTEND_EVENT_NAME: TauriEventName = TauriEventName::ShowProviderBillingModalEvent;
|
||||
const EVENT_STATUS: BasicEventStatus = BasicEventStatus::Failure;
|
||||
}
|
||||
+1
-5
@@ -1,8 +1,6 @@
|
||||
use crate::core::events::basic_sendable_event_trait::{BasicEventStatus, BasicSendableEvent};
|
||||
use crate::core::events::sendable_event_error::SendableEventError;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::tauri::ux::tauri_event_name::TauriEventName;
|
||||
use log::error;
|
||||
use serde_derive::Serialize;
|
||||
use tauri::AppHandle;
|
||||
|
||||
@@ -16,9 +14,7 @@ pub struct ShowProviderLoginModalEvent {
|
||||
impl ShowProviderLoginModalEvent {
|
||||
pub fn send_for_provider(provider: GenerationProvider, app: &AppHandle) {
|
||||
let event = Self { provider };
|
||||
if let Err(err) = event.send(&app) {
|
||||
error!("Failed to send ShowProviderLoginModalEvent: {:?}", err); // Fail open
|
||||
}
|
||||
event.send_infallible(&app);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,11 @@ pub enum TauriEventName {
|
||||
#[serde(rename = "refresh_account_state_event")]
|
||||
RefreshAccountStateEvent,
|
||||
|
||||
/// Special event:
|
||||
/// Show a billing modal (a suggestion to pay, etc. for a specific provider)
|
||||
#[serde(rename = "show_provider_billing_modal_event")]
|
||||
ShowProviderBillingModalEvent,
|
||||
|
||||
/// Special event:
|
||||
/// Show a login modal (or a suggestion to login)
|
||||
#[serde(rename = "show_provider_login_modal_event")]
|
||||
@@ -86,6 +91,7 @@ impl TauriEventName {
|
||||
Self::TextToImageGenerationCompleteEvent => "text_to_image_generation_complete_event",
|
||||
Self::ImageEditCompleteEvent => "image_edit_complete_event",
|
||||
Self::RefreshAccountStateEvent => "refresh_account_state_event",
|
||||
Self::ShowProviderBillingModalEvent => "show_provider_billing_modal_event",
|
||||
Self::ShowProviderLoginModalEvent => "show_provider_login_modal_event",
|
||||
Self::FlashUserInputErrorEvent => "flash_user_input_error_event",
|
||||
}
|
||||
@@ -101,6 +107,7 @@ impl TauriEventName {
|
||||
"text_to_image_generation_complete_event" => Ok(Self::TextToImageGenerationCompleteEvent),
|
||||
"image_edit_complete_event" => Ok(Self::ImageEditCompleteEvent),
|
||||
"refresh_account_state_event" => Ok(Self::RefreshAccountStateEvent),
|
||||
"show_provider_billing_modal_event" => Ok(Self::ShowProviderBillingModalEvent),
|
||||
"show_provider_login_modal_event" => Ok(Self::ShowProviderLoginModalEvent),
|
||||
"flash_user_input_error_event" => Ok(Self::FlashUserInputErrorEvent),
|
||||
_ => Err(format!("invalid tauri_event_name: {:?}", job_status)),
|
||||
@@ -119,6 +126,7 @@ impl TauriEventName {
|
||||
Self::TextToImageGenerationCompleteEvent,
|
||||
Self::ImageEditCompleteEvent,
|
||||
Self::RefreshAccountStateEvent,
|
||||
Self::ShowProviderBillingModalEvent,
|
||||
Self::ShowProviderLoginModalEvent,
|
||||
Self::FlashUserInputErrorEvent,
|
||||
])
|
||||
@@ -143,6 +151,7 @@ mod tests {
|
||||
assert_serialization(TauriEventName::TextToImageGenerationCompleteEvent, "text_to_image_generation_complete_event");
|
||||
assert_serialization(TauriEventName::ImageEditCompleteEvent, "image_edit_complete_event");
|
||||
assert_serialization(TauriEventName::RefreshAccountStateEvent, "refresh_account_state_event");
|
||||
assert_serialization(TauriEventName::ShowProviderBillingModalEvent, "show_provider_billing_modal_event");
|
||||
assert_serialization(TauriEventName::ShowProviderLoginModalEvent, "show_provider_login_modal_event");
|
||||
assert_serialization(TauriEventName::FlashUserInputErrorEvent, "flash_user_input_error_event");
|
||||
}
|
||||
@@ -157,6 +166,7 @@ mod tests {
|
||||
assert_eq!(TauriEventName::TextToImageGenerationCompleteEvent.to_str(), "text_to_image_generation_complete_event");
|
||||
assert_eq!(TauriEventName::ImageEditCompleteEvent.to_str(), "image_edit_complete_event");
|
||||
assert_eq!(TauriEventName::RefreshAccountStateEvent.to_str(), "refresh_account_state_event");
|
||||
assert_eq!(TauriEventName::ShowProviderBillingModalEvent.to_str(), "show_provider_billing_modal_event");
|
||||
assert_eq!(TauriEventName::ShowProviderLoginModalEvent.to_str(), "show_provider_login_modal_event");
|
||||
assert_eq!(TauriEventName::FlashUserInputErrorEvent.to_str(), "flash_user_input_error_event");
|
||||
}
|
||||
@@ -171,6 +181,7 @@ mod tests {
|
||||
assert_eq!(TauriEventName::from_str("text_to_image_generation_complete_event").unwrap(), TauriEventName::TextToImageGenerationCompleteEvent);
|
||||
assert_eq!(TauriEventName::from_str("image_edit_complete_event").unwrap(), TauriEventName::ImageEditCompleteEvent);
|
||||
assert_eq!(TauriEventName::from_str("refresh_account_state_event").unwrap(), TauriEventName::RefreshAccountStateEvent);
|
||||
assert_eq!(TauriEventName::from_str("show_provider_billing_modal_event").unwrap(), TauriEventName::ShowProviderBillingModalEvent);
|
||||
assert_eq!(TauriEventName::from_str("show_provider_login_modal_event").unwrap(), TauriEventName::ShowProviderLoginModalEvent);
|
||||
assert_eq!(TauriEventName::from_str("flash_user_input_error_event").unwrap(), TauriEventName::FlashUserInputErrorEvent);
|
||||
}
|
||||
@@ -178,7 +189,7 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = TauriEventName::all_variants();
|
||||
assert_eq!(variants.len(), 10);
|
||||
assert_eq!(variants.len(), 11);
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::GenerationEnqueueSuccessEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::GenerationEnqueueFailureEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::GenerationCompleteEvent));
|
||||
@@ -187,6 +198,7 @@ mod tests {
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::TextToImageGenerationCompleteEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::ImageEditCompleteEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::RefreshAccountStateEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::ShowProviderBillingModalEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::ShowProviderLoginModalEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::FlashUserInputErrorEvent));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
|
||||
Reference in new issue
Block a user