Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 72 additions & 4 deletions src-tauri/src/commands/conversations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use std::collections::{HashMap, HashSet};
use crate::app_error::AppCommandError;
use crate::db::entities::conversation;
use crate::db::entities::folder::FolderKind;
use crate::db::service::{conversation_service, folder_service, import_service, tab_service};
use crate::db::service::{
conversation_composer_draft_service, conversation_service, folder_service, import_service,
tab_service,
};
#[cfg(feature = "tauri-runtime")]
use crate::db::AppDatabase;
use crate::models::*;
Expand All @@ -26,9 +29,10 @@ use crate::parsers::{
ParseError,
};
use crate::web::event_bridge::{
emit_event, ConversationChange, ConversationsBulkChanged, EventEmitter, ImportScanProgress,
TabsChanged, CONVERSATIONS_BULK_CHANGED_EVENT, CONVERSATION_CHANGED_EVENT,
IMPORT_SCAN_PROGRESS_EVENT, TABS_CHANGED_EVENT,
emit_event, ComposerDraftChanged, ConversationChange, ConversationsBulkChanged, EventEmitter,
ImportScanProgress, TabsChanged, COMPOSER_DRAFT_CHANGED_EVENT,
CONVERSATIONS_BULK_CHANGED_EVENT, CONVERSATION_CHANGED_EVENT, IMPORT_SCAN_PROGRESS_EVENT,
TABS_CHANGED_EVENT,
};

pub async fn list_all_conversations_core(
Expand Down Expand Up @@ -2000,6 +2004,70 @@ pub async fn update_conversation_pinned(
Ok(())
}

/// Fetch the unsent composer text for a persisted conversation. `None` when
/// no client has saved a draft yet. The body is only returned here — the
/// WS notify never carries it.
pub async fn get_composer_draft_core(
conn: &sea_orm::DatabaseConnection,
conversation_id: i32,
) -> Result<Option<conversation_composer_draft_service::ComposerDraft>, AppCommandError> {
conversation_composer_draft_service::get(conn, conversation_id)
.await
.map_err(AppCommandError::from)
}

#[cfg(feature = "tauri-runtime")]
#[cfg_attr(feature = "tauri-runtime", tauri::command)]
pub async fn get_composer_draft(
db: tauri::State<'_, AppDatabase>,
conversation_id: i32,
) -> Result<Option<conversation_composer_draft_service::ComposerDraft>, AppCommandError> {
get_composer_draft_core(&db.conn, conversation_id).await
}

/// Last-write-wins save of unsent composer text. Broadcasts
/// [`COMPOSER_DRAFT_CHANGED_EVENT`] with ids only (no body).
pub async fn put_composer_draft_core(
conn: &sea_orm::DatabaseConnection,
emitter: &EventEmitter,
conversation_id: i32,
text: String,
origin: String,
) -> Result<conversation_composer_draft_service::ComposerDraftPutResult, AppCommandError> {
let result =
conversation_composer_draft_service::put(conn, conversation_id, text, origin).await?;
emit_event(
emitter,
COMPOSER_DRAFT_CHANGED_EVENT,
ComposerDraftChanged {
conversation_id: result.conversation_id,
revision: result.revision,
origin: result.origin.clone(),
cleared: result.cleared,
},
);
Ok(result)
}

#[cfg(feature = "tauri-runtime")]
#[cfg_attr(feature = "tauri-runtime", tauri::command)]
pub async fn put_composer_draft(
app: tauri::AppHandle,
db: tauri::State<'_, AppDatabase>,
conversation_id: i32,
text: String,
origin: String,
) -> Result<conversation_composer_draft_service::ComposerDraftPutResult, AppCommandError> {
put_composer_draft_core(
&db.conn,
&EventEmitter::Tauri(app),
conversation_id,
text,
origin,
)
.await
}

pub async fn delete_conversation_core(
conn: &sea_orm::DatabaseConnection,
conversation_id: i32,
Expand Down
19 changes: 19 additions & 0 deletions src-tauri/src/db/entities/conversation_composer_draft.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use sea_orm::entity::prelude::*;

/// Per-conversation unsent composer text, shared across desktop / web / mobile.
/// See `crate::db::service::conversation_composer_draft_service`.
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "conversation_composer_draft")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub conversation_id: i32,
pub text: String,
pub revision: i64,
pub origin: String,
pub updated_at: DateTimeUtc,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}
1 change: 1 addition & 0 deletions src-tauri/src/db/entities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod chat_channel_message_log;
pub mod chat_channel_sender_context;
pub mod chat_channel_thread_binding;
pub mod conversation;
pub mod conversation_composer_draft;
pub mod custom_agent;
pub mod folder;
pub mod folder_command;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use sea_orm_migration::prelude::*;

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(ConversationComposerDraft::Table)
.if_not_exists()
.col(
ColumnDef::new(ConversationComposerDraft::ConversationId)
.integer()
.not_null()
.primary_key(),
)
.col(
ColumnDef::new(ConversationComposerDraft::Text)
.text()
.not_null(),
)
.col(
ColumnDef::new(ConversationComposerDraft::Revision)
.big_integer()
.not_null(),
)
.col(
ColumnDef::new(ConversationComposerDraft::Origin)
.string()
.not_null(),
)
.col(
ColumnDef::new(ConversationComposerDraft::UpdatedAt)
.timestamp_with_time_zone()
.not_null(),
)
.to_owned(),
)
.await
}

async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(
Table::drop()
.table(ConversationComposerDraft::Table)
.to_owned(),
)
.await
}
}

#[derive(DeriveIden)]
enum ConversationComposerDraft {
Table,
ConversationId,
Text,
Revision,
Origin,
UpdatedAt,
}
2 changes: 2 additions & 0 deletions src-tauri/src/db/migration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ mod m20260803_000001_folder_link;
mod m20260803_000001_token_usage;
mod m20260807_000001_work_task_scheduled_at;
mod m20260808_000001_custom_agent_supports_mcp;
mod m20260815_000002_conversation_composer_draft;
pub struct Migrator;

#[async_trait::async_trait]
Expand Down Expand Up @@ -80,6 +81,7 @@ impl MigratorTrait for Migrator {
Box::new(m20260803_000001_token_usage::Migration),
Box::new(m20260807_000001_work_task_scheduled_at::Migration),
Box::new(m20260808_000001_custom_agent_supports_mcp::Migration),
Box::new(m20260815_000002_conversation_composer_draft::Migration),
]
}
}
Loading