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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions apps/desktop/src/components/settings/general/account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,31 @@ export function AccountSettings() {
});

if (store) {
const currentSttProvider = store.getValue("current_stt_provider");
const currentSttModel = store.getValue("current_stt_model");
const currentLlmProvider = store.getValue("current_llm_provider");
const preProSttProvider = store.getValue("pre_pro_stt_provider");
const preProSttModel = store.getValue("pre_pro_stt_model");
const preProLlmProvider = store.getValue("pre_pro_llm_provider");
const preProLlmModel = store.getValue("pre_pro_llm_model");

if (currentSttProvider === "hyprnote" && currentSttModel === "cloud") {
if (preProSttProvider) {
store.setValue("current_stt_provider", preProSttProvider);
store.setValue("current_stt_model", preProSttModel ?? "");
} else {
store.setValue("current_stt_provider", "");
store.setValue("current_stt_model", "");
}

if (currentLlmProvider === "hyprnote") {
if (preProLlmProvider) {
store.setValue("current_llm_provider", preProLlmProvider);
store.setValue("current_llm_model", preProLlmModel ?? "");
} else {
store.setValue("current_llm_provider", "");
store.setValue("current_llm_model", "");
}

store.setValue("pre_pro_stt_provider", "");
store.setValue("pre_pro_stt_model", "");
store.setValue("pre_pro_llm_provider", "");
store.setValue("pre_pro_llm_model", "");
Comment on lines +60 to +84
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Critical Step: The sign-out flow reads and restores pre_pro_* configs but never saves the current Pro configuration before clearing.

What breaks: If a user manually customized their Pro settings (e.g., changed from default hyprnote models to specific models), those customizations are lost on sign-out. The code only restores configs that were saved when first becoming Pro, not the current state at sign-out time.

Impact: User loses any Pro configuration changes they made between sign-in and sign-out.

Fix: Save current config before restoration:

if (store) {
  // Save current Pro settings before clearing
  const currentSttProvider = store.getValue("current_stt_provider");
  const currentSttModel = store.getValue("current_stt_model");
  const currentLlmProvider = store.getValue("current_llm_provider");
  const currentLlmModel = store.getValue("current_llm_model");
  
  if (currentSttProvider === "hyprnote") {
    store.setValue("pre_logout_stt_provider", currentSttProvider);
    store.setValue("pre_logout_stt_model", currentSttModel ?? "");
  }
  if (currentLlmProvider === "hyprnote") {
    store.setValue("pre_logout_llm_provider", currentLlmProvider);
    store.setValue("pre_logout_llm_model", currentLlmModel ?? "");
  }

  // Then restore pre-Pro configs or clear
  const preProSttProvider = store.getValue("pre_pro_stt_provider");
  // ... rest of the logic
}

Note: This requires adding pre_logout_* settings to settings.ts as mentioned in the PR description but missing from implementation.

Suggested change
const preProSttProvider = store.getValue("pre_pro_stt_provider");
const preProSttModel = store.getValue("pre_pro_stt_model");
const preProLlmProvider = store.getValue("pre_pro_llm_provider");
const preProLlmModel = store.getValue("pre_pro_llm_model");
if (currentSttProvider === "hyprnote" && currentSttModel === "cloud") {
if (preProSttProvider) {
store.setValue("current_stt_provider", preProSttProvider);
store.setValue("current_stt_model", preProSttModel ?? "");
} else {
store.setValue("current_stt_provider", "");
store.setValue("current_stt_model", "");
}
if (currentLlmProvider === "hyprnote") {
if (preProLlmProvider) {
store.setValue("current_llm_provider", preProLlmProvider);
store.setValue("current_llm_model", preProLlmModel ?? "");
} else {
store.setValue("current_llm_provider", "");
store.setValue("current_llm_model", "");
}
store.setValue("pre_pro_stt_provider", "");
store.setValue("pre_pro_stt_model", "");
store.setValue("pre_pro_llm_provider", "");
store.setValue("pre_pro_llm_model", "");
// Save current Pro settings before clearing
const currentSttProvider = store.getValue("current_stt_provider");
const currentSttModel = store.getValue("current_stt_model");
const currentLlmProvider = store.getValue("current_llm_provider");
const currentLlmModel = store.getValue("current_llm_model");
if (currentSttProvider === "hyprnote") {
store.setValue("pre_logout_stt_provider", currentSttProvider);
store.setValue("pre_logout_stt_model", currentSttModel ?? "");
}
if (currentLlmProvider === "hyprnote") {
store.setValue("pre_logout_llm_provider", currentLlmProvider);
store.setValue("pre_logout_llm_model", currentLlmModel ?? "");
}
// Then restore pre-Pro configs or clear
const preProSttProvider = store.getValue("pre_pro_stt_provider");
const preProSttModel = store.getValue("pre_pro_stt_model");
const preProLlmProvider = store.getValue("pre_pro_llm_provider");
const preProLlmModel = store.getValue("pre_pro_llm_model");
if (preProSttProvider) {
store.setValue("current_stt_provider", preProSttProvider);
store.setValue("current_stt_model", preProSttModel ?? "");
} else {
store.setValue("current_stt_provider", "");
store.setValue("current_stt_model", "");
}
if (preProLlmProvider) {
store.setValue("current_llm_provider", preProLlmProvider);
store.setValue("current_llm_model", preProLlmModel ?? "");
} else {
store.setValue("current_llm_provider", "");
store.setValue("current_llm_model", "");
}
store.setValue("pre_pro_stt_provider", "");
store.setValue("pre_pro_stt_model", "");
store.setValue("pre_pro_llm_provider", "");
store.setValue("pre_pro_llm_model", "");

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

}

await auth?.signOut();
Expand Down
176 changes: 176 additions & 0 deletions apps/desktop/src/hooks/useProModelAutoConfig.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { describe, expect, test } from "vitest";

import { createTestSettingsStore } from "../store/tinybase/persister/testing/mocks";

describe("Pro model auto-config store logic", () => {
test("on becoming Pro: saves non-Pro config and sets Pro models", () => {
const store = createTestSettingsStore();

store.setValue("current_stt_provider", "openai");
store.setValue("current_stt_model", "whisper-1");
store.setValue("current_llm_provider", "anthropic");
store.setValue("current_llm_model", "claude-3");

const currentSttProvider = store.getValue("current_stt_provider");
const currentSttModel = store.getValue("current_stt_model");
const currentLlmProvider = store.getValue("current_llm_provider");
const currentLlmModel = store.getValue("current_llm_model");

if (currentSttProvider && currentSttProvider !== "hyprnote") {
store.setValue("pre_pro_stt_provider", currentSttProvider);
store.setValue("pre_pro_stt_model", currentSttModel ?? "");
}
if (currentLlmProvider && currentLlmProvider !== "hyprnote") {
store.setValue("pre_pro_llm_provider", currentLlmProvider);
store.setValue("pre_pro_llm_model", currentLlmModel ?? "");
}

store.setValue("current_stt_provider", "hyprnote");
store.setValue("current_stt_model", "cloud");
store.setValue("current_llm_provider", "hyprnote");
store.setValue("current_llm_model", "Auto");

expect(store.getValue("current_stt_provider")).toBe("hyprnote");
expect(store.getValue("current_stt_model")).toBe("cloud");
expect(store.getValue("current_llm_provider")).toBe("hyprnote");
expect(store.getValue("current_llm_model")).toBe("Auto");
expect(store.getValue("pre_pro_stt_provider")).toBe("openai");
expect(store.getValue("pre_pro_stt_model")).toBe("whisper-1");
expect(store.getValue("pre_pro_llm_provider")).toBe("anthropic");
expect(store.getValue("pre_pro_llm_model")).toBe("claude-3");
});

test("on sign-out: restores pre-Pro config and clears saved values", () => {
const store = createTestSettingsStore();

store.setValue("current_stt_provider", "hyprnote");
store.setValue("current_stt_model", "cloud");
store.setValue("current_llm_provider", "hyprnote");
store.setValue("current_llm_model", "Auto");
store.setValue("pre_pro_stt_provider", "openai");
store.setValue("pre_pro_stt_model", "whisper-1");
store.setValue("pre_pro_llm_provider", "anthropic");
store.setValue("pre_pro_llm_model", "claude-3");

const preProSttProvider = store.getValue("pre_pro_stt_provider");
const preProSttModel = store.getValue("pre_pro_stt_model");
const preProLlmProvider = store.getValue("pre_pro_llm_provider");
const preProLlmModel = store.getValue("pre_pro_llm_model");

if (preProSttProvider) {
store.setValue("current_stt_provider", preProSttProvider);
store.setValue("current_stt_model", preProSttModel ?? "");
} else {
store.setValue("current_stt_provider", "");
store.setValue("current_stt_model", "");
}

if (preProLlmProvider) {
store.setValue("current_llm_provider", preProLlmProvider);
store.setValue("current_llm_model", preProLlmModel ?? "");
} else {
store.setValue("current_llm_provider", "");
store.setValue("current_llm_model", "");
}

store.setValue("pre_pro_stt_provider", "");
store.setValue("pre_pro_stt_model", "");
store.setValue("pre_pro_llm_provider", "");
store.setValue("pre_pro_llm_model", "");

expect(store.getValue("current_stt_provider")).toBe("openai");
expect(store.getValue("current_stt_model")).toBe("whisper-1");
expect(store.getValue("current_llm_provider")).toBe("anthropic");
expect(store.getValue("current_llm_model")).toBe("claude-3");
expect(store.getValue("pre_pro_stt_provider")).toBe("");
expect(store.getValue("pre_pro_stt_model")).toBe("");
expect(store.getValue("pre_pro_llm_provider")).toBe("");
expect(store.getValue("pre_pro_llm_model")).toBe("");
});

test("on sign-out without pre-Pro config: clears current config", () => {
const store = createTestSettingsStore();

store.setValue("current_stt_provider", "hyprnote");
store.setValue("current_stt_model", "cloud");
store.setValue("current_llm_provider", "hyprnote");
store.setValue("current_llm_model", "Auto");

const preProSttProvider = store.getValue("pre_pro_stt_provider");
const preProLlmProvider = store.getValue("pre_pro_llm_provider");

if (preProSttProvider) {
store.setValue("current_stt_provider", preProSttProvider);
} else {
store.setValue("current_stt_provider", "");
store.setValue("current_stt_model", "");
}

if (preProLlmProvider) {
store.setValue("current_llm_provider", preProLlmProvider);
} else {
store.setValue("current_llm_provider", "");
store.setValue("current_llm_model", "");
}

expect(store.getValue("current_stt_provider")).toBe("");
expect(store.getValue("current_stt_model")).toBe("");
expect(store.getValue("current_llm_provider")).toBe("");
expect(store.getValue("current_llm_model")).toBe("");
});

test("full flow: non-Pro -> Pro -> sign-out restores original config", () => {
const store = createTestSettingsStore();

store.setValue("current_stt_provider", "deepgram");
store.setValue("current_stt_model", "nova-2");
store.setValue("current_llm_provider", "openai");
store.setValue("current_llm_model", "gpt-4");

const sttBefore = store.getValue("current_stt_provider");
const sttModelBefore = store.getValue("current_stt_model");
const llmBefore = store.getValue("current_llm_provider");
const llmModelBefore = store.getValue("current_llm_model");

if (sttBefore && sttBefore !== "hyprnote") {
store.setValue("pre_pro_stt_provider", sttBefore);
store.setValue("pre_pro_stt_model", sttModelBefore ?? "");
}
if (llmBefore && llmBefore !== "hyprnote") {
store.setValue("pre_pro_llm_provider", llmBefore);
store.setValue("pre_pro_llm_model", llmModelBefore ?? "");
}
store.setValue("current_stt_provider", "hyprnote");
store.setValue("current_stt_model", "cloud");
store.setValue("current_llm_provider", "hyprnote");
store.setValue("current_llm_model", "Auto");

expect(store.getValue("current_stt_provider")).toBe("hyprnote");
expect(store.getValue("current_llm_provider")).toBe("hyprnote");

const preProStt = store.getValue("pre_pro_stt_provider");
const preProSttModel = store.getValue("pre_pro_stt_model");
const preProLlm = store.getValue("pre_pro_llm_provider");
const preProLlmModel = store.getValue("pre_pro_llm_model");

if (preProStt) {
store.setValue("current_stt_provider", preProStt);
store.setValue("current_stt_model", preProSttModel ?? "");
}
if (preProLlm) {
store.setValue("current_llm_provider", preProLlm);
store.setValue("current_llm_model", preProLlmModel ?? "");
}
store.setValue("pre_pro_stt_provider", "");
store.setValue("pre_pro_stt_model", "");
store.setValue("pre_pro_llm_provider", "");
store.setValue("pre_pro_llm_model", "");

expect(store.getValue("current_stt_provider")).toBe("deepgram");
expect(store.getValue("current_stt_model")).toBe("nova-2");
expect(store.getValue("current_llm_provider")).toBe("openai");
expect(store.getValue("current_llm_model")).toBe("gpt-4");
expect(store.getValue("pre_pro_stt_provider")).toBe("");
expect(store.getValue("pre_pro_llm_provider")).toBe("");
});
});
23 changes: 13 additions & 10 deletions apps/desktop/src/hooks/useProModelAutoConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,23 @@ export function useProModelAutoConfig() {

if (wasNotPro && isNowPro) {
const currentSttProvider = store.getValue("current_stt_provider");
const currentSttModel = store.getValue("current_stt_model");
const currentLlmProvider = store.getValue("current_llm_provider");
const currentLlmModel = store.getValue("current_llm_model");

const sttNotConfigured = !currentSttProvider;
const llmNotConfigured = !currentLlmProvider;

if (sttNotConfigured) {
store.setValue("current_stt_provider", "hyprnote");
store.setValue("current_stt_model", "cloud");
if (currentSttProvider && currentSttProvider !== "hyprnote") {
store.setValue("pre_pro_stt_provider", currentSttProvider);
store.setValue("pre_pro_stt_model", currentSttModel ?? "");
}

if (llmNotConfigured) {
store.setValue("current_llm_provider", "hyprnote");
store.setValue("current_llm_model", "Auto");
if (currentLlmProvider && currentLlmProvider !== "hyprnote") {
store.setValue("pre_pro_llm_provider", currentLlmProvider);
store.setValue("pre_pro_llm_model", currentLlmModel ?? "");
}

store.setValue("current_stt_provider", "hyprnote");
store.setValue("current_stt_model", "cloud");
store.setValue("current_llm_provider", "hyprnote");
store.setValue("current_llm_model", "Auto");
}

prevIsProRef.current = isPro;
Expand Down
16 changes: 16 additions & 0 deletions apps/desktop/src/store/tinybase/store/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,22 @@ export const SETTINGS_MAPPING = {
type: "string",
path: ["ai", "current_stt_model"],
},
pre_pro_stt_provider: {
type: "string",
path: ["ai", "pre_pro_stt_provider"],
},
pre_pro_stt_model: {
type: "string",
path: ["ai", "pre_pro_stt_model"],
},
pre_pro_llm_provider: {
type: "string",
path: ["ai", "pre_pro_llm_provider"],
},
pre_pro_llm_model: {
type: "string",
path: ["ai", "pre_pro_llm_model"],
},
trial_expired_modal_dismissed_at: {
type: "number",
path: ["billing", "trial_expired_modal_dismissed_at"],
Expand Down
Loading