fix(dictation): stop dictation before edit-save, remove expect() in openai_client

- Stop active dictation run before the edit branch clears/saves, matching
  the normal-send path. Prevents late transcript events from writing back
  into the restored or fresh draft after an edit save.
- Replace .expect() in openai_client() with proper error propagation via
  Result, avoiding a panic if TLS backend initialization ever fails.

Addresses review feedback on PR #1511.
This commit is contained in:
klopez4212
2026-07-11 16:18:36 +01:00
parent d493378835
commit 1815e02931
2 changed files with 22 additions and 8 deletions
+21 -8
View File
@@ -91,7 +91,9 @@ pub async fn create_transcribe_session(
let model = state.config.transcription_model.clone();
let response = openai_client()
let client = openai_client().map_err(|(status, msg)| api_error(status, msg))?;
let response = client
.post(OPENAI_REALTIME_CLIENT_SECRETS_URL)
.header("Authorization", format!("Bearer {api_key}"))
.header("Content-Type", "application/json")
@@ -220,14 +222,25 @@ fn transcribe_rate_limited(state: &AppState, community: CommunityId, pubkey: &no
}
/// Shared HTTP client for OpenAI requests (connection pooling).
fn openai_client() -> &'static reqwest::Client {
fn openai_client() -> Result<&'static reqwest::Client, (StatusCode, &'static str)> {
static CLIENT: std::sync::OnceLock<reqwest::Client> = std::sync::OnceLock::new();
CLIENT.get_or_init(|| {
reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.build()
.expect("failed to build reqwest client")
})
// `get_or_init` only runs the closure once; if TLS backend init fails we
// propagate an API error instead of panicking in production.
if let Some(c) = CLIENT.get() {
return Ok(c);
}
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.build()
.map_err(|_| {
(
StatusCode::INTERNAL_SERVER_ERROR,
"failed to initialize HTTP client",
)
})?;
// Another thread may have raced us — `get_or_init` is fine here since we
// already proved construction succeeds.
Ok(CLIENT.get_or_init(|| client))
}
fn extract_client_secret(value: &Value) -> Option<String> {
@@ -558,6 +558,7 @@ function MessageComposerImpl({
// Edit mode
if (editTargetRef.current && onEditSaveRef.current) {
if (isSendingRef.current || isUploadingRef.current) return;
stopDictationRef.current(); // stop dictation so late transcripts don't refill during edit save
const currentPendingImeta = media.pendingImetaRef.current;
const hasMedia = currentPendingImeta.length > 0;
// Empty text + zero attachments is a no-op (don't let edit become an