Skip to content

Fix (Calendar) overwrite current events with new updated/inserted Google events #3079

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 12, 2025

Conversation

DennieDan
Copy link
Collaborator

@DennieDan DennieDan commented Jun 11, 2025

Summary by CodeRabbit

  • New Features
    • Improved calendar synchronization by separately tracking update times for database and Google events.
  • Refactor
    • Enhanced cache management for calendar events, providing more accurate and efficient refresh logic.
  • Style
    • Minor adjustments to improve code readability and maintainability (no user-facing impact).

@DennieDan DennieDan requested a review from vhpx June 11, 2025 14:56
@DennieDan DennieDan self-assigned this Jun 11, 2025
Copy link
Contributor

coderabbitai bot commented Jun 11, 2025

Walkthrough

This change reorganizes the order of properties within argument and return type definitions in supabase.ts for consistency and clarity, without altering their types. Additionally, in use-calendar-sync.tsx, the calendar cache now tracks separate timestamps for database and Google events, updating related logic for cache staleness, invalidation, and refresh.

Changes

File(s) Change Summary
packages/types/src/supabase.ts Reordered properties in argument and return type definitions for various database functions; no type changes.
packages/ui/src/hooks/use-calendar-sync.tsx Replaced single cache timestamp with dbLastUpdated and googleLastUpdated; updated cache logic and refresh.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CalendarSyncProvider
    participant Cache
    participant Database
    participant GoogleAPI

    User->>CalendarSyncProvider: Request events (db or Google)
    CalendarSyncProvider->>Cache: Check dbLastUpdated / googleLastUpdated
    alt Cache stale or forced
        CalendarSyncProvider->>Database: Fetch DB events
        CalendarSyncProvider->>GoogleAPI: Fetch Google events
        Database-->>CalendarSyncProvider: Return events
        GoogleAPI-->>CalendarSyncProvider: Return events
        CalendarSyncProvider->>Cache: Update dbLastUpdated / googleLastUpdated
    else Cache fresh
        CalendarSyncProvider->>Cache: Return cached events
    end
    CalendarSyncProvider-->>User: Return events
Loading

Possibly related PRs

Suggested labels

enhancement, calendar

Suggested reviewers

  • vhpx

Poem

A rabbit hopped through fields of type,
Reordering props, both left and right.
Now cache ticks for Google and DB,
Each timestamp clear as can be!
With every sync, the carrots cheer—
Consistent types and caches here!
🥕✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/types/src/supabase.ts

Oops! Something went wrong! :(

ESLint: 9.28.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@typescript-eslint/parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:801:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:725:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:708:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:309:38)
at #link (node:internal/modules/esm/module_job:201:49)

packages/ui/src/hooks/use-calendar-sync.tsx

Oops! Something went wrong! :(

ESLint: 9.28.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@typescript-eslint/parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:801:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:725:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:708:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:309:38)
at #link (node:internal/modules/esm/module_job:201:49)

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate Unit Tests
  • Create PR with Unit Tests
  • Commit Unit Tests in branch bugfix/active-sync-error
  • Post Copyable Unit Tests in Comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

graphite-app bot commented Jun 11, 2025

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (1)
packages/ui/src/hooks/use-calendar-sync.tsx (1)

564-602: 💡 Verification agent

🧩 Analysis chain

Potential race condition between sync and refresh cycles.

The syncToTuturuuu function calls refresh() after upserting (line 565), which triggers new fetches from both database and Google. Since this function is called in a useEffect when Google data changes (lines 598), there's a risk of creating a loop if the newly fetched Google data differs from what was just synced.

Consider adding a mechanism to prevent immediate re-sync after a sync operation completes.


🌐 Web query:

What are best practices for preventing sync loops in React when dealing with external data sources that may have slight variations in data between fetches?

💡 Result:

Preventing sync loops in React when dealing with external data sources that might have slight variations between fetches is crucial to avoid unnecessary re-renders or network requests. Here are best practices to handle this effectively:

  1. Deep Comparison Before State Update
    Instead of blindly setting state every time you fetch data, compare the incoming data with the current state deeply. Only update the state if there are meaningful changes. Libraries like lodash.isequal can help with deep comparison.

  2. Use Stable State Structures
    Ensure your state shape and data structure remain consistent between updates to avoid unnecessary diffs. Normalize data if needed.

  3. Debounce or Throttle Fetches
    If data fetches happen on user input or events, debounce or throttle the trigger to reduce frequency and avoid rapid update loops.

  4. Memoize Data and Callbacks
    Use useMemo and useCallback to prevent changes in dependencies that trigger effects unnecessarily.

  5. Handle Slight Variations Gracefully
    If the external data source has non-critical variations (e.g., timestamps, IDs with different sorting), transform or sanitize data before comparing or setting state.

  6. Use Effect Cleanup and Dependency Arrays Correctly
    Ensure your useEffect dependencies are accurate. Avoid putting non-stable objects or functions directly in the dependency array to prevent re-runs.

  7. Avoid State Updates in Render Phase
    Ensure state updates are only done inside effects or event handlers, not during render, to prevent infinite loops.

  8. Use a Persistent Reference for External Data
    Store the last received data in a useRef and compare new data against it before updating state.

  9. Consider Immutable Data Structures
    Using immutable.js or similar can help detect real changes more easily.

  10. Implement Retry or Back-off Logic
    If fetches are automatic, implement back-off strategies to reduce load and avoid frequent updates.


Would you like a code example demonstrating how to implement deep comparison before updating state or any other specific pattern?


Prevent infinite sync loops between syncToTuturuuu and refresh()

The current flow in packages/ui/src/hooks/use-calendar-sync.tsx (lines 564–602) is prone to a loop:

  1. syncToTuturuuu() upserts events then calls refresh().
  2. refresh() invalidates queries, which may refetch Google data with slight diffs.
  3. The useEffect on fetchedGoogleData sees “changed” data and calls syncToTuturuuu() again.

To break this cycle, I recommend:

• Guard against self-triggered refreshes
– Create a useRef flag (e.g. isRefreshTriggeredRef) and set it to true just before calling refresh().
– In your useEffect, if that flag is set, reset it and skip calling syncToTuturuuu().

• Deep-compare only meaningful data
– Normalize or sanitize fetchedGoogleData (strip timestamps, sort arrays) before stringifying or use lodash.isequal to avoid spurious diffs.

• (Optional) Debounce/throttle rapid changes
– If Google data changes frequently, debounce the useEffect trigger.

• Cleanup dependency array
– Since useQueryClient() returns a stable instance, you can safely remove queryClient from the useCallback deps for clarity.

Example snippet:

 // at top of hook
+ const isRefreshTriggeredRef = useRef(false);

 const syncToTuturuuu = useCallback(async () => {
   try {
     setIsSyncing(true);
+    isRefreshTriggeredRef.current = true;
     // …upsert logic…
     refresh();
 useEffect(() => {
   if (experimentalGoogleToken?.ws_id !== wsId) return;

+  if (isRefreshTriggeredRef.current) {
+    isRefreshTriggeredRef.current = false;
+    return;
+  }

   const currentGoogleDataStr = JSON.stringify(normalize(fetchedGoogleData));
   if (currentGoogleDataStr !== prevGoogleDataRef.current) {
     syncToTuturuuu();
     prevGoogleDataRef.current = currentGoogleDataStr;
   }
 }, [fetchedGoogleData, syncToTuturuuu]);

These changes ensure that a refresh-driven refetch doesn’t immediately retrigger a sync.

🧹 Nitpick comments (2)
packages/ui/src/hooks/use-calendar-sync.tsx (2)

763-781: Consider the asynchronous nature of state updates in debug logging.

The debug logging is helpful, but be aware that the "After refresh" log (lines 776-781) might not show the updated values immediately since updateCache uses setState which is asynchronous. The logged values will still reference the old cacheData object.

If you need to verify the actual updated state, consider moving the "After refresh" logging into a separate useEffect that watches the calendarCache state:

-    console.log('After refresh - cacheData:', {
-      isForced: cacheData?.isForced,
-      dbLastUpdated: cacheData?.dbLastUpdated,
-      googleLastUpdated: cacheData?.googleLastUpdated,
-      dbEventsLength: cacheData?.dbEvents?.length,
-    });

Add a separate effect for debugging:

useEffect(() => {
  const cacheKey = getCacheKey(dates);
  const cacheData = calendarCache[cacheKey];
  console.log('Cache state updated:', {
    cacheKey,
    isForced: cacheData?.isForced,
    dbLastUpdated: cacheData?.dbLastUpdated,
    googleLastUpdated: cacheData?.googleLastUpdated,
    dbEventsLength: cacheData?.dbEvents?.length,
  });
}, [calendarCache, dates]);

213-213: Consider implementing a proper logging solution for production.

The code contains numerous console.log statements for debugging. While helpful during development, these should be removed or replaced with a configurable logging solution before deploying to production.

Consider using a logging library or a debug flag:

const DEBUG = process.env.NODE_ENV === 'development';
const log = (...args) => DEBUG && console.log(...args);

Also applies to: 280-280, 371-379, 415-423, 449-449, 485-509, 537-544, 561-561, 597-597, 616-616, 758-758, 763-781

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89adb14 and f88a2e6.

📒 Files selected for processing (2)
  • packages/types/src/supabase.ts (20 hunks)
  • packages/ui/src/hooks/use-calendar-sync.tsx (14 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Deploy-Preview
🔇 Additional comments (32)
packages/types/src/supabase.ts (28)

7349-7358: Consistent property ordering for generate_cross_app_token overloads
Both overloads now list p_origin_app, p_target_app, p_expiry_seconds, and p_session_data in the same sequence. Types unchanged, only ordering for clarity.


7372-7376: Align return fields in get_daily_income_expense
total_expense has been moved to follow total_income, matching the convention used elsewhere. No type modifications.


7406-7412: Standardize return order in get_hourly_prompt_completion_tokens
The hour field is positioned before the token totals, consistent with similar metrics definitions.


7423-7427: Reorder Args for get_inventory_products
The parameters _ws_id, _warehouse_ids, and _has_unit are now listed immediately after _category_ids to maintain a predictable grouping.


7429-7437: Organize return fields in get_inventory_products
Return properties (id, name, manufacturer, unit, unit_id, category, price, amount) are reordered for consistency and easier scanning.


7460-7464: Maintain symmetry in get_monthly_income_expense returns
total_expense is now placed after total_income, mirroring the daily version.


7466-7472: Align return order in get_monthly_prompt_completion_tokens
total_prompt_tokens follows month, consistent with other prompt/completion token endpoints.


7498-7504: Consistent fields ordering for get_session_statistics
active_count and completed_count follow unique_users_count, matching the overall statistics pattern.


7514-7519: Reorder return fields in get_session_templates
category_name is now listed right after tags, aligning with the order of related category properties.


7528-7532: Align return properties in get_submission_statistics
unique_users_count moved after latest_submission_date to mirror other stats endpoints.


7534-7544: Organize return type in get_transaction_categories_with_amount
The fields ws_id, created_at, and amount are reordered to follow the ID/name pair, improving readability.


7549-7555: Standardize fields in get_user_session_stats
active_sessions now comes immediately after total_sessions, matching the naming sequence of related stats calls.


7571-7579: Consistent return ordering for get_user_tasks
The sequence (name, description, priority, completed, start_date, end_date, board_id) has been aligned to group related properties.


7591-7596: Reorder Args in get_workspace_transactions_count
start_date and end_date now follow ws_id, reflecting their optional nature after the required parameter.


7599-7603: Standardize Args in get_workspace_user_groups
search_query is now placed after the included/excluded tag lists to keep filters grouped.


7607-7613: Align return fields in get_workspace_user_groups
created_at moved to follow tag_count, matching the layout used in other list endpoints.


7619-7624: Organize Args in get_workspace_users
included_groups, excluded_groups, and search_query are ordered consistently with other workspace list calls.


7626-7647: Consistent field sequence in get_workspace_users return type
Demographic and workspace fields (full_name, display_name, email, etc.) are now arranged in a logical, grouped order.


7654-7659: Reorder Args in get_workspace_wallets_expense
Optional start_date and end_date are placed after the required ws_id to clarify parameter importance.


7657-7663: Reorder Args in get_workspace_wallets_income
Mirrors the expense call: optional dates follow ws_id.


7662-7667: Standardize Args in has_other_owner
Arguments _ws_id and _user_id are grouped in declaration order for consistency.


7666-7673: Align Args in insert_ai_chat_message
chat_id and source now follow message, grouping chat-specific parameters.


7746-7753: Consistent overload definitions in search_users
Both overloads now list filters (role_filter, enabled_filter) at the end, maintaining symmetry in paging parameters.


7780-7786: Reorder return fields in search_users_by_name
handle precedes display_name and relevance, aligning with identification-first grouping.


7792-7796: Standardize Args for transactions_have_same_abs_amount
Both transaction IDs are declared in a consistent left-to-right order.


7718-7723: Align Args in nova_get_challenge_with_user_stats
challenge_id is listed before user_id, matching other Nova endpoints.


7722-7727: Standardize Args in nova_get_user_daily_sessions
Arguments reflect the same order as related Nova calls (challenge_id, then user_id).


7803-7808: Consistent Args in update_session_total_score
The parameters challenge_id_param and user_id_param are sequenced for clarity.

packages/ui/src/hooks/use-calendar-sync.tsx (4)

81-89: Good separation of concerns for cache timestamps!

Splitting the single lastUpdated into separate dbLastUpdated and googleLastUpdated timestamps allows for more granular cache management and staleness checking for each data source independently.


167-169: Improved cache freshness for current week events.

The reduction of stale time from 2 minutes to 30 seconds for the current week ensures more timely updates. The inclusive condition (>=) is also more precise.


213-250: Correct implementation of database-specific cache management.

The changes properly utilize the new dbLastUpdated timestamp for staleness checks and cache updates. The debug logging and isForced flag reset are good additions for maintainability.


548-565: Important: Changed sync behavior to ensure data consistency.

The function now triggers a full refresh after upserting events instead of directly updating the cache. This ensures the displayed data accurately reflects the database state after synchronization, which aligns with the PR objective to fix event overwriting issues. While this approach may be slightly less efficient, it provides better data consistency guarantees.

Copy link

codecov bot commented Jun 11, 2025

Codecov Report

Attention: Patch coverage is 0% with 35 lines in your changes missing coverage. Please review.

Project coverage is 0.84%. Comparing base (89adb14) to head (f88a2e6).

Files with missing lines Patch % Lines
packages/ui/src/hooks/use-calendar-sync.tsx 0.00% 35 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3079      +/-   ##
==========================================
- Coverage    0.84%    0.84%   -0.01%     
==========================================
  Files        2488     2488              
  Lines      306768   306773       +5     
  Branches     2990     2990              
==========================================
  Hits         2577     2577              
- Misses     302063   302068       +5     
  Partials     2128     2128              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Member

@vhpx vhpx left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks @DennieDan!

@vhpx vhpx merged commit 92ca2f7 into main Jun 12, 2025
19 checks passed
@vhpx vhpx deleted the bugfix/active-sync-error branch June 12, 2025 03:00
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