Skip to content

Conversation

@GJRobert
Copy link
Collaborator

No description provided.

Claude Code added 2 commits January 25, 2026 08:54
- 修復 HTML 結構:調整 </details> 和 </div> 的順序
- 移除未使用的 SYNC_KEYS 常數
- 移除 setTimeout(100ms) 競合:同步完成後立即更新 UI
- 新增 showSyncError:同步失敗時顯示用戶可見的錯誤訊息
- 移除不可靠的 beforeunload,只使用 visibilitychange
@gemini-code-assist
Copy link

Summary of Changes

Hello @GJRobert, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request primarily focuses on enhancing the robustness and user experience of the cloud synchronization mechanism. It streamlines the synchronization logic, provides clearer error feedback to users, and ensures more reliable data persistence when a page is closed or hidden. Additionally, a minor HTML structural correction is included.

Highlights

  • Cloud Sync Refinement: Streamlined the cloud synchronization process by removing the explicit SYNC_KEYS constant and optimizing UI updates for immediate feedback.
  • Enhanced Error Handling: Introduced a new showSyncError function to provide temporary, user-visible error notifications for cloud synchronization failures.
  • Reliable Page Unload Synchronization: Improved the reliability of data persistence upon page unload by exclusively using the visibilitychange event and removing the unreliable beforeunload listener for asynchronous operations.
  • HTML Structure Correction: Fixed a minor structural issue in index.html by correcting the nesting of a </details> tag.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@claude
Copy link

claude bot commented Jan 25, 2026

Code Review for PR #195: Fix OAuth Redirect

I've reviewed the changes in this PR and have the following feedback:

✅ Positive Changes

  1. Improved Error Handling: The addition of showSyncError() function (lines 305-317) provides better user feedback when sync operations fail. This is a good UX improvement.

  2. Cleaner UI Update Logic: Consolidating the UI update call outside the if/else blocks (lines 166-169) is cleaner and eliminates code duplication.

  3. Better Page Unload Handling: Removing the beforeunload event listener is the right decision, as asynchronous operations in beforeunload are unreliable and often don't complete before the page unloads.

  4. HTML Structure Fix: The correction of the </details> tag placement in index.html fixes a potential rendering issue.

⚠️ Issues & Concerns

1. Critical: OAuth Redirect Configuration (js/cloud-sync.js:76)

The OAuth redirect is currently configured as:

redirectTo: window.location.origin + window.location.pathname,

Issue: This doesn't preserve query parameters or hash fragments. If the OAuth provider adds state parameters or the user was on a specific section of the page, this information will be lost.

Recommendation:

redirectTo: window.location.origin + window.location.pathname + window.location.search,

Or even better, preserve the full URL (excluding hash to avoid issues):

redirectTo: window.location.href.split('#')[0],

2. Race Condition in UI Updates (js/cloud-sync.js:166-169)

The UI update now happens immediately after syncToCloud(), but there's no guarantee that syncToCloud() has completed successfully since errors are caught silently.

Recommendation: Consider moving the UI update inside syncToCloud() after successful completion, or at least check if sync succeeded before updating UI.

3. Silent Failure in syncToCloud() (js/cloud-sync.js:216-218)

catch (err) {
  console.error('[CloudSync] 上傳失敗:', err);
}

Issue: Upload failures are only logged to console but not surfaced to the user or the calling function. Users won't know their data wasn't saved.

Recommendation:

  • Throw the error or return a status indicator
  • Call showSyncError() when uploads fail
  • Update the sync status UI to reflect the failure

4. Removed Constant Without Clear Reason

The SYNC_KEYS constant was removed but its purpose isn't clear from the diff. If this was used for validation or configuration, its removal might indicate a regression.

Question: What was SYNC_KEYS used for, and why was it safe to remove?

5. visibilitychange Timing Issue (js/cloud-sync.js:370-379)

The visibilitychange event fires when the page becomes hidden, but calling syncToCloud() at this point may not complete if the browser suspends the page immediately.

Consideration: Modern browsers may support the Page Lifecycle API with a freeze event that's more reliable. However, the current approach is reasonable given the limitations mentioned in the comment.

🔒 Security Review

Concern: The Supabase anonymous key is hardcoded in js/supabase-config.js (line 7-8). While this is standard for client-side Supabase apps, ensure that:

  • Row Level Security (RLS) policies are properly configured on the Supabase backend
  • The user_sync_data table has policies that prevent users from accessing other users' data
  • The anonymous key has appropriate restrictions

🧪 Testing Considerations

Missing Test Coverage: This PR has no automated tests. Consider testing:

  1. OAuth redirect flow preserves user context
  2. Sync operations handle network failures gracefully
  3. UI updates reflect actual sync status
  4. Data isn't lost when page visibility changes rapidly
  5. Error messages display correctly to users

📊 Performance

The changes should have minimal performance impact. The removal of the beforeunload listener actually improves performance by removing an unreliable synchronous operation.

📝 Recommendations Summary

High Priority:

  1. Fix the OAuth redirect URL to preserve query parameters
  2. Add error handling to syncToCloud() with user-visible feedback
  3. Ensure UI updates only happen after successful sync operations

Medium Priority:
4. Add integration tests for OAuth flow and sync operations
5. Document the removal of SYNC_KEYS if it was intentional
6. Consider adding a retry mechanism for failed syncs

Low Priority:
7. Add JSDoc comments to the new showSyncError() function (already done ✓)
8. Consider implementing exponential backoff for sync retries

Overall Assessment

This PR makes several good improvements to the sync reliability and user experience. The main concerns are around error handling completeness and the OAuth redirect configuration. With the recommended fixes, this will be a solid improvement to the codebase.

Recommendation: Request changes for the OAuth redirect URL handling and error surfacing before merging.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

這份 PR 對雲端同步功能做了幾項很棒的改進。它重構了 UI 更新的邏輯,讓程式碼結構更清晰;為同步失敗的情況增加了使用者可見的錯誤通知;並且透過移除有問題的 beforeunload 事件監聽器,提高了頁面關閉時資料同步的可靠性。此外,也修正了 HTML 的結構。整體而言,這些都是能提升程式碼品質與使用者體驗的正面變更。我只對新的錯誤提示函式有一點建議,希望能讓它在處理連續快速的錯誤時更加穩健。

Comment on lines 305 to 317
function showSyncError(message) {
// 使用現有的 cloudSyncBtn 顯示 tooltip 風格的錯誤訊息
const cloudSyncBtn = document.getElementById('cloudSyncBtn');
if (!cloudSyncBtn) return;

const originalTitle = cloudSyncBtn.title;
cloudSyncBtn.title = message;

// 3 秒後恢復原本的 title
setTimeout(() => {
cloudSyncBtn.title = originalTitle;
}, 3000);
}

Choose a reason for hiding this comment

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

medium

目前的 showSyncError 實作在快速連續觸發時,可能存在 race condition。如果函式在 3 秒的 timeout 結束前被多次呼叫,會導致按鈕的 title 最終停留在舊的錯誤訊息,而無法還原到最初的狀態。這是因為 originalTitle 變數會捕獲到呼叫當下、可能是前一個錯誤訊息的 title

為了讓此函式更加穩健,建議將「真正的」原始 title 儲存在元素的 dataset 中,並將 timeout ID 也附加到元素上管理。這樣可以確保即使函式被多次呼叫,timeout 也能被正確重置,且 UI 最終能回到正確的非錯誤狀態。

function showSyncError(message) {
  // 使用現有的 cloudSyncBtn 顯示 tooltip 風格的錯誤訊息
  const cloudSyncBtn = document.getElementById('cloudSyncBtn');
  if (!cloudSyncBtn) return;

  // 如果已有錯誤提示的 timeout,先清除,避免舊的 timeout 覆蓋新的狀態
  if (cloudSyncBtn.syncErrorTimeout) {
    clearTimeout(cloudSyncBtn.syncErrorTimeout);
  } else {
    // 如果沒有,表示這是新的錯誤提示,儲存當前的 title 做為原始 title
    cloudSyncBtn.dataset.originalTitle = cloudSyncBtn.title;
  }

  cloudSyncBtn.title = message;

  // 設定新的 timeout 來還原 title
  cloudSyncBtn.syncErrorTimeout = setTimeout(() => {
    // 還原 title,並清除暫存的屬性
    if (cloudSyncBtn.dataset.originalTitle) {
      cloudSyncBtn.title = cloudSyncBtn.dataset.originalTitle;
      delete cloudSyncBtn.dataset.originalTitle;
    }
    delete cloudSyncBtn.syncErrorTimeout;
  }, 3000);
}

- OAuth redirect 保留 query parameters(腔調、級別、類別等狀態)
- syncToCloud() 失敗時顯示錯誤通知
- showSyncError() 使用 dataset 避免 race condition
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Jan 25, 2026

Deploying hakspring with  Cloudflare Pages  Cloudflare Pages

Latest commit: 40b9937
Status: ✅  Deploy successful!
Preview URL: https://5a205f0a.hakspring.pages.dev
Branch Preview URL: https://claude-fix-oauth-redirect-k2.hakspring.pages.dev

View logs

@claude
Copy link

claude bot commented Jan 25, 2026

Pull Request Review: Fix OAuth Redirect

Summary

This PR fixes the OAuth redirect flow to preserve query parameters and improves the cloud sync error handling and UI update logic. Overall, the changes are well-implemented with good attention to user experience.


Code Quality & Best Practices ✅

Positive Changes:

  1. Better State Preservation: The OAuth redirect now preserves window.location.search, allowing users to return to their previous state (dialect, level, category) after login - excellent UX improvement!

  2. Code Simplification: Consolidated duplicate UI update logic by removing redundant setTimeout calls and moving the UI update outside the conditional blocks (js/cloud-sync.js:171-174).

  3. Better Error Handling: Added user-facing error notifications via showSyncError() function with proper timeout management.

  4. Removed Unreliable Code: Correctly removed the beforeunload event handler since async operations in that context are unreliable.

  5. HTML Structure Fix: Fixed the misplaced </details> closing tag in index.html (moved from line 224 to line 223).


Potential Issues & Concerns ⚠️

1. Race Condition in showSyncError() (Medium Priority)

Location: js/cloud-sync.js:312-336

Issue: The function stores timeout IDs as strings in dataset.errorTimeoutId, but there's a potential issue:

cloudSyncBtn.dataset.errorTimeoutId = timeoutId; // timeoutId is a number
// Later...
clearTimeout(Number(cloudSyncBtn.dataset.errorTimeoutId)); // converts back

Problem: If showSyncError() is called multiple times in rapid succession, the originalTitle might get overwritten with an error message instead of the actual original title.

Recommendation: Store the original title once during initialization or use a WeakMap:

const originalTitles = new WeakMap();

function showSyncError(message) {
  const cloudSyncBtn = document.getElementById('cloudSyncBtn');
  if (!cloudSyncBtn) return;
  
  if (cloudSyncBtn.dataset.errorTimeoutId) {
    clearTimeout(Number(cloudSyncBtn.dataset.errorTimeoutId));
  }
  
  // Store original title only once
  if (!originalTitles.has(cloudSyncBtn)) {
    originalTitles.set(cloudSyncBtn, cloudSyncBtn.title);
  }
  
  cloudSyncBtn.title = message;
  
  const timeoutId = setTimeout(() => {
    cloudSyncBtn.title = originalTitles.get(cloudSyncBtn);
    delete cloudSyncBtn.dataset.errorTimeoutId;
  }, 3000);
  
  cloudSyncBtn.dataset.errorTimeoutId = timeoutId;
}

2. Removed Unused Variable (Minor - Already Fixed ✓)

The removal of SYNC_KEYS constant is correct as it's no longer used in the code.

3. Async Operation Without Await (Low Priority)

Location: js/cloud-sync.js:395

The syncToCloud() call in visibilitychange event handler is not awaited. While this is intentional (to avoid blocking), consider whether you want to track if this sync completes:

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden' && hasPendingSync) {
    if (syncDebounceTimer) {
      clearTimeout(syncDebounceTimer);
      syncDebounceTimer = null;
    }
    syncToCloud(); // Not awaited - fire and forget
    hasPendingSync = false;
  }
});

This is likely acceptable given the unreliable nature of page unload events, but worth noting.


Security Considerations 🔒

1. XSS Protection (Checked ✓)

The code uses textContent instead of innerHTML for user data:

cloudSyncUserInfo.textContent = displayName; // Safe

This is correct and prevents XSS attacks.

2. OAuth Redirect URL Validation (Checked ✓)

The redirect URL construction is safe:

const redirectUrl = window.location.origin + window.location.pathname + window.location.search;

This uses browser-provided properties, which are safe. Supabase client should validate the redirect URL server-side.

3. localStorage Data (Existing Issue - Not in Scope)

The code reads/writes to localStorage without sanitization, but this is consistent with the existing codebase and not introduced by this PR.


Performance Considerations ⚡

1. UI Update Optimization (Improved ✓)

The PR correctly consolidates UI updates to happen once after sync completion instead of multiple times with setTimeout delays. This is a performance improvement!

Before:

setTimeout(() => {
  if (typeof window.updateProgressDropdown === 'function') {
    window.updateProgressDropdown();
  }
}, 100);

After:

// Sync completed - update UI immediately
if (typeof window.updateProgressDropdown === 'function') {
  window.updateProgressDropdown();
}

2. Debounce Logic (Existing - Good)

The 30-second debounce for cloud sync is appropriate for reducing API calls.


Test Coverage 📋

Issue: No test files found in the repository.

Recommendation: Consider adding tests for:

  1. OAuth redirect URL construction with various query parameters
  2. mergeBookmarks() logic with edge cases (empty arrays, duplicate entries, timestamp sorting)
  3. showSyncError() race condition scenarios
  4. Error handling paths in sync functions

Manual Testing Checklist for this PR:

  • Login with Google preserves query parameters (dialect, level, category)
  • Error messages appear in button tooltip and disappear after 3 seconds
  • Multiple rapid sync errors don't corrupt the original button title
  • UI updates correctly after sync completion
  • Page visibility change triggers sync when pending

Additional Observations

Documentation

Good: Added helpful comment explaining why beforeunload was removed (js/cloud-sync.js:385-386).

Code Style

Consistent: The code follows the existing style conventions in the codebase (indentation, naming, comments in Traditional Chinese).


Summary & Recommendations

Must Fix:

  • None (no blocking issues)

Should Fix:

  • Race condition in showSyncError() with originalTitle storage

Nice to Have:

  • Add test coverage for OAuth redirect and error handling
  • Consider tracking sync completion in visibilitychange handler

Overall Assessment: ✅ Approved with Minor Suggestions

The PR successfully fixes the OAuth redirect issue and improves error handling. The code quality is good, and the changes align well with user experience goals. The race condition in showSyncError() is a minor issue that should be addressed but doesn't block merging.

Great work on preserving user state through the OAuth flow! 🎉

@GJRobert GJRobert merged commit 25e7f0a into feat/cloud-sync Jan 25, 2026
2 checks passed
@GJRobert GJRobert deleted the claude/fix-oauth-redirect-K2p8t branch January 25, 2026 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants