fix: address final review findings

- Finding 1: add AppState::authed_get/authed_post helpers that attach
  Bearer token when present; all 4 commands now use them
- Finding 2: MessageInput accepts onSent callback, calls it after
  successful send; ChatPage passes refreshKey bump as onSent
- Finding 3: MessageList useEffect uses cancellation flag so stale
  in-flight responses cannot overwrite a newly selected conversation
- Finding 4: scrollIntoView moved to separate effect keyed on
  messages.length so scroll only fires when new messages arrive
This commit is contained in:
2026-08-12 17:58:02 +02:00
parent 91260c8e30
commit 455d304885
7 changed files with 56 additions and 17 deletions

View File

@@ -7,8 +7,7 @@ pub async fn list_conversations(
workspace_id: String,
) -> Result<Vec<Conversation>, String> {
state
.client
.get(format!("{}/conversations?workspace_id={}", BASE_URL, workspace_id))
.authed_get(&format!("{}/conversations?workspace_id={}", BASE_URL, workspace_id))
.send()
.await
.map_err(|e| e.to_string())?

View File

@@ -7,8 +7,7 @@ pub async fn list_messages(
conversation_id: String,
) -> Result<Vec<Message>, String> {
state
.client
.get(format!("{}/conversations/{}/messages", BASE_URL, conversation_id))
.authed_get(&format!("{}/conversations/{}/messages", BASE_URL, conversation_id))
.send()
.await
.map_err(|e| e.to_string())?
@@ -37,8 +36,7 @@ pub async fn send_message(
});
state
.client
.post(format!("{}/messages", BASE_URL))
.authed_post(&format!("{}/messages", BASE_URL))
.json(&body)
.send()
.await

View File

@@ -4,8 +4,7 @@ use crate::state::{AppState, BASE_URL};
#[tauri::command]
pub async fn list_workspaces(state: tauri::State<'_, AppState>) -> Result<Vec<Workspace>, String> {
state
.client
.get(format!("{}/workspaces", BASE_URL))
.authed_get(&format!("{}/workspaces", BASE_URL))
.send()
.await
.map_err(|e| e.to_string())?

View File

@@ -16,4 +16,21 @@ impl AppState {
user_id: Mutex::new(None),
}
}
pub fn authed_get(&self, url: &str) -> reqwest::RequestBuilder {
let builder = self.client.get(url);
// ponytail: omit header when no token — server may not require auth yet
match self.token.lock().unwrap().clone() {
Some(t) => builder.bearer_auth(t),
None => builder,
}
}
pub fn authed_post(&self, url: &str) -> reqwest::RequestBuilder {
let builder = self.client.post(url);
match self.token.lock().unwrap().clone() {
Some(t) => builder.bearer_auth(t),
None => builder,
}
}
}