Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion .github/workflows/android-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
frontend/src-tauri/gen/android/.gradle
frontend/src-tauri/gen/android/build
frontend/src-tauri/gen/android/app/build
key: ${{ runner.os }}-gradle-${{ hashFiles('frontend/src-tauri/gen/android/**/*.gradle*', 'frontend/src-tauri/gen/android/gradle-wrapper.properties') }}
key: ${{ runner.os }}-gradle-${{ hashFiles('frontend/src-tauri/gen/android/**/*.gradle*', 'frontend/src-tauri/gen/android/gradle-wrapper.properties', 'frontend/src-tauri/tauri.conf.json', 'frontend/src-tauri/capabilities/**') }}
restore-keys: |
${{ runner.os }}-gradle-

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ jobs:
frontend/src-tauri/gen/android/.gradle
frontend/src-tauri/gen/android/build
frontend/src-tauri/gen/android/app/build
key: ${{ runner.os }}-gradle-${{ hashFiles('frontend/src-tauri/gen/android/**/*.gradle*', 'frontend/src-tauri/gen/android/gradle-wrapper.properties') }}
key: ${{ runner.os }}-gradle-${{ hashFiles('frontend/src-tauri/gen/android/**/*.gradle*', 'frontend/src-tauri/gen/android/gradle-wrapper.properties', 'frontend/src-tauri/tauri.conf.json', 'frontend/src-tauri/capabilities/**') }}
restore-keys: |
${{ runner.os }}-gradle-

Expand Down
125 changes: 95 additions & 30 deletions frontend/src/routes/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,32 +106,53 @@ function LoginPage() {
};

const handleGitHubLogin = async () => {
setError(null);
const platform = isIOSPlatform ? "iOS" : "Android/Desktop";
setError(`[DEBUG] GitHub click detected. Tauri=${isTauriEnv}, Platform=${platform}`);
Comment on lines +109 to +111
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

logic: Debug code displaying [DEBUG] messages to users via setError(). The commit message says "Login Debug - delete after" - this should be removed before merging to production.

Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/routes/login.tsx
Line: 109:111

Comment:
**logic:** Debug code displaying `[DEBUG]` messages to users via `setError()`. The commit message says "Login Debug - delete after" - this should be removed before merging to production.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +109 to +111
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Remove debug code from production or gate behind a feature flag.

The code uses setError to display debug messages directly to users via the AlertDestructive component. This is inappropriate for production:

  • Users will see technical debug messages like [DEBUG] GitHub click detected. Tauri=true, Platform=iOS
  • This violates the purpose of error messaging which should communicate actionable user-facing information

Additionally, multiple sequential setError calls throughout these handlers will overwrite each other due to React's state batching, making the debug traces unreliable.

Apply this pattern to remove debug setError calls and rely solely on console logging:

  const handleGitHubLogin = async () => {
-   setError(null);
-   const platform = isIOSPlatform ? "iOS" : "Android/Desktop";
-   setError(`[DEBUG] GitHub click detected. Tauri=${isTauriEnv}, Platform=${platform}`);
-
    try {
+     setError(null);
      console.log("[OAuth] Using", isTauriEnv ? "Tauri" : "web", "flow");
+     console.log("[OAuth] Platform:", isIOSPlatform ? "iOS" : "Android/Desktop");

If you need to keep debug traces for troubleshooting production issues, gate them behind a feature flag:

const DEBUG_OAUTH = import.meta.env.DEV || sessionStorage.getItem('debug_oauth') === 'true';

if (DEBUG_OAUTH) {
  setError(`[DEBUG] GitHub click detected. Tauri=${isTauriEnv}, Platform=${platform}`);
}

Also applies to: 173-175, 363-365

🤖 Prompt for AI Agents
In frontend/src/routes/login.tsx around lines 109-111 (and similarly at 173-175
and 363-365), remove the debug setError calls that write debug strings into
user-facing error state; instead log debug information to console
(console.debug/console.log) or gate the debug setError behind a runtime feature
flag (e.g., DEBUG_OAUTH derived from DEV env or sessionStorage) so that non-dev
users never see technical messages; ensure you consolidate multiple sequential
debug setError calls into a single gated log and keep user-facing setError
messages reserved for actionable errors only.


try {
console.log("[OAuth] Using", isTauriEnv ? "Tauri" : "web", "flow");

if (isTauriEnv) {
// For Tauri (desktop or mobile), redirect to the web app's desktop-auth route
let desktopAuthUrl = "https://trymaple.ai/desktop-auth?provider=github";

// If there's a selected plan, add it to the URL
if (selected_plan) {
desktopAuthUrl += `&selected_plan=${encodeURIComponent(selected_plan)}`;
}

// If there's a redemption code, add it to the URL
if (code) {
desktopAuthUrl += `&code=${encodeURIComponent(code)}`;
}

// Use the opener plugin by directly invoking the command
// This works for both desktop and mobile (iOS/Android)
console.log("[OAuth] Opening URL in external browser:", desktopAuthUrl);
invoke("plugin:opener|open_url", { url: desktopAuthUrl }).catch((error: Error) => {
console.error("[OAuth] Failed to open external browser:", error);
setError("Failed to open authentication page in browser");
});
setError(`[DEBUG] Attempting to open: ${desktopAuthUrl}`);

try {
const result = await invoke("plugin:opener|open_url", { url: desktopAuthUrl });
console.log("[OAuth] invoke result:", result);
setError(`[DEBUG] invoke succeeded, result: ${JSON.stringify(result)}`);
} catch (invokeError) {
const errMsg =
invokeError instanceof Error ? invokeError.message : JSON.stringify(invokeError);
console.error("[OAuth] invoke failed:", invokeError);
setError(`[ERROR] invoke failed: ${errMsg}`);

// Try fallback with plugin import
try {
setError(`[DEBUG] Trying fallback with plugin import...`);
const { openUrl } = await import("@tauri-apps/plugin-opener");
await openUrl(desktopAuthUrl);
setError(`[DEBUG] Fallback openUrl succeeded`);
} catch (fallbackError) {
const fbErrMsg =
fallbackError instanceof Error
? fallbackError.message
: JSON.stringify(fallbackError);
console.error("[OAuth] Fallback also failed:", fallbackError);
setError(`[ERROR] Both methods failed. invoke: ${errMsg} | fallback: ${fbErrMsg}`);
}
}
Comment on lines +128 to +154
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Sequential setError calls will not work as intended.

The code attempts to trace execution by calling setError multiple times sequentially (lines 128, 133, 138, 142, 145, 152). Due to React's state batching, only the final setError call in each synchronous execution block will take effect. The intermediate debug traces will be lost.

If you need step-by-step debugging visibility, consider:

  1. Accumulating messages in an array and displaying all at once
  2. Using console logging exclusively (recommended)
  3. Implementing a dedicated debug message array state separate from error state

Example with accumulated debug messages (if feature-flagged):

const debugMessages: string[] = [];

try {
  debugMessages.push(`Attempting to open: ${desktopAuthUrl}`);
  const result = await invoke("plugin:opener|open_url", { url: desktopAuthUrl });
  debugMessages.push(`invoke succeeded: ${JSON.stringify(result)}`);
  if (DEBUG_OAUTH) setError(debugMessages.join('\n'));
} catch (invokeError) {
  // ...
}

However, console logging remains the cleaner approach for debugging.

🤖 Prompt for AI Agents
frontend/src/routes/login.tsx lines 128-154: the code uses multiple sequential
setError(...) calls for debug tracing which will be batched by React so
intermediate messages are lost; replace the debug setError calls with
console.log/debug statements (or, if you must display multiple steps, collect
messages into a local array and call setError once with the joined string or use
a separate debugMessages state) and keep a single setError call for the final
error message that includes both invoke and fallback error details.

🛠️ Refactor suggestion | 🟠 Major

Extract the two-step opener pattern to eliminate code duplication.

The same two-step opener logic (invoke → fallback with dynamic import) is duplicated across all three OAuth handlers (GitHub, Google, Apple non-iOS). This violates the DRY principle and creates a maintenance burden.

Extract the shared logic into a reusable helper function:

async function openTauriUrl(url: string): Promise<void> {
  console.log("[OAuth] Opening URL in external browser:", url);
  
  try {
    const result = await invoke("plugin:opener|open_url", { url });
    console.log("[OAuth] invoke result:", result);
  } catch (invokeError) {
    console.error("[OAuth] invoke failed, trying fallback:", invokeError);
    
    // Fallback: dynamically import and use plugin-opener
    const { openUrl } = await import("@tauri-apps/plugin-opener");
    await openUrl(url);
    console.log("[OAuth] Fallback openUrl succeeded");
  }
}

Then simplify each handler:

if (isTauriEnv) {
  let desktopAuthUrl = `https://trymaple.ai/desktop-auth?provider=github`;
  
  if (selected_plan) {
    desktopAuthUrl += `&selected_plan=${encodeURIComponent(selected_plan)}`;
  }
  
  if (code) {
    desktopAuthUrl += `&code=${encodeURIComponent(code)}`;
  }
  
  await openTauriUrl(desktopAuthUrl);
}

This reduces ~75 lines of duplicated code to a single reusable function.

Also applies to: 192-217, 378-403

🤖 Prompt for AI Agents
frontend/src/routes/login.tsx around lines 128-154 (also apply same change at
192-217 and 378-403): the two-step opener logic (invoke → fallback dynamic
import) is duplicated across three OAuth handlers; extract it to a single helper
async function openTauriUrl(url: string) that (1) logs the URL, (2) attempts
invoke("plugin:opener|open_url", { url }), (3) on invoke failure dynamically
imports @tauri-apps/plugin-opener and calls openUrl(url), and (4) if both
methods fail throws a new Error that includes both error messages; then replace
the duplicated blocks in each handler with await openTauriUrl(desktopAuthUrl)
wrapped in try/catch in the handler so setError/console.error behavior is
preserved, and keep the existing desktopAuthUrl query param encoding logic
unchanged.

} else {
// Web flow remains unchanged
const { auth_url } = await os.initiateGitHubAuth("");
if (selected_plan) {
sessionStorage.setItem("selected_plan", selected_plan);
Expand All @@ -142,38 +163,59 @@ function LoginPage() {
window.location.href = auth_url;
}
} catch (error) {
const errMsg = error instanceof Error ? error.message : JSON.stringify(error);
console.error("Failed to initiate GitHub login:", error);
setError("Failed to initiate GitHub login. Please try again.");
setError(`[ERROR] Outer catch: ${errMsg}`);
}
Comment on lines +166 to 169
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove [ERROR] prefix from user-facing error messages.

While extracting the error message properly (using instanceof Error check) is good, the [ERROR] Outer catch: prefix should be removed from production code. Users don't need to see technical debugging markers.

Apply this diff:

  } catch (error) {
    const errMsg = error instanceof Error ? error.message : JSON.stringify(error);
    console.error("Failed to initiate GitHub login:", error);
-   setError(`[ERROR] Outer catch: ${errMsg}`);
+   setError(errMsg || "Failed to initiate GitHub login. Please try again.");
  }

Also applies to: 229-232

🤖 Prompt for AI Agents
In frontend/src/routes/login.tsx around lines 166-169 and 229-232, remove the
technical “[ERROR] Outer catch:” prefix from user-facing error strings; instead
set the error state to the extracted message only (use errMsg or the same
extracted string variable) and keep console.error for debugging. Update both
locations to call setError(errMsg) (or a short user-friendly variant) rather
than prepending the “[ERROR]” tag, ensuring consistent messages shown to users
while retaining the detailed console.error logs for developers.

};

const handleGoogleLogin = async () => {
setError(null);
const platform = isIOSPlatform ? "iOS" : "Android/Desktop";
setError(`[DEBUG] Google click detected. Tauri=${isTauriEnv}, Platform=${platform}`);
Comment on lines +173 to +175
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

logic: Same debug pattern in handleGoogleLogin - displays debug info to users.

Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/routes/login.tsx
Line: 173:175

Comment:
**logic:** Same debug pattern in `handleGoogleLogin` - displays debug info to users.

How can I resolve this? If you propose a fix, please make it concise.


try {
console.log("[OAuth] Using", isTauriEnv ? "Tauri" : "web", "flow");

if (isTauriEnv) {
// For Tauri (desktop or mobile), redirect to the web app's desktop-auth route
let desktopAuthUrl = "https://trymaple.ai/desktop-auth?provider=google";

// If there's a selected plan, add it to the URL
if (selected_plan) {
desktopAuthUrl += `&selected_plan=${encodeURIComponent(selected_plan)}`;
}

// If there's a redemption code, add it to the URL
if (code) {
desktopAuthUrl += `&code=${encodeURIComponent(code)}`;
}

// Use the opener plugin by directly invoking the command
// This works for both desktop and mobile (iOS/Android)
console.log("[OAuth] Opening URL in external browser:", desktopAuthUrl);
invoke("plugin:opener|open_url", { url: desktopAuthUrl }).catch((error: Error) => {
console.error("[OAuth] Failed to open external browser:", error);
setError("Failed to open authentication page in browser");
});
setError(`[DEBUG] Attempting to open: ${desktopAuthUrl}`);

try {
const result = await invoke("plugin:opener|open_url", { url: desktopAuthUrl });
console.log("[OAuth] invoke result:", result);
setError(`[DEBUG] invoke succeeded, result: ${JSON.stringify(result)}`);
} catch (invokeError) {
const errMsg =
invokeError instanceof Error ? invokeError.message : JSON.stringify(invokeError);
console.error("[OAuth] invoke failed:", invokeError);
setError(`[ERROR] invoke failed: ${errMsg}`);

try {
setError(`[DEBUG] Trying fallback with plugin import...`);
const { openUrl } = await import("@tauri-apps/plugin-opener");
await openUrl(desktopAuthUrl);
setError(`[DEBUG] Fallback openUrl succeeded`);
} catch (fallbackError) {
const fbErrMsg =
fallbackError instanceof Error
? fallbackError.message
: JSON.stringify(fallbackError);
console.error("[OAuth] Fallback also failed:", fallbackError);
setError(`[ERROR] Both methods failed. invoke: ${errMsg} | fallback: ${fbErrMsg}`);
}
}
} else {
// Web flow remains unchanged
const { auth_url } = await os.initiateGoogleAuth("");
if (selected_plan) {
sessionStorage.setItem("selected_plan", selected_plan);
Expand All @@ -184,8 +226,9 @@ function LoginPage() {
window.location.href = auth_url;
}
} catch (error) {
const errMsg = error instanceof Error ? error.message : JSON.stringify(error);
console.error("Failed to initiate Google login:", error);
setError("Failed to initiate Google login. Please try again.");
setError(`[ERROR] Outer catch: ${errMsg}`);
}
};

Expand Down Expand Up @@ -317,25 +360,47 @@ function LoginPage() {
setError(errorMessage);
}
} else if (isTauriEnv) {
// For Tauri desktop and Android, redirect to the web app's desktop-auth route
setError(
`[DEBUG] Apple click (non-iOS Tauri). Platform=${isIOSPlatform ? "iOS" : "Android/Desktop"}`
);
Comment on lines +363 to +365
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

logic: Same debug pattern in handleAppleLogin - displays debug info to users.

Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/routes/login.tsx
Line: 363:365

Comment:
**logic:** Same debug pattern in `handleAppleLogin` - displays debug info to users.

How can I resolve this? If you propose a fix, please make it concise.


let desktopAuthUrl = "https://trymaple.ai/desktop-auth?provider=apple";

// If there's a selected plan, add it to the URL
if (selected_plan) {
desktopAuthUrl += `&selected_plan=${encodeURIComponent(selected_plan)}`;
}

// If there's a redemption code, add it to the URL
if (code) {
desktopAuthUrl += `&code=${encodeURIComponent(code)}`;
}

// Use the opener plugin by directly invoking the command
console.log("[OAuth] Opening URL in external browser:", desktopAuthUrl);
invoke("plugin:opener|open_url", { url: desktopAuthUrl }).catch((error: Error) => {
console.error("[OAuth] Failed to open external browser:", error);
setError("Failed to open authentication page in browser");
});
setError(`[DEBUG] Attempting to open: ${desktopAuthUrl}`);

try {
const result = await invoke("plugin:opener|open_url", { url: desktopAuthUrl });
console.log("[OAuth] invoke result:", result);
setError(`[DEBUG] invoke succeeded, result: ${JSON.stringify(result)}`);
} catch (invokeError) {
const errMsg =
invokeError instanceof Error ? invokeError.message : JSON.stringify(invokeError);
console.error("[OAuth] invoke failed:", invokeError);
setError(`[ERROR] invoke failed: ${errMsg}`);

try {
setError(`[DEBUG] Trying fallback with plugin import...`);
const { openUrl } = await import("@tauri-apps/plugin-opener");
await openUrl(desktopAuthUrl);
setError(`[DEBUG] Fallback openUrl succeeded`);
} catch (fallbackError) {
const fbErrMsg =
fallbackError instanceof Error
? fallbackError.message
: JSON.stringify(fallbackError);
console.error("[OAuth] Fallback also failed:", fallbackError);
setError(`[ERROR] Both methods failed. invoke: ${errMsg} | fallback: ${fbErrMsg}`);
}
}
} else {
// Web flow - use AppleAuthProvider component which will initiate the flow
console.log("[OAuth] Using web flow for Apple Sign In (Web only)");
Expand Down
Loading