mirror of
https://github.com/getpaseo/paseo.git
synced 2026-08-14 20:32:46 +00:00
fix(app): resolve create agent dictation confirm handler timing issue
Fixed a state management bug where clicking the checkmark in create agent dictation mode would not trigger processing. The issue was that setIsDictationProcessing(true) was called after stopping the recorder, causing a race condition where the UI never showed the processing state. Changes: - Move setIsDictationProcessing(true) to execute immediately at the start of the confirm handler - Add proper cleanup when audioData is null - Ensure UI shows loading spinner when checkmark is clicked This aligns the create agent dictation behavior with the working agent chat dictation implementation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,55 +1,131 @@
|
||||
import { create } from "zustand";
|
||||
import { useSyncExternalStore } from "react";
|
||||
import type { SessionContextValue } from "@/contexts/session-context";
|
||||
|
||||
// SessionData mirrors SessionContextValue so consumers can subscribe to a single source of truth.
|
||||
export type SessionData = SessionContextValue;
|
||||
|
||||
interface SessionStore {
|
||||
interface SessionStoreState {
|
||||
sessions: Record<string, SessionData>;
|
||||
}
|
||||
|
||||
interface SessionStore extends SessionStoreState {
|
||||
setSession: (serverId: string, data: SessionData) => void;
|
||||
updateSession: (serverId: string, partial: Partial<SessionData>) => void;
|
||||
clearSession: (serverId: string) => void;
|
||||
getSession: (serverId: string) => SessionData | undefined;
|
||||
}
|
||||
|
||||
export const useSessionStore = create<SessionStore>((set, get) => ({
|
||||
sessions: {},
|
||||
setSession: (serverId, data) => {
|
||||
set((state) => ({
|
||||
type SessionListener = () => void;
|
||||
|
||||
let storeState: SessionStoreState = { sessions: {} };
|
||||
const listeners = new Set<SessionListener>();
|
||||
|
||||
const emit = () => {
|
||||
for (const listener of listeners) {
|
||||
listener();
|
||||
}
|
||||
};
|
||||
|
||||
const updateStoreState = (updater: (prev: SessionStoreState) => SessionStoreState) => {
|
||||
const next = updater(storeState);
|
||||
if (next === storeState) {
|
||||
return;
|
||||
}
|
||||
storeState = next;
|
||||
emit();
|
||||
};
|
||||
|
||||
const setSession: SessionStore["setSession"] = (serverId, data) => {
|
||||
updateStoreState((prev) => {
|
||||
if (prev.sessions[serverId] === data) {
|
||||
return prev;
|
||||
}
|
||||
return {
|
||||
sessions: {
|
||||
...state.sessions,
|
||||
...prev.sessions,
|
||||
[serverId]: data,
|
||||
},
|
||||
}));
|
||||
},
|
||||
updateSession: (serverId, partial) => {
|
||||
set((state) => {
|
||||
const existing = state.sessions[serverId];
|
||||
const next: SessionData | undefined = existing
|
||||
? { ...existing, ...partial, serverId }
|
||||
: (partial.serverId ? { ...(partial as SessionData), serverId } : undefined);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
if (!next) {
|
||||
return state;
|
||||
}
|
||||
const updateSession: SessionStore["updateSession"] = (serverId, partial) => {
|
||||
updateStoreState((prev) => {
|
||||
const existing = prev.sessions[serverId];
|
||||
const next: SessionData | undefined = existing
|
||||
? { ...existing, ...partial, serverId }
|
||||
: partial.serverId
|
||||
? ({ ...(partial as SessionData), serverId })
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
sessions: {
|
||||
...state.sessions,
|
||||
[serverId]: next,
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
clearSession: (serverId) => {
|
||||
set((state) => {
|
||||
if (!(serverId in state.sessions)) {
|
||||
return state;
|
||||
}
|
||||
const nextSessions = { ...state.sessions };
|
||||
delete nextSessions[serverId];
|
||||
return { sessions: nextSessions };
|
||||
});
|
||||
},
|
||||
getSession: (serverId) => get().sessions[serverId],
|
||||
}));
|
||||
if (!next) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
if (existing && shallowEqual(existing, next)) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
return {
|
||||
sessions: {
|
||||
...prev.sessions,
|
||||
[serverId]: next,
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const clearSession: SessionStore["clearSession"] = (serverId) => {
|
||||
updateStoreState((prev) => {
|
||||
if (!(serverId in prev.sessions)) {
|
||||
return prev;
|
||||
}
|
||||
const nextSessions = { ...prev.sessions };
|
||||
delete nextSessions[serverId];
|
||||
return { sessions: nextSessions };
|
||||
});
|
||||
};
|
||||
|
||||
const getSession: SessionStore["getSession"] = (serverId) => {
|
||||
return storeState.sessions[serverId];
|
||||
};
|
||||
|
||||
const subscribe = (listener: SessionListener) => {
|
||||
listeners.add(listener);
|
||||
return () => {
|
||||
listeners.delete(listener);
|
||||
};
|
||||
};
|
||||
|
||||
const buildSnapshot = (): SessionStore => ({
|
||||
sessions: storeState.sessions,
|
||||
setSession,
|
||||
updateSession,
|
||||
clearSession,
|
||||
getSession,
|
||||
});
|
||||
|
||||
const shallowEqual = (left: SessionData, right: SessionData): boolean => {
|
||||
if (left === right) {
|
||||
return true;
|
||||
}
|
||||
const leftEntries = Object.entries(left);
|
||||
const rightEntries = Object.entries(right);
|
||||
if (leftEntries.length !== rightEntries.length) {
|
||||
return false;
|
||||
}
|
||||
for (const [key, value] of leftEntries) {
|
||||
if (right[key as keyof SessionData] !== value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
export function useSessionStore<T>(selector: (state: SessionStore) => T): T {
|
||||
return useSyncExternalStore(
|
||||
subscribe,
|
||||
() => selector(buildSnapshot()),
|
||||
() => selector(buildSnapshot())
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user