diff --git a/apps/desktop/src/auth/auth-context.ts b/apps/desktop/src/auth/auth-context.ts index a06e481e7cc..2a5fff3e8c1 100644 --- a/apps/desktop/src/auth/auth-context.ts +++ b/apps/desktop/src/auth/auth-context.ts @@ -12,6 +12,7 @@ type AuthActions = { signIn: () => Promise; signOut: () => Promise; refreshSession: () => Promise; + getSessionForRequest: () => Promise; }; type AuthTokenHandlers = { diff --git a/apps/desktop/src/auth/client.test.ts b/apps/desktop/src/auth/client.test.ts new file mode 100644 index 00000000000..0c8f0e6353a --- /dev/null +++ b/apps/desktop/src/auth/client.test.ts @@ -0,0 +1,88 @@ +import { beforeEach, describe, expect, test, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + getItem: vi.fn(), + removeItem: vi.fn(), + setItem: vi.fn(), +})); + +vi.mock("@anlg/plugin-auth", () => ({ + commands: { + getItem: mocks.getItem, + removeItem: mocks.removeItem, + setItem: mocks.setItem, + }, +})); + +vi.mock("@supabase/supabase-js", () => ({ + createClient: vi.fn(() => ({})), + processLock: vi.fn(), +})); + +vi.mock("@tauri-apps/plugin-http", () => ({ fetch: vi.fn() })); + +vi.mock("~/env", () => ({ + env: { + VITE_SUPABASE_ANON_KEY: "anon-key", + VITE_SUPABASE_URL: "https://project.supabase.co", + }, +})); + +import { readPersistedAuthSession, tauriStorage } from "./client"; + +const authStorageKey = "sb-project-auth-token"; + +function serializedSession(accessToken: string) { + return JSON.stringify({ + access_token: accessToken, + refresh_token: "refresh-token", + token_type: "bearer", + user: { id: "user-id" }, + }); +} + +describe("auth storage", () => { + beforeEach(() => { + vi.clearAllMocks(); + mocks.getItem.mockResolvedValue({ status: "ok", data: null }); + mocks.removeItem.mockResolvedValue({ status: "ok", data: null }); + mocks.setItem.mockResolvedValue({ status: "ok", data: null }); + }); + + test("does not let the SDK clear the persisted session", async () => { + await tauriStorage.removeItem(authStorageKey); + + expect(mocks.removeItem).not.toHaveBeenCalled(); + }); + + test("persists the session once", async () => { + await tauriStorage.setItem(authStorageKey, "serialized-session"); + + expect(mocks.setItem).toHaveBeenCalledOnce(); + expect(mocks.setItem).toHaveBeenCalledWith( + authStorageKey, + "serialized-session", + ); + }); + + test("still removes auxiliary auth values", async () => { + await tauriStorage.removeItem("pkce-code-verifier"); + + expect(mocks.removeItem).toHaveBeenCalledWith("pkce-code-verifier"); + }); + + test("loads the persisted session after a cold start", async () => { + mocks.getItem.mockImplementation(async (key: string) => ({ + status: "ok", + data: + key === authStorageKey ? serializedSession("persisted-token") : null, + })); + + await expect(readPersistedAuthSession()).resolves.toEqual({ + access_token: "persisted-token", + refresh_token: "refresh-token", + token_type: "bearer", + user: { id: "user-id" }, + }); + }); +}); diff --git a/apps/desktop/src/auth/client.ts b/apps/desktop/src/auth/client.ts index 36c9939387e..5790ee983de 100644 --- a/apps/desktop/src/auth/client.ts +++ b/apps/desktop/src/auth/client.ts @@ -11,6 +11,10 @@ import { commands as authCommands } from "@anlg/plugin-auth"; import { env } from "~/env"; +const authStorageKey = env.VITE_SUPABASE_URL + ? `sb-${new URL(env.VITE_SUPABASE_URL).hostname.split(".")[0]}-auth-token` + : null; + export const tauriStorage: SupportedStorage = { async getItem(key: string): Promise { const result = await authCommands.getItem(key); @@ -26,6 +30,10 @@ export const tauriStorage: SupportedStorage = { } }, async removeItem(key: string): Promise { + if (key === authStorageKey) { + return; + } + const result = await authCommands.removeItem(key); if (result.status === "error") { throw new Error(`auth storage removeItem failed: ${result.error}`); @@ -33,9 +41,31 @@ export const tauriStorage: SupportedStorage = { }, }; -const authStorageKey = env.VITE_SUPABASE_URL - ? `sb-${new URL(env.VITE_SUPABASE_URL).hostname.split(".")[0]}-auth-token` - : null; +function parsePersistedSession(value: string | null): Session | null { + if (!value) { + return null; + } + + try { + const parsed = JSON.parse(value) as Partial; + return typeof parsed.access_token === "string" && + typeof parsed.refresh_token === "string" && + typeof parsed.token_type === "string" && + typeof parsed.user?.id === "string" + ? (parsed as Session) + : null; + } catch { + return null; + } +} + +export async function readPersistedAuthSession(): Promise { + if (!authStorageKey) { + return null; + } + + return parsePersistedSession(await tauriStorage.getItem(authStorageKey)); +} export async function persistAuthSession(session: Session): Promise { if (!authStorageKey) { diff --git a/apps/desktop/src/auth/context.test.tsx b/apps/desktop/src/auth/context.test.tsx index a85b88e7fe2..0b5a2387874 100644 --- a/apps/desktop/src/auth/context.test.tsx +++ b/apps/desktop/src/auth/context.test.tsx @@ -8,6 +8,7 @@ import { screen, waitFor, } from "@testing-library/react"; +import { useState } from "react"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { useAuth } from "./auth-context"; @@ -25,13 +26,14 @@ const mocks = vi.hoisted(() => ({ bindCloudsyncAccountForAuth: vi.fn(), clearAuthStorage: vi.fn(), currentWebviewWindowLabel: "main", + emit: vi.fn(), emitTo: vi.fn(), eventCallbacks: new Map void>(), focusCallback: null as ((event: { payload: boolean }) => void) | null, getSession: vi.fn(), handleCloudsyncAuthChange: vi.fn(), - isFatalSessionError: vi.fn(), persistAuthSession: vi.fn(), + readPersistedAuthSession: vi.fn(), prepareCloudsyncSignOut: vi.fn(), refreshCloudsyncForSession: vi.fn(), refreshSession: vi.fn(), @@ -44,6 +46,7 @@ const mocks = vi.hoisted(() => ({ vi.mock("./client", () => ({ persistAuthSession: mocks.persistAuthSession, + readPersistedAuthSession: mocks.readPersistedAuthSession, supabase: { auth: { getSession: mocks.getSession, @@ -79,7 +82,6 @@ vi.mock("./cloudsync", () => ({ vi.mock("./errors", () => ({ clearAuthStorage: mocks.clearAuthStorage, - isFatalSessionError: mocks.isFatalSessionError, })); vi.mock("@anlg/plugin-analytics", () => ({ @@ -130,6 +132,7 @@ vi.mock("@tauri-apps/api/app", () => ({ })); vi.mock("@tauri-apps/api/event", () => ({ + emit: mocks.emit, emitTo: mocks.emitTo, listen: vi.fn( (event: string, callback: (event: { payload: unknown }) => void) => { @@ -196,7 +199,9 @@ function makeSession(userId: string): Session { } function SessionProbe() { - const { getHeaders, refreshSession, session, signOut } = useAuth(); + const { getHeaders, getSessionForRequest, refreshSession, session, signOut } = + useAuth(); + const [requestAccessToken, setRequestAccessToken] = useState("none"); return ( <>
{session?.user.id ?? "none"}
@@ -204,9 +209,19 @@ function SessionProbe() {
{getHeaders()?.Authorization ?? "none"}
+
{requestAccessToken}
+ ); @@ -242,13 +257,14 @@ describe("AuthProvider", () => { mocks.bindCloudsyncAccountForAuth.mockReset(); mocks.clearAuthStorage.mockReset(); mocks.currentWebviewWindowLabel = "main"; + mocks.emit.mockReset(); mocks.emitTo.mockReset(); mocks.eventCallbacks.clear(); mocks.focusCallback = null; mocks.getSession.mockReset(); mocks.handleCloudsyncAuthChange.mockReset(); - mocks.isFatalSessionError.mockReset(); mocks.persistAuthSession.mockReset(); + mocks.readPersistedAuthSession.mockReset(); mocks.prepareCloudsyncSignOut.mockReset(); mocks.refreshCloudsyncForSession.mockReset(); mocks.refreshSession.mockReset(); @@ -259,11 +275,12 @@ describe("AuthProvider", () => { mocks.toastError.mockReset(); mocks.bindCloudsyncAccountForAuth.mockResolvedValue(true); mocks.clearAuthStorage.mockResolvedValue(undefined); + mocks.emit.mockResolvedValue(undefined); mocks.emitTo.mockResolvedValue(undefined); mocks.getSession.mockImplementation(() => new Promise(() => {})); mocks.handleCloudsyncAuthChange.mockResolvedValue("ok"); - mocks.isFatalSessionError.mockReturnValue(false); mocks.persistAuthSession.mockResolvedValue(undefined); + mocks.readPersistedAuthSession.mockResolvedValue(null); mocks.prepareCloudsyncSignOut.mockResolvedValue(undefined); mocks.refreshCloudsyncForSession.mockResolvedValue("ok"); mocks.refreshSession.mockResolvedValue({ @@ -312,9 +329,7 @@ describe("AuthProvider", () => { ); }); - act(() => { - mocks.authCallback?.("SIGNED_OUT", null); - }); + fireEvent.click(screen.getByRole("button", { name: "Sign out" })); await waitFor(() => { expect(mocks.analyticsClearGroups).toHaveBeenCalledTimes(1); @@ -340,9 +355,7 @@ describe("AuthProvider", () => { expect(mocks.analyticsIdentify).toHaveBeenCalledTimes(1); }); - act(() => { - mocks.authCallback?.("SIGNED_OUT", null); - }); + fireEvent.click(screen.getByRole("button", { name: "Sign out" })); await waitFor(() => { expect(mocks.clearAuthStorage).toHaveBeenCalledTimes(1); @@ -377,9 +390,7 @@ describe("AuthProvider", () => { }); }); - act(() => { - mocks.authCallback?.("SIGNED_OUT", null); - }); + fireEvent.click(screen.getByRole("button", { name: "Sign out" })); await waitFor(() => { expect(mocks.clearAuthStorage).toHaveBeenCalledTimes(1); @@ -402,7 +413,7 @@ describe("AuthProvider", () => { expect(mocks.focusCallback).not.toBeNull(); }); - mocks.getSession.mockResolvedValueOnce({ + mocks.getSession.mockResolvedValue({ data: { session: currentSession }, error: null, }); @@ -512,6 +523,97 @@ describe("AuthProvider", () => { expect(mocks.refreshCloudsyncForSession).not.toHaveBeenCalled(); }); + it("refreshes an expiring session before an authenticated request", async () => { + const staleSession = makeSession("bound-account"); + staleSession.expires_at = Math.floor(Date.now() / 1000) + 60; + const refreshedSession = makeSession("bound-account"); + mocks.getSession.mockResolvedValue({ + data: { session: staleSession }, + error: null, + }); + mocks.refreshSession.mockResolvedValueOnce({ + data: { session: refreshedSession }, + error: null, + }); + + renderAuthProvider(); + + fireEvent.click( + screen.getByRole("button", { name: "Get request session" }), + ); + + await waitFor(() => { + expect(mocks.refreshSession).toHaveBeenCalledTimes(1); + }); + }); + + it("uses the current session when the SDK lookup fails", async () => { + const currentSession = makeSession("bound-account"); + + renderAuthProvider(); + + await waitFor(() => { + expect(mocks.authCallback).not.toBeNull(); + }); + + act(() => { + mocks.authCallback?.("SIGNED_IN", currentSession); + }); + + await waitFor(() => { + expect(screen.getByTestId("session").textContent).toBe( + currentSession.user.id, + ); + }); + + mocks.getSession.mockRejectedValueOnce(new Error("offline")); + fireEvent.click( + screen.getByRole("button", { name: "Get request session" }), + ); + + await waitFor(() => { + expect(screen.getByTestId("request-access-token").textContent).toBe( + currentSession.access_token, + ); + }); + }); + + it("uses a still-valid session when proactive refresh fails", async () => { + const currentSession = makeSession("bound-account"); + currentSession.expires_at = Math.floor(Date.now() / 1000) + 60; + + renderAuthProvider(); + + await waitFor(() => { + expect(mocks.authCallback).not.toBeNull(); + }); + + act(() => { + mocks.authCallback?.("SIGNED_IN", currentSession); + }); + + await waitFor(() => { + expect(screen.getByTestId("session").textContent).toBe( + currentSession.user.id, + ); + }); + + mocks.getSession.mockResolvedValueOnce({ + data: { session: currentSession }, + error: null, + }); + mocks.refreshSession.mockRejectedValueOnce(new Error("offline")); + fireEvent.click( + screen.getByRole("button", { name: "Get request session" }), + ); + + await waitFor(() => { + expect(screen.getByTestId("request-access-token").textContent).toBe( + currentSession.access_token, + ); + }); + }); + it("only runs cloudsync from the main window", async () => { const currentSession = makeSession("bound-account"); mocks.currentWebviewWindowLabel = "note-session-id"; @@ -676,9 +778,14 @@ describe("AuthProvider", () => { expect(mocks.emitTo).toHaveBeenCalledTimes(1); }); - it("coordinates spontaneous secondary sign-out with main exactly once", async () => { + it("preserves a secondary-window session after an unsolicited SDK sign-out", async () => { const currentSession = makeSession("bound-account"); + const recoveredSession = { + ...makeSession("bound-account"), + access_token: "recovered-access-token", + }; mocks.currentWebviewWindowLabel = "note-session-id"; + vi.spyOn(console, "warn").mockImplementation(() => {}); renderAuthProvider(); @@ -700,116 +807,42 @@ describe("AuthProvider", () => { mocks.authCallback?.("SIGNED_OUT", null); }); - await waitFor(() => { - expect(mocks.emitTo).toHaveBeenCalledWith( - "main", - "anlg:auth-sign-out-request", - { - requestId: "request-id", - sourceLabel: "note-session-id", - }, - ); - }); + expect(screen.getByTestId("session").textContent).toBe( + currentSession.user.id, + ); + expect(screen.getByTestId("authorization").textContent).toBe( + `bearer ${currentSession.access_token}`, + ); + expect(mocks.emitTo).not.toHaveBeenCalled(); expect(mocks.clearAuthStorage).not.toHaveBeenCalled(); expect(mocks.signOut).not.toHaveBeenCalled(); act(() => { - mocks.authCallback?.("SIGNED_OUT", null); - mocks.eventCallbacks.get("anlg:auth-sign-out-result")?.({ - payload: { requestId: "request-id", completed: true, error: null }, - }); - }); - - await waitFor(() => { - expect(screen.getByTestId("session").textContent).toBe("none"); - expect(mocks.clearAuthStorage).toHaveBeenCalledTimes(1); - }); - expect(mocks.emitTo).toHaveBeenCalledTimes(1); - expect(mocks.signOut).not.toHaveBeenCalled(); - }); - - it("hides a spontaneous secondary sign-out while retrying incomplete main coordination", async () => { - const currentSession = makeSession("bound-account"); - mocks.currentWebviewWindowLabel = "note-session-id"; - - renderAuthProvider(); - - await waitFor(() => { - expect(mocks.authCallback).not.toBeNull(); - }); - - act(() => { - mocks.authCallback?.("SIGNED_IN", currentSession); + mocks.authCallback?.("TOKEN_REFRESHED", recoveredSession); }); await waitFor(() => { - expect(screen.getByTestId("session").textContent).toBe( - currentSession.user.id, + expect(screen.getByTestId("access-token").textContent).toBe( + recoveredSession.access_token, ); expect(screen.getByTestId("authorization").textContent).toBe( - `bearer ${currentSession.access_token}`, + `bearer ${recoveredSession.access_token}`, ); }); - - act(() => { - mocks.authCallback?.("SIGNED_OUT", null); - }); - - await waitFor(() => { - expect(mocks.emitTo).toHaveBeenCalledTimes(1); - expect(screen.getByTestId("session").textContent).toBe("none"); - expect(screen.getByTestId("authorization").textContent).toBe("none"); - }); expect(mocks.clearAuthStorage).not.toHaveBeenCalled(); - expect(mocks.signOut).not.toHaveBeenCalled(); - - act(() => { - mocks.eventCallbacks.get("anlg:auth-sign-out-result")?.({ - payload: { requestId: "request-id", completed: false, error: null }, - }); - }); - - await waitFor(() => { - expect(mocks.eventCallbacks.has("anlg:auth-sign-out-result")).toBe(false); - }); - expect(mocks.analyticsClearGroups).not.toHaveBeenCalled(); - expect(mocks.clearAuthStorage).not.toHaveBeenCalled(); - expect(screen.getByTestId("session").textContent).toBe("none"); - - act(() => { - mocks.authCallback?.("SIGNED_OUT", null); - }); - - await waitFor(() => { - expect(mocks.emitTo).toHaveBeenCalledTimes(2); - }); - - act(() => { - mocks.eventCallbacks.get("anlg:auth-sign-out-result")?.({ - payload: { requestId: "request-id", completed: true, error: null }, - }); - }); - - await waitFor(() => { - expect(mocks.clearAuthStorage).toHaveBeenCalledTimes(1); - }); - expect(mocks.signOut).not.toHaveBeenCalled(); - expect(screen.getByTestId("session").textContent).toBe("none"); }); - it("keeps a spontaneous secondary sign-out hidden after rejected coordination and allows recovery", async () => { + it("clears a secondary-window session after committed sign-out", async () => { const currentSession = makeSession("bound-account"); - const recoveredSession = { - ...makeSession("bound-account"), - access_token: "recovered-access-token", - }; mocks.currentWebviewWindowLabel = "note-session-id"; - vi.spyOn(console, "warn").mockImplementation(() => {}); renderAuthProvider(); await waitFor(() => { expect(mocks.authCallback).not.toBeNull(); + expect(mocks.eventCallbacks.has("anlg:auth-sign-out-committed")).toBe( + true, + ); }); act(() => { @@ -823,46 +856,18 @@ describe("AuthProvider", () => { }); act(() => { - mocks.authCallback?.("SIGNED_OUT", null); - }); - - await waitFor(() => { - expect(mocks.emitTo).toHaveBeenCalledTimes(1); - expect(screen.getByTestId("session").textContent).toBe("none"); - expect(screen.getByTestId("authorization").textContent).toBe("none"); - }); - expect(mocks.clearAuthStorage).not.toHaveBeenCalled(); - expect(mocks.signOut).not.toHaveBeenCalled(); - - act(() => { - mocks.eventCallbacks.get("anlg:auth-sign-out-result")?.({ - payload: { - requestId: "request-id", - completed: false, - error: "cloudsync suspension failed", - }, + mocks.eventCallbacks.get("anlg:auth-sign-out-committed")?.({ + payload: { sourceLabel: "main" }, }); }); await waitFor(() => { - expect(mocks.eventCallbacks.has("anlg:auth-sign-out-result")).toBe(false); - }); - expect(mocks.clearAuthStorage).not.toHaveBeenCalled(); - expect(screen.getByTestId("session").textContent).toBe("none"); - - act(() => { - mocks.authCallback?.("TOKEN_REFRESHED", recoveredSession); - }); - - await waitFor(() => { - expect(screen.getByTestId("access-token").textContent).toBe( - recoveredSession.access_token, - ); - expect(screen.getByTestId("authorization").textContent).toBe( - `bearer ${recoveredSession.access_token}`, - ); + expect(screen.getByTestId("session").textContent).toBe("none"); }); - expect(mocks.clearAuthStorage).not.toHaveBeenCalled(); + expect(mocks.stopAutoRefresh).toHaveBeenCalled(); + expect(mocks.signOut).toHaveBeenCalledWith({ scope: "local" }); + expect(mocks.clearAuthStorage).toHaveBeenCalledTimes(1); + expect(mocks.emitTo).not.toHaveBeenCalled(); }); it("keeps the secondary-window session when main sign-out fails", async () => { @@ -1013,6 +1018,9 @@ describe("AuthProvider", () => { expect.any(Function), ); expect(mocks.signOut).toHaveBeenCalledWith({ scope: "local" }); + expect(mocks.emit).toHaveBeenCalledWith("anlg:auth-sign-out-committed", { + sourceLabel: "main", + }); expect( mocks.prepareCloudsyncSignOut.mock.invocationCallOrder[0], ).toBeLessThan(mocks.signOut.mock.invocationCallOrder[0]); @@ -1101,6 +1109,10 @@ describe("AuthProvider", () => { mocks.prepareCloudsyncSignOut.mockRejectedValueOnce( new Error("cloudsync suspension failed"), ); + mocks.getSession.mockResolvedValue({ + data: { session: null }, + error: null, + }); renderAuthProvider(); @@ -1108,10 +1120,6 @@ describe("AuthProvider", () => { expect(mocks.authCallback).not.toBeNull(); }); - act(() => { - mocks.authCallback?.("INITIAL_SESSION", null); - }); - await waitFor(() => { expect(mocks.handleCloudsyncAuthChange).toHaveBeenCalledWith( "INITIAL_SESSION", @@ -1490,7 +1498,7 @@ describe("AuthProvider", () => { ); }); - it("fails closed when the local database account cannot be verified", async () => { + it("preserves local auth when the database account cannot be verified", async () => { const nextSession = makeSession("unverified-account"); mocks.bindCloudsyncAccountForAuth.mockRejectedValue( new Error("database unavailable"), @@ -1508,9 +1516,11 @@ describe("AuthProvider", () => { }); await waitFor(() => { - expect(mocks.clearAuthStorage).toHaveBeenCalledTimes(1); + expect(screen.getByTestId("session").textContent).toBe( + nextSession.user.id, + ); }); - expect(screen.getByTestId("session").textContent).toBe("none"); + expect(mocks.clearAuthStorage).not.toHaveBeenCalled(); expect(mocks.persistAuthSession).not.toHaveBeenCalled(); expect(mocks.handleCloudsyncAuthChange).not.toHaveBeenCalledWith( "SIGNED_IN", @@ -1738,19 +1748,14 @@ describe("AuthProvider", () => { ); }); - it("does not let delayed fatal initial cleanup erase a newer session", async () => { + it("does not let failed initial recovery erase a newer session", async () => { const fatalError = new Error("invalid refresh token"); const initialSession = deferred<{ data: { session: null }; error: Error; }>(); - const clear = deferred(); const newSession = makeSession("new-account"); mocks.getSession.mockReturnValue(initialSession.promise); - mocks.isFatalSessionError.mockImplementation( - (error: unknown) => error === fatalError, - ); - mocks.clearAuthStorage.mockReturnValue(clear.promise); renderAuthProvider(); @@ -1766,42 +1771,34 @@ describe("AuthProvider", () => { await initialSession.promise; }); - await waitFor(() => { - expect(mocks.clearAuthStorage).toHaveBeenCalledTimes(1); - }); + expect(mocks.clearAuthStorage).not.toHaveBeenCalled(); act(() => { mocks.authCallback?.("SIGNED_IN", newSession); }); - await act(async () => { - clear.resolve(); - await clear.promise; - }); - await waitFor(() => { expect(screen.getByTestId("session").textContent).toBe( newSession.user.id, ); }); - expect(mocks.persistAuthSession).toHaveBeenCalledWith(newSession); + expect(mocks.persistAuthSession).not.toHaveBeenCalled(); expect(mocks.handleCloudsyncAuthChange).not.toHaveBeenCalledWith( "SIGNED_OUT", null, ); }); - it("clears fatal initial storage after the auth subscription initializes", async () => { + it("restores stored auth when initial token refresh fails", async () => { const fatalError = new Error("invalid refresh token"); const initialSession = deferred<{ data: { session: null }; error: Error; }>(); + const storedSession = makeSession("stored-account"); mocks.getSession.mockReturnValue(initialSession.promise); - mocks.isFatalSessionError.mockImplementation( - (error: unknown) => error === fatalError, - ); + mocks.readPersistedAuthSession.mockResolvedValue(storedSession); renderAuthProvider(); @@ -1809,10 +1806,6 @@ describe("AuthProvider", () => { expect(mocks.authCallback).not.toBeNull(); }); - act(() => { - mocks.authCallback?.("INITIAL_SESSION", null); - }); - await act(async () => { initialSession.resolve({ data: { session: null }, @@ -1822,12 +1815,17 @@ describe("AuthProvider", () => { }); await waitFor(() => { - expect(mocks.clearAuthStorage).toHaveBeenCalledTimes(1); + expect(screen.getByTestId("session").textContent).toBe( + storedSession.user.id, + ); }); + expect(mocks.clearAuthStorage).not.toHaveBeenCalled(); + expect(mocks.persistAuthSession).not.toHaveBeenCalled(); expect(mocks.handleCloudsyncAuthChange).toHaveBeenCalledWith( - "SIGNED_OUT", - null, + "INITIAL_SESSION", + storedSession, + expect.any(Function), ); }); diff --git a/apps/desktop/src/auth/context.tsx b/apps/desktop/src/auth/context.tsx index 8bd2d08a210..c197a6cba97 100644 --- a/apps/desktop/src/auth/context.tsx +++ b/apps/desktop/src/auth/context.tsx @@ -6,7 +6,7 @@ import { type Session, } from "@supabase/supabase-js"; import { useMutation } from "@tanstack/react-query"; -import { emitTo, listen } from "@tauri-apps/api/event"; +import { emit, emitTo, listen } from "@tauri-apps/api/event"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; import { getCurrentWindow } from "@tauri-apps/api/window"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; @@ -33,11 +33,14 @@ import { import { clearAuthStorage } from "./errors"; import { loadInitialSession } from "./initial-session"; import { + AUTH_SIGN_OUT_COMMITTED_EVENT, AUTH_SIGN_OUT_REQUEST_EVENT, AUTH_SIGN_OUT_RESULT_EVENT, + type AuthSignOutCommittedPayload, type AuthSignOutRequestPayload, type AuthSignOutResultPayload, getErrorMessage, + isAuthSignOutCommittedPayload, isAuthSignOutRequestPayload, requestMainSignOut, } from "./sign-out-coordination"; @@ -63,7 +66,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const initStartedRef = useRef(false); const authTransitionRef = useRef(0); const authTransitionEventRef = useRef(null); - const nonInitialAuthTransitionRef = useRef(0); const authTransitionQueueRef = useRef(Promise.resolve()); const authAnalyticsQueueRef = useRef(Promise.resolve()); const authStorageRevisionRef = useRef(0); @@ -222,8 +224,18 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { { id: ACCOUNT_MISMATCH_TOAST_ID }, ); await rejectAuthChange(transition, true); + + if (managesCloudsync && transition === authTransitionRef.current) { + try { + await emit(AUTH_SIGN_OUT_COMMITTED_EVENT, { + sourceLabel: currentWindowLabel, + } satisfies AuthSignOutCommittedPayload); + } catch { + console.warn("[auth] account rejection could not be synchronized"); + } + } }, - [rejectAuthChange], + [currentWindowLabel, managesCloudsync, rejectAuthChange], ); const applyAuthChange = useCallback( @@ -232,13 +244,12 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { nextSession: Session | null, transition: number, storageRevision: number, - clearStorage: boolean, ) => { if (transition !== authTransitionRef.current) { return; } - if (clearStorage || event === "SIGNED_OUT") { + if (event === "SIGNED_OUT") { let mainSignOutCompleted = false; if (event === "SIGNED_OUT" && !managesCloudsync) { resetTrackedAuthIdentity(); @@ -259,11 +270,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { } } - await rejectAuthChange( - transition, - clearStorage && event !== "SIGNED_OUT", - mainSignOutCompleted, - ); + await rejectAuthChange(transition, false, mainSignOutCompleted); return; } @@ -289,8 +296,11 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { if (transition !== authTransitionRef.current) { return; } - console.warn("[auth] local database account verification failed"); - await rejectAuthChange(transition, true); + console.warn( + "[auth] local database account verification failed; preserving the local session", + ); + setSession(nextSession); + void enqueueAuthAnalytics(() => trackAuthEvent(event, nextSession)); return; } } @@ -352,11 +362,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { ); const enqueueAuthChange = useCallback( - ( - event: AuthChangeEvent, - nextSession: Session | null, - clearStorage = false, - ) => { + (event: AuthChangeEvent, nextSession: Session | null) => { if (event !== "SIGNED_OUT") { coordinatedMainSignOutRef.current = null; } @@ -364,13 +370,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const transition = ++authTransitionRef.current; const storageRevision = authStorageRevisionRef.current; const apply = () => - applyAuthChange( - event, - nextSession, - transition, - storageRevision, - clearStorage, - ); + applyAuthChange(event, nextSession, transition, storageRevision); const queued = event === "SIGNED_OUT" ? Promise.resolve().then(apply) @@ -389,19 +389,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { if (!initStartedRef.current) { initStartedRef.current = true; const initialTransition = authTransitionRef.current; - const initialNonInitialTransition = nonInitialAuthTransitionRef.current; - void loadInitialSession(supabase).then((initial) => { - if (initial.clearStorage) { - if ( - initialNonInitialTransition === nonInitialAuthTransitionRef.current - ) { - void enqueueAuthChange("INITIAL_SESSION", null, true); - } - return; - } - + void loadInitialSession(supabase).then((initialSession) => { if (initialTransition === authTransitionRef.current) { - void enqueueAuthChange("INITIAL_SESSION", initial.session); + void enqueueAuthChange("INITIAL_SESSION", initialSession); } }); } @@ -409,9 +399,14 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const { data: { subscription }, } = supabase.auth.onAuthStateChange((event, session) => { - if (event !== "INITIAL_SESSION") { - nonInitialAuthTransitionRef.current += 1; + if (event === "INITIAL_SESSION") { + return; } + if (event === "SIGNED_OUT") { + console.warn("[auth] ignoring unsolicited SDK sign-out"); + return; + } + console.log( `[auth] onAuthStateChange: ${event}`, session ? `expires_at=${session.expires_at}` : "no session", @@ -619,8 +614,19 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { } await enqueueAuthChange("SIGNED_OUT", null); + if (authTransitionEventRef.current !== "SIGNED_OUT") { + return false; + } + + try { + await emit(AUTH_SIGN_OUT_COMMITTED_EVENT, { + sourceLabel: currentWindowLabel, + } satisfies AuthSignOutCommittedPayload); + } catch { + console.warn("[auth] sign-out could not be synchronized"); + } return true; - }, [enqueueAuthChange, rejectAccountMismatch, session]); + }, [currentWindowLabel, enqueueAuthChange, rejectAccountMismatch, session]); const signOutFromMainRef = useLatestRef(signOutFromMain); useMountEffect(() => { @@ -677,6 +683,43 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { }; }); + useMountEffect(() => { + let active = true; + let unlisten: (() => void) | null = null; + + void listen( + AUTH_SIGN_OUT_COMMITTED_EVENT, + (event) => { + if ( + !active || + !isAuthSignOutCommittedPayload(event.payload) || + event.payload.sourceLabel === currentWindowLabel + ) { + return; + } + + authTransitionEventRef.current = "SIGNED_OUT"; + const transition = ++authTransitionRef.current; + void rejectAuthChange(transition, true, true); + }, + ) + .then((fn) => { + if (active) { + unlisten = fn; + } else { + fn(); + } + }) + .catch(() => { + console.warn("[auth] sign-out synchronization failed to initialize"); + }); + + return () => { + active = false; + unlisten?.(); + }; + }); + const signOut = useCallback(async () => { if (managesCloudsync) { await signOutFromMain(); @@ -715,6 +758,46 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { [refreshSessionMutation.mutateAsync], ); + const getSessionForRequest = + useCallback(async (): Promise => { + if (!supabase) { + return null; + } + + let requestSession = session ?? null; + try { + const { data, error } = await supabase.auth.getSession(); + if (!error && data.session) { + requestSession = data.session; + } + } catch { + // Fall back to the in-memory session below. + } + + if (!requestSession) { + return null; + } + + const expiresAt = requestSession.expires_at + ? requestSession.expires_at * 1000 + : null; + if (!expiresAt || expiresAt > Date.now() + 120_000) { + return requestSession; + } + + let refreshedSession: Session | null = null; + try { + refreshedSession = await refreshSession(); + } catch { + // Fall back to the current session while it remains valid. + } + if (refreshedSession) { + return refreshedSession; + } + + return expiresAt > Date.now() ? requestSession : null; + }, [refreshSession, session]); + const getHeaders = useCallback(() => { if (!session) { return null; @@ -761,6 +844,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { signIn, signOut, refreshSession, + getSessionForRequest, isRefreshingSession: refreshSessionMutation.isPending, handleAuthCallback, setSessionFromTokens, @@ -772,6 +856,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { signIn, signOut, refreshSession, + getSessionForRequest, refreshSessionMutation.isPending, handleAuthCallback, setSessionFromTokens, diff --git a/apps/desktop/src/auth/errors.ts b/apps/desktop/src/auth/errors.ts index 43891db4caf..6957d5df5c4 100644 --- a/apps/desktop/src/auth/errors.ts +++ b/apps/desktop/src/auth/errors.ts @@ -1,21 +1,5 @@ -import { AuthApiError, AuthSessionMissingError } from "@supabase/supabase-js"; - import { commands as authCommands } from "@anlg/plugin-auth"; -export const isFatalSessionError = (error: unknown): boolean => { - if (error instanceof AuthSessionMissingError) { - return true; - } - if (error instanceof AuthApiError) { - const fatalCodes = [ - "refresh_token_not_found", - "refresh_token_already_used", - ]; - return fatalCodes.includes(error.code ?? ""); - } - return false; -}; - export const clearAuthStorage = async (): Promise => { try { await authCommands.clear(); diff --git a/apps/desktop/src/auth/initial-session.test.ts b/apps/desktop/src/auth/initial-session.test.ts new file mode 100644 index 00000000000..89c7e6f5a87 --- /dev/null +++ b/apps/desktop/src/auth/initial-session.test.ts @@ -0,0 +1,54 @@ +import type { Session, SupabaseClient } from "@supabase/supabase-js"; +import { beforeEach, describe, expect, test, vi } from "vitest"; + +import { loadInitialSession } from "./initial-session"; + +const { readPersistedAuthSessionMock } = vi.hoisted(() => ({ + readPersistedAuthSessionMock: vi.fn(), +})); + +vi.mock("./client", () => ({ + readPersistedAuthSession: readPersistedAuthSessionMock, +})); + +const storedSession = { access_token: "stored" } as Session; +const refreshedSession = { access_token: "refreshed" } as Session; + +function makeClient(getSession: () => unknown) { + return { + auth: { getSession }, + } as unknown as SupabaseClient; +} + +describe("loadInitialSession", () => { + beforeEach(() => { + readPersistedAuthSessionMock.mockResolvedValue(storedSession); + }); + + test("uses the refreshed session when recovery succeeds", async () => { + const client = makeClient(async () => ({ + data: { session: refreshedSession }, + error: null, + })); + + await expect(loadInitialSession(client)).resolves.toBe(refreshedSession); + }); + + test("keeps the stored session when refresh fails", async () => { + const client = makeClient(async () => ({ + data: { session: null }, + error: new Error("refresh token unavailable"), + })); + + await expect(loadInitialSession(client)).resolves.toBe(storedSession); + }); + + test("keeps the stored session when the SDK returns no session", async () => { + const client = makeClient(async () => ({ + data: { session: null }, + error: null, + })); + + await expect(loadInitialSession(client)).resolves.toBe(storedSession); + }); +}); diff --git a/apps/desktop/src/auth/initial-session.ts b/apps/desktop/src/auth/initial-session.ts index 5ac134f6985..b6aecb5d303 100644 --- a/apps/desktop/src/auth/initial-session.ts +++ b/apps/desktop/src/auth/initial-session.ts @@ -1,28 +1,21 @@ import type { Session, SupabaseClient } from "@supabase/supabase-js"; -import { isFatalSessionError } from "./errors"; +import { readPersistedAuthSession } from "./client"; export async function loadInitialSession( client: SupabaseClient, -): Promise<{ clearStorage: boolean; session: Session | null }> { +): Promise { + const storedSession = await readPersistedAuthSession(); + try { const { data, error } = await client.auth.getSession(); if (error) { - return { - clearStorage: isFatalSessionError(error), - session: null, - }; + return storedSession; } - return { - clearStorage: false, - session: data.session ?? null, - }; - } catch (error) { - return { - clearStorage: isFatalSessionError(error), - session: null, - }; + return data.session ?? storedSession; + } catch { + return storedSession; } } diff --git a/apps/desktop/src/auth/sign-out-coordination.ts b/apps/desktop/src/auth/sign-out-coordination.ts index 8941396f7f0..6a26ce5d5b8 100644 --- a/apps/desktop/src/auth/sign-out-coordination.ts +++ b/apps/desktop/src/auth/sign-out-coordination.ts @@ -4,6 +4,7 @@ import { id } from "~/shared/utils"; export const AUTH_SIGN_OUT_REQUEST_EVENT = "anlg:auth-sign-out-request"; export const AUTH_SIGN_OUT_RESULT_EVENT = "anlg:auth-sign-out-result"; +export const AUTH_SIGN_OUT_COMMITTED_EVENT = "anlg:auth-sign-out-committed"; const AUTH_SIGN_OUT_TIMEOUT_MS = 10_000; export type AuthSignOutRequestPayload = { @@ -17,6 +18,24 @@ export type AuthSignOutResultPayload = { error: string | null; }; +export type AuthSignOutCommittedPayload = { + sourceLabel: string; +}; + +export function isAuthSignOutCommittedPayload( + payload: unknown, +): payload is AuthSignOutCommittedPayload { + if (!payload || typeof payload !== "object") { + return false; + } + + const candidate = payload as Partial; + return ( + typeof candidate.sourceLabel === "string" && + candidate.sourceLabel.length > 0 + ); +} + export function isAuthSignOutRequestPayload( payload: unknown, ): payload is AuthSignOutRequestPayload { diff --git a/apps/desktop/src/i18n/locales/af/messages.po b/apps/desktop/src/i18n/locales/af/messages.po index a2ca8eee225..d3bf6a8c569 100644 --- a/apps/desktop/src/i18n/locales/af/messages.po +++ b/apps/desktop/src/i18n/locales/af/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/am/messages.po b/apps/desktop/src/i18n/locales/am/messages.po index 3c862d3db24..2809b5a2291 100644 --- a/apps/desktop/src/i18n/locales/am/messages.po +++ b/apps/desktop/src/i18n/locales/am/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ar/messages.po b/apps/desktop/src/i18n/locales/ar/messages.po index 855dfb5bc3f..40344a62c94 100644 --- a/apps/desktop/src/i18n/locales/ar/messages.po +++ b/apps/desktop/src/i18n/locales/ar/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/as/messages.po b/apps/desktop/src/i18n/locales/as/messages.po index 020ded06939..56f72ab69fc 100644 --- a/apps/desktop/src/i18n/locales/as/messages.po +++ b/apps/desktop/src/i18n/locales/as/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/az/messages.po b/apps/desktop/src/i18n/locales/az/messages.po index d6e80ddaad4..d8481bb2578 100644 --- a/apps/desktop/src/i18n/locales/az/messages.po +++ b/apps/desktop/src/i18n/locales/az/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ba/messages.po b/apps/desktop/src/i18n/locales/ba/messages.po index d6b240fee35..e54a06fbb23 100644 --- a/apps/desktop/src/i18n/locales/ba/messages.po +++ b/apps/desktop/src/i18n/locales/ba/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/be/messages.po b/apps/desktop/src/i18n/locales/be/messages.po index 61efe839193..4e94e98dcfe 100644 --- a/apps/desktop/src/i18n/locales/be/messages.po +++ b/apps/desktop/src/i18n/locales/be/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/bg/messages.po b/apps/desktop/src/i18n/locales/bg/messages.po index eb949fab907..4fca112d3df 100644 --- a/apps/desktop/src/i18n/locales/bg/messages.po +++ b/apps/desktop/src/i18n/locales/bg/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/bn/messages.po b/apps/desktop/src/i18n/locales/bn/messages.po index d68a15248cb..be74fd8b84c 100644 --- a/apps/desktop/src/i18n/locales/bn/messages.po +++ b/apps/desktop/src/i18n/locales/bn/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/bo/messages.po b/apps/desktop/src/i18n/locales/bo/messages.po index 6cb784d5834..9fa3d577cb4 100644 --- a/apps/desktop/src/i18n/locales/bo/messages.po +++ b/apps/desktop/src/i18n/locales/bo/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/br/messages.po b/apps/desktop/src/i18n/locales/br/messages.po index 1f52fcc92ef..543745505c8 100644 --- a/apps/desktop/src/i18n/locales/br/messages.po +++ b/apps/desktop/src/i18n/locales/br/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/bs/messages.po b/apps/desktop/src/i18n/locales/bs/messages.po index ec4af155e8a..2c896bf8a0d 100644 --- a/apps/desktop/src/i18n/locales/bs/messages.po +++ b/apps/desktop/src/i18n/locales/bs/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ca/messages.po b/apps/desktop/src/i18n/locales/ca/messages.po index 0faf922d6a1..cbea46e4c72 100644 --- a/apps/desktop/src/i18n/locales/ca/messages.po +++ b/apps/desktop/src/i18n/locales/ca/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/cs/messages.po b/apps/desktop/src/i18n/locales/cs/messages.po index ed2859b69de..53e3cf8ccfe 100644 --- a/apps/desktop/src/i18n/locales/cs/messages.po +++ b/apps/desktop/src/i18n/locales/cs/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/cy/messages.po b/apps/desktop/src/i18n/locales/cy/messages.po index 4b7c1961d19..20852d2d937 100644 --- a/apps/desktop/src/i18n/locales/cy/messages.po +++ b/apps/desktop/src/i18n/locales/cy/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/da/messages.po b/apps/desktop/src/i18n/locales/da/messages.po index f2ca8a7d424..94c68c2fc6b 100644 --- a/apps/desktop/src/i18n/locales/da/messages.po +++ b/apps/desktop/src/i18n/locales/da/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/de/messages.po b/apps/desktop/src/i18n/locales/de/messages.po index 6767d591227..be41974207f 100644 --- a/apps/desktop/src/i18n/locales/de/messages.po +++ b/apps/desktop/src/i18n/locales/de/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/el/messages.po b/apps/desktop/src/i18n/locales/el/messages.po index 23926defd4a..8d545ae31b0 100644 --- a/apps/desktop/src/i18n/locales/el/messages.po +++ b/apps/desktop/src/i18n/locales/el/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/en/messages.po b/apps/desktop/src/i18n/locales/en/messages.po index 449d2649cce..eb2f13127cb 100644 --- a/apps/desktop/src/i18n/locales/en/messages.po +++ b/apps/desktop/src/i18n/locales/en/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "Transcription complete" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "Transcription failed" diff --git a/apps/desktop/src/i18n/locales/es/messages.po b/apps/desktop/src/i18n/locales/es/messages.po index 969efc58de6..3511de7e57f 100644 --- a/apps/desktop/src/i18n/locales/es/messages.po +++ b/apps/desktop/src/i18n/locales/es/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/et/messages.po b/apps/desktop/src/i18n/locales/et/messages.po index b650b05505e..6f3fa49f73c 100644 --- a/apps/desktop/src/i18n/locales/et/messages.po +++ b/apps/desktop/src/i18n/locales/et/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/eu/messages.po b/apps/desktop/src/i18n/locales/eu/messages.po index eb6b3f9b8fc..da5402b754c 100644 --- a/apps/desktop/src/i18n/locales/eu/messages.po +++ b/apps/desktop/src/i18n/locales/eu/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/fa/messages.po b/apps/desktop/src/i18n/locales/fa/messages.po index 23e26a28732..8b614d83503 100644 --- a/apps/desktop/src/i18n/locales/fa/messages.po +++ b/apps/desktop/src/i18n/locales/fa/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ff/messages.po b/apps/desktop/src/i18n/locales/ff/messages.po index f815a3d77dd..f7405c41d34 100644 --- a/apps/desktop/src/i18n/locales/ff/messages.po +++ b/apps/desktop/src/i18n/locales/ff/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/fi/messages.po b/apps/desktop/src/i18n/locales/fi/messages.po index a1d003448ec..b5af3cc3d48 100644 --- a/apps/desktop/src/i18n/locales/fi/messages.po +++ b/apps/desktop/src/i18n/locales/fi/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/fo/messages.po b/apps/desktop/src/i18n/locales/fo/messages.po index 40137436d97..b7f72bc07c6 100644 --- a/apps/desktop/src/i18n/locales/fo/messages.po +++ b/apps/desktop/src/i18n/locales/fo/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/fr/messages.po b/apps/desktop/src/i18n/locales/fr/messages.po index 97f21706e16..d5ff6228d10 100644 --- a/apps/desktop/src/i18n/locales/fr/messages.po +++ b/apps/desktop/src/i18n/locales/fr/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ga/messages.po b/apps/desktop/src/i18n/locales/ga/messages.po index 41bb167ebc5..1fdaf83d001 100644 --- a/apps/desktop/src/i18n/locales/ga/messages.po +++ b/apps/desktop/src/i18n/locales/ga/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/gl/messages.po b/apps/desktop/src/i18n/locales/gl/messages.po index 1e5991bc05b..f2f672f6ad5 100644 --- a/apps/desktop/src/i18n/locales/gl/messages.po +++ b/apps/desktop/src/i18n/locales/gl/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/gu/messages.po b/apps/desktop/src/i18n/locales/gu/messages.po index 12f452cf872..ff16dd57210 100644 --- a/apps/desktop/src/i18n/locales/gu/messages.po +++ b/apps/desktop/src/i18n/locales/gu/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ha/messages.po b/apps/desktop/src/i18n/locales/ha/messages.po index 7240e1176a6..1eabcf44bbd 100644 --- a/apps/desktop/src/i18n/locales/ha/messages.po +++ b/apps/desktop/src/i18n/locales/ha/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/he/messages.po b/apps/desktop/src/i18n/locales/he/messages.po index 9fd4a197f58..3170a5be2ba 100644 --- a/apps/desktop/src/i18n/locales/he/messages.po +++ b/apps/desktop/src/i18n/locales/he/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/hi/messages.po b/apps/desktop/src/i18n/locales/hi/messages.po index 53c13ad9e78..c67f10b6dde 100644 --- a/apps/desktop/src/i18n/locales/hi/messages.po +++ b/apps/desktop/src/i18n/locales/hi/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/hr/messages.po b/apps/desktop/src/i18n/locales/hr/messages.po index 849abc7aba8..94b41e3adc2 100644 --- a/apps/desktop/src/i18n/locales/hr/messages.po +++ b/apps/desktop/src/i18n/locales/hr/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ht/messages.po b/apps/desktop/src/i18n/locales/ht/messages.po index 9eadc5558f8..9b04eb86f27 100644 --- a/apps/desktop/src/i18n/locales/ht/messages.po +++ b/apps/desktop/src/i18n/locales/ht/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/hu/messages.po b/apps/desktop/src/i18n/locales/hu/messages.po index b5ad1b470f1..02e7ecdd961 100644 --- a/apps/desktop/src/i18n/locales/hu/messages.po +++ b/apps/desktop/src/i18n/locales/hu/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/hy/messages.po b/apps/desktop/src/i18n/locales/hy/messages.po index f82e3d3b6ec..f116227f845 100644 --- a/apps/desktop/src/i18n/locales/hy/messages.po +++ b/apps/desktop/src/i18n/locales/hy/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/id/messages.po b/apps/desktop/src/i18n/locales/id/messages.po index 9da1fae58d6..724083d6afe 100644 --- a/apps/desktop/src/i18n/locales/id/messages.po +++ b/apps/desktop/src/i18n/locales/id/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ig/messages.po b/apps/desktop/src/i18n/locales/ig/messages.po index cc08c38eb4b..4e583810ae3 100644 --- a/apps/desktop/src/i18n/locales/ig/messages.po +++ b/apps/desktop/src/i18n/locales/ig/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/is/messages.po b/apps/desktop/src/i18n/locales/is/messages.po index 49a8c947bc2..9f72d7d40f4 100644 --- a/apps/desktop/src/i18n/locales/is/messages.po +++ b/apps/desktop/src/i18n/locales/is/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/it/messages.po b/apps/desktop/src/i18n/locales/it/messages.po index 4f6e15480b5..a222269d1be 100644 --- a/apps/desktop/src/i18n/locales/it/messages.po +++ b/apps/desktop/src/i18n/locales/it/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ja/messages.po b/apps/desktop/src/i18n/locales/ja/messages.po index d31f243f499..ad4f5366876 100644 --- a/apps/desktop/src/i18n/locales/ja/messages.po +++ b/apps/desktop/src/i18n/locales/ja/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/jv/messages.po b/apps/desktop/src/i18n/locales/jv/messages.po index b850f34781f..8d5da38b3c8 100644 --- a/apps/desktop/src/i18n/locales/jv/messages.po +++ b/apps/desktop/src/i18n/locales/jv/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ka/messages.po b/apps/desktop/src/i18n/locales/ka/messages.po index 790c2753580..caccdd57d84 100644 --- a/apps/desktop/src/i18n/locales/ka/messages.po +++ b/apps/desktop/src/i18n/locales/ka/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/kk/messages.po b/apps/desktop/src/i18n/locales/kk/messages.po index fbb9cf53c77..543cca13a67 100644 --- a/apps/desktop/src/i18n/locales/kk/messages.po +++ b/apps/desktop/src/i18n/locales/kk/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/km/messages.po b/apps/desktop/src/i18n/locales/km/messages.po index cba60b31f36..5d17aa99bad 100644 --- a/apps/desktop/src/i18n/locales/km/messages.po +++ b/apps/desktop/src/i18n/locales/km/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/kn/messages.po b/apps/desktop/src/i18n/locales/kn/messages.po index 7c5cf3d6615..e2d4ed01908 100644 --- a/apps/desktop/src/i18n/locales/kn/messages.po +++ b/apps/desktop/src/i18n/locales/kn/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ko/messages.po b/apps/desktop/src/i18n/locales/ko/messages.po index 61f303d93ca..8c9e35d816d 100644 --- a/apps/desktop/src/i18n/locales/ko/messages.po +++ b/apps/desktop/src/i18n/locales/ko/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ku/messages.po b/apps/desktop/src/i18n/locales/ku/messages.po index 33741f47b15..c6b44fb93a5 100644 --- a/apps/desktop/src/i18n/locales/ku/messages.po +++ b/apps/desktop/src/i18n/locales/ku/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ky/messages.po b/apps/desktop/src/i18n/locales/ky/messages.po index 83c9e861a25..6c4a7b4c506 100644 --- a/apps/desktop/src/i18n/locales/ky/messages.po +++ b/apps/desktop/src/i18n/locales/ky/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/la/messages.po b/apps/desktop/src/i18n/locales/la/messages.po index d5085b1cdd3..9942c09f232 100644 --- a/apps/desktop/src/i18n/locales/la/messages.po +++ b/apps/desktop/src/i18n/locales/la/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/lb/messages.po b/apps/desktop/src/i18n/locales/lb/messages.po index 2ef605cf9e1..a37186d1d62 100644 --- a/apps/desktop/src/i18n/locales/lb/messages.po +++ b/apps/desktop/src/i18n/locales/lb/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/lg/messages.po b/apps/desktop/src/i18n/locales/lg/messages.po index 0f975ed8ffd..875602f3cbd 100644 --- a/apps/desktop/src/i18n/locales/lg/messages.po +++ b/apps/desktop/src/i18n/locales/lg/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ln/messages.po b/apps/desktop/src/i18n/locales/ln/messages.po index bd67e732c0f..142cbc2f042 100644 --- a/apps/desktop/src/i18n/locales/ln/messages.po +++ b/apps/desktop/src/i18n/locales/ln/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/lo/messages.po b/apps/desktop/src/i18n/locales/lo/messages.po index 9fc7ee4feed..4466a1e8e21 100644 --- a/apps/desktop/src/i18n/locales/lo/messages.po +++ b/apps/desktop/src/i18n/locales/lo/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/lt/messages.po b/apps/desktop/src/i18n/locales/lt/messages.po index 2f47591a070..36479dca5bd 100644 --- a/apps/desktop/src/i18n/locales/lt/messages.po +++ b/apps/desktop/src/i18n/locales/lt/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/lv/messages.po b/apps/desktop/src/i18n/locales/lv/messages.po index 3b174e3a289..4284419af9c 100644 --- a/apps/desktop/src/i18n/locales/lv/messages.po +++ b/apps/desktop/src/i18n/locales/lv/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/mg/messages.po b/apps/desktop/src/i18n/locales/mg/messages.po index a0a6def692c..f9de66218bd 100644 --- a/apps/desktop/src/i18n/locales/mg/messages.po +++ b/apps/desktop/src/i18n/locales/mg/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/mi/messages.po b/apps/desktop/src/i18n/locales/mi/messages.po index 20aff29ed7a..2d81b6edf56 100644 --- a/apps/desktop/src/i18n/locales/mi/messages.po +++ b/apps/desktop/src/i18n/locales/mi/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/mk/messages.po b/apps/desktop/src/i18n/locales/mk/messages.po index c4beddd47a4..9835de4c87d 100644 --- a/apps/desktop/src/i18n/locales/mk/messages.po +++ b/apps/desktop/src/i18n/locales/mk/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ml/messages.po b/apps/desktop/src/i18n/locales/ml/messages.po index bb513ae5500..1463a7c2e37 100644 --- a/apps/desktop/src/i18n/locales/ml/messages.po +++ b/apps/desktop/src/i18n/locales/ml/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/mn/messages.po b/apps/desktop/src/i18n/locales/mn/messages.po index e6a069b42f6..8cf57c52041 100644 --- a/apps/desktop/src/i18n/locales/mn/messages.po +++ b/apps/desktop/src/i18n/locales/mn/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/mr/messages.po b/apps/desktop/src/i18n/locales/mr/messages.po index 21105dbabaa..db35d3282fd 100644 --- a/apps/desktop/src/i18n/locales/mr/messages.po +++ b/apps/desktop/src/i18n/locales/mr/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ms/messages.po b/apps/desktop/src/i18n/locales/ms/messages.po index 31ff3788eb3..67e4df1eaa2 100644 --- a/apps/desktop/src/i18n/locales/ms/messages.po +++ b/apps/desktop/src/i18n/locales/ms/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/mt/messages.po b/apps/desktop/src/i18n/locales/mt/messages.po index ca781651e9c..f3aa2e49e4b 100644 --- a/apps/desktop/src/i18n/locales/mt/messages.po +++ b/apps/desktop/src/i18n/locales/mt/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/my/messages.po b/apps/desktop/src/i18n/locales/my/messages.po index 80615011b8f..57d5938e039 100644 --- a/apps/desktop/src/i18n/locales/my/messages.po +++ b/apps/desktop/src/i18n/locales/my/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ne/messages.po b/apps/desktop/src/i18n/locales/ne/messages.po index bacc69ee713..90b38707a0a 100644 --- a/apps/desktop/src/i18n/locales/ne/messages.po +++ b/apps/desktop/src/i18n/locales/ne/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/nl/messages.po b/apps/desktop/src/i18n/locales/nl/messages.po index 1ddf06766e1..440f855b76c 100644 --- a/apps/desktop/src/i18n/locales/nl/messages.po +++ b/apps/desktop/src/i18n/locales/nl/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/nn/messages.po b/apps/desktop/src/i18n/locales/nn/messages.po index 6a41f0f4ec4..ec819df007f 100644 --- a/apps/desktop/src/i18n/locales/nn/messages.po +++ b/apps/desktop/src/i18n/locales/nn/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/no/messages.po b/apps/desktop/src/i18n/locales/no/messages.po index 0b39589e4d7..db746ebc461 100644 --- a/apps/desktop/src/i18n/locales/no/messages.po +++ b/apps/desktop/src/i18n/locales/no/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ny/messages.po b/apps/desktop/src/i18n/locales/ny/messages.po index 27062f426c5..1a86eeb6f87 100644 --- a/apps/desktop/src/i18n/locales/ny/messages.po +++ b/apps/desktop/src/i18n/locales/ny/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/oc/messages.po b/apps/desktop/src/i18n/locales/oc/messages.po index 72ab33b2dea..ba0fb26d08a 100644 --- a/apps/desktop/src/i18n/locales/oc/messages.po +++ b/apps/desktop/src/i18n/locales/oc/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/or/messages.po b/apps/desktop/src/i18n/locales/or/messages.po index 36b84a6cc94..4d6168b20c0 100644 --- a/apps/desktop/src/i18n/locales/or/messages.po +++ b/apps/desktop/src/i18n/locales/or/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/pa/messages.po b/apps/desktop/src/i18n/locales/pa/messages.po index c737899962a..41cc10c250e 100644 --- a/apps/desktop/src/i18n/locales/pa/messages.po +++ b/apps/desktop/src/i18n/locales/pa/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/pl/messages.po b/apps/desktop/src/i18n/locales/pl/messages.po index 47fdafa47a1..ee2a4dcd5be 100644 --- a/apps/desktop/src/i18n/locales/pl/messages.po +++ b/apps/desktop/src/i18n/locales/pl/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ps/messages.po b/apps/desktop/src/i18n/locales/ps/messages.po index 59ef49b863a..0435e269d9d 100644 --- a/apps/desktop/src/i18n/locales/ps/messages.po +++ b/apps/desktop/src/i18n/locales/ps/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/pt/messages.po b/apps/desktop/src/i18n/locales/pt/messages.po index 8f4b33b0b0e..9b3ca8c8436 100644 --- a/apps/desktop/src/i18n/locales/pt/messages.po +++ b/apps/desktop/src/i18n/locales/pt/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ro/messages.po b/apps/desktop/src/i18n/locales/ro/messages.po index 614287ef037..1e76b581875 100644 --- a/apps/desktop/src/i18n/locales/ro/messages.po +++ b/apps/desktop/src/i18n/locales/ro/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ru/messages.po b/apps/desktop/src/i18n/locales/ru/messages.po index 25bfbbcec5c..9dc822433f7 100644 --- a/apps/desktop/src/i18n/locales/ru/messages.po +++ b/apps/desktop/src/i18n/locales/ru/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/sa/messages.po b/apps/desktop/src/i18n/locales/sa/messages.po index acd605b5fed..9fc55f98a16 100644 --- a/apps/desktop/src/i18n/locales/sa/messages.po +++ b/apps/desktop/src/i18n/locales/sa/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/sd/messages.po b/apps/desktop/src/i18n/locales/sd/messages.po index 3a1d1811129..16db6051a55 100644 --- a/apps/desktop/src/i18n/locales/sd/messages.po +++ b/apps/desktop/src/i18n/locales/sd/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/si/messages.po b/apps/desktop/src/i18n/locales/si/messages.po index 144eec4fc41..d12e979130f 100644 --- a/apps/desktop/src/i18n/locales/si/messages.po +++ b/apps/desktop/src/i18n/locales/si/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/sk/messages.po b/apps/desktop/src/i18n/locales/sk/messages.po index 83c23a90226..c68a1f77854 100644 --- a/apps/desktop/src/i18n/locales/sk/messages.po +++ b/apps/desktop/src/i18n/locales/sk/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/sl/messages.po b/apps/desktop/src/i18n/locales/sl/messages.po index 74a14f74b54..a0fe74f0f2e 100644 --- a/apps/desktop/src/i18n/locales/sl/messages.po +++ b/apps/desktop/src/i18n/locales/sl/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/sn/messages.po b/apps/desktop/src/i18n/locales/sn/messages.po index c1f6af15bd7..8d70cd74004 100644 --- a/apps/desktop/src/i18n/locales/sn/messages.po +++ b/apps/desktop/src/i18n/locales/sn/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/so/messages.po b/apps/desktop/src/i18n/locales/so/messages.po index 35db79ec26f..acb9d397561 100644 --- a/apps/desktop/src/i18n/locales/so/messages.po +++ b/apps/desktop/src/i18n/locales/so/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/sq/messages.po b/apps/desktop/src/i18n/locales/sq/messages.po index 7a1ba86f5a5..ac808e6f0c5 100644 --- a/apps/desktop/src/i18n/locales/sq/messages.po +++ b/apps/desktop/src/i18n/locales/sq/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/sr/messages.po b/apps/desktop/src/i18n/locales/sr/messages.po index 455e214f864..9b52c6e463c 100644 --- a/apps/desktop/src/i18n/locales/sr/messages.po +++ b/apps/desktop/src/i18n/locales/sr/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/su/messages.po b/apps/desktop/src/i18n/locales/su/messages.po index f9f70a18811..f9f76481113 100644 --- a/apps/desktop/src/i18n/locales/su/messages.po +++ b/apps/desktop/src/i18n/locales/su/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/sv/messages.po b/apps/desktop/src/i18n/locales/sv/messages.po index a7d87e00b3a..532c2face9d 100644 --- a/apps/desktop/src/i18n/locales/sv/messages.po +++ b/apps/desktop/src/i18n/locales/sv/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/sw/messages.po b/apps/desktop/src/i18n/locales/sw/messages.po index cefa1545e8d..e0ece0d21f9 100644 --- a/apps/desktop/src/i18n/locales/sw/messages.po +++ b/apps/desktop/src/i18n/locales/sw/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ta/messages.po b/apps/desktop/src/i18n/locales/ta/messages.po index 404bd31b5fa..603724a2570 100644 --- a/apps/desktop/src/i18n/locales/ta/messages.po +++ b/apps/desktop/src/i18n/locales/ta/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/te/messages.po b/apps/desktop/src/i18n/locales/te/messages.po index 4bcdf34f55c..49dd5db3503 100644 --- a/apps/desktop/src/i18n/locales/te/messages.po +++ b/apps/desktop/src/i18n/locales/te/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/tg/messages.po b/apps/desktop/src/i18n/locales/tg/messages.po index da49ba75f7d..7710d5df144 100644 --- a/apps/desktop/src/i18n/locales/tg/messages.po +++ b/apps/desktop/src/i18n/locales/tg/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/th/messages.po b/apps/desktop/src/i18n/locales/th/messages.po index 6134c00f220..8ebde81776b 100644 --- a/apps/desktop/src/i18n/locales/th/messages.po +++ b/apps/desktop/src/i18n/locales/th/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/tk/messages.po b/apps/desktop/src/i18n/locales/tk/messages.po index 60cd71cfa53..c4b4730c162 100644 --- a/apps/desktop/src/i18n/locales/tk/messages.po +++ b/apps/desktop/src/i18n/locales/tk/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/tl/messages.po b/apps/desktop/src/i18n/locales/tl/messages.po index 957cb8d619c..44ad58d56f9 100644 --- a/apps/desktop/src/i18n/locales/tl/messages.po +++ b/apps/desktop/src/i18n/locales/tl/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/tr/messages.po b/apps/desktop/src/i18n/locales/tr/messages.po index f24729d3fa2..107e16dc4d4 100644 --- a/apps/desktop/src/i18n/locales/tr/messages.po +++ b/apps/desktop/src/i18n/locales/tr/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/tt/messages.po b/apps/desktop/src/i18n/locales/tt/messages.po index 36894b1517c..74fcb23bf12 100644 --- a/apps/desktop/src/i18n/locales/tt/messages.po +++ b/apps/desktop/src/i18n/locales/tt/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/uk/messages.po b/apps/desktop/src/i18n/locales/uk/messages.po index 50ef85bcf0c..b1cd38b2f66 100644 --- a/apps/desktop/src/i18n/locales/uk/messages.po +++ b/apps/desktop/src/i18n/locales/uk/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/ur/messages.po b/apps/desktop/src/i18n/locales/ur/messages.po index 17e05a721c3..57ae91c6f85 100644 --- a/apps/desktop/src/i18n/locales/ur/messages.po +++ b/apps/desktop/src/i18n/locales/ur/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/uz/messages.po b/apps/desktop/src/i18n/locales/uz/messages.po index cc1a4930f6c..891f68ae5d1 100644 --- a/apps/desktop/src/i18n/locales/uz/messages.po +++ b/apps/desktop/src/i18n/locales/uz/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/vi/messages.po b/apps/desktop/src/i18n/locales/vi/messages.po index a87f22a3502..c79194ed08a 100644 --- a/apps/desktop/src/i18n/locales/vi/messages.po +++ b/apps/desktop/src/i18n/locales/vi/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/wo/messages.po b/apps/desktop/src/i18n/locales/wo/messages.po index 592f3eafda8..d97c574eb37 100644 --- a/apps/desktop/src/i18n/locales/wo/messages.po +++ b/apps/desktop/src/i18n/locales/wo/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/xh/messages.po b/apps/desktop/src/i18n/locales/xh/messages.po index a96bde34351..4fcc0c54298 100644 --- a/apps/desktop/src/i18n/locales/xh/messages.po +++ b/apps/desktop/src/i18n/locales/xh/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/yi/messages.po b/apps/desktop/src/i18n/locales/yi/messages.po index 9b9250a6b07..045de929a5f 100644 --- a/apps/desktop/src/i18n/locales/yi/messages.po +++ b/apps/desktop/src/i18n/locales/yi/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/yo/messages.po b/apps/desktop/src/i18n/locales/yo/messages.po index b31154c7517..50d2b458a1c 100644 --- a/apps/desktop/src/i18n/locales/yo/messages.po +++ b/apps/desktop/src/i18n/locales/yo/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/zh/messages.po b/apps/desktop/src/i18n/locales/zh/messages.po index 25c336fc11e..fb989b9bef4 100644 --- a/apps/desktop/src/i18n/locales/zh/messages.po +++ b/apps/desktop/src/i18n/locales/zh/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/i18n/locales/zu/messages.po b/apps/desktop/src/i18n/locales/zu/messages.po index 0f36a15a328..567df067cc6 100644 --- a/apps/desktop/src/i18n/locales/zu/messages.po +++ b/apps/desktop/src/i18n/locales/zu/messages.po @@ -4969,6 +4969,7 @@ msgid "Transcription complete" msgstr "" #: src/session/components/note-input/transcript/screens/empty.tsx +#: src/stt/useRunBatch.ts msgid "Transcription failed" msgstr "" diff --git a/apps/desktop/src/stt/useRunBatch.test.ts b/apps/desktop/src/stt/useRunBatch.test.ts index bfdfba7a11a..a13c2c6d945 100644 --- a/apps/desktop/src/stt/useRunBatch.test.ts +++ b/apps/desktop/src/stt/useRunBatch.test.ts @@ -22,6 +22,7 @@ const { useSessionParticipantsMock, useSTTConnectionMock, useAuthMock, + getSessionForRequestMock, refreshSessionMock, useBillingAccessMock, useConfigValueMock, @@ -41,6 +42,7 @@ const { useSessionParticipantsMock: vi.fn(), useSTTConnectionMock: vi.fn(), useAuthMock: vi.fn(), + getSessionForRequestMock: vi.fn(), refreshSessionMock: vi.fn(), useBillingAccessMock: vi.fn(), useConfigValueMock: vi.fn(), @@ -614,8 +616,12 @@ describe("useRunBatch", () => { access_token: "paid-token", user: { id: "user-1" }, }, + getSessionForRequest: getSessionForRequestMock, refreshSession: refreshSessionMock, }); + getSessionForRequestMock.mockResolvedValue({ + access_token: "paid-token", + }); refreshSessionMock.mockResolvedValue(null); useBillingAccessMock.mockReturnValue({ isPaid: false, @@ -1198,6 +1204,60 @@ describe("useRunBatch", () => { ); }); + test("uses a request-ready cloud token before transcription starts", async () => { + useSTTConnectionMock.mockReturnValue({ + conn: { + provider: "anarlog", + model: "cloud", + baseUrl: "https://api.test/stt", + apiKey: "stale-token", + }, + }); + useBillingAccessMock.mockReturnValue({ isPaid: true }); + getSessionForRequestMock.mockResolvedValue({ + access_token: "request-ready-token", + }); + startTranscriptionMock.mockResolvedValue(undefined); + + const { result } = renderHook(() => useRunBatch("session-1")); + + await act(async () => { + await result.current("/tmp/session.wav"); + }); + + expect(startTranscriptionMock).toHaveBeenCalledTimes(1); + expect(startTranscriptionMock).toHaveBeenCalledWith( + expect.objectContaining({ api_key: "request-ready-token" }), + expect.any(Object), + ); + }); + + test("falls back to the current cloud token when refresh is unavailable", async () => { + useSTTConnectionMock.mockReturnValue({ + conn: { + provider: "anarlog", + model: "cloud", + baseUrl: "https://api.test/stt", + apiKey: "stale-token", + }, + }); + useBillingAccessMock.mockReturnValue({ isPaid: true }); + getSessionForRequestMock.mockRejectedValue(new Error("offline")); + startTranscriptionMock.mockResolvedValue(undefined); + + const { result } = renderHook(() => useRunBatch("session-1")); + + await act(async () => { + await result.current("/tmp/session.wav"); + }); + + expect(startTranscriptionMock).toHaveBeenCalledTimes(1); + expect(startTranscriptionMock).toHaveBeenCalledWith( + expect.objectContaining({ api_key: "paid-token" }), + expect.any(Object), + ); + }); + test("refreshes an expired cloud token and retries transcription once", async () => { useSTTConnectionMock.mockReturnValue({ conn: { @@ -1212,8 +1272,12 @@ describe("useRunBatch", () => { access_token: "stale-token", user: { id: "user-1" }, }, + getSessionForRequest: getSessionForRequestMock, refreshSession: refreshSessionMock, }); + getSessionForRequestMock.mockResolvedValue({ + access_token: "stale-token", + }); refreshSessionMock.mockResolvedValue({ access_token: "fresh-token" }); startTranscriptionMock .mockImplementationOnce(async (_params, options) => { diff --git a/apps/desktop/src/stt/useRunBatch.ts b/apps/desktop/src/stt/useRunBatch.ts index c571067b517..37edf78e2a5 100644 --- a/apps/desktop/src/stt/useRunBatch.ts +++ b/apps/desktop/src/stt/useRunBatch.ts @@ -1,3 +1,4 @@ +import { t } from "@lingui/core/macro"; import { arch, platform } from "@tauri-apps/plugin-os"; import { useCallback } from "react"; @@ -589,9 +590,18 @@ export const useRunBatch = (sessionId: string) => { languages, ) : false; + const requiresCloudSession = + billing.isPaid || + (selectedTarget?.provider === "anarlog" && + selectedTarget.model === "cloud"); + const requestSession = requiresCloudSession + ? await auth.getSessionForRequest().catch(() => null) + : null; + const cloudAccessToken = + requestSession?.access_token ?? auth.session?.access_token; const fallbackTarget = getBatchFallbackTarget({ isPaid: billing.isPaid, - accessToken: auth?.session?.access_token, + accessToken: cloudAccessToken, apiBaseUrl: env.VITE_API_URL, currentPlatform, currentArch, @@ -599,7 +609,7 @@ export const useRunBatch = (sessionId: string) => { const shouldUseSelectedTarget = selectedTargetSupported || (fallbackTarget && sameBatchTarget(selectedTarget, fallbackTarget)); - const target = shouldUseSelectedTarget + let target = shouldUseSelectedTarget ? (selectedTarget ?? fallbackTarget) : fallbackTarget; @@ -611,6 +621,13 @@ export const useRunBatch = (sessionId: string) => { ); } + if (target.provider === "anarlog" && target.model === "cloud") { + if (!cloudAccessToken) { + throw new Error(t`Transcription failed`); + } + target = { ...target, apiKey: cloudAccessToken }; + } + if (!shouldUseSelectedTarget) { sonnerToast.warning("Using a batch transcription provider", { description: `${ @@ -862,7 +879,7 @@ export const useRunBatch = (sessionId: string) => { [ conn, auth, - auth?.session?.access_token, + auth.session?.access_token, aiLanguage, audioRetention, billing.isPaid,