Skip to content

[CLEAN] Synthetic Benchmark PR #26931 - feat(companion): add optimistic updates to availability edit screens#669

Open
tomerqodo wants to merge 8 commits intobase_pr_26931_20260121_4267from
clean_pr_26931_20260121_4267
Open

[CLEAN] Synthetic Benchmark PR #26931 - feat(companion): add optimistic updates to availability edit screens#669
tomerqodo wants to merge 8 commits intobase_pr_26931_20260121_4267from
clean_pr_26931_20260121_4267

Conversation

@tomerqodo
Copy link

Benchmark PR calcom#26931

Type: Clean (correct implementation)

Original PR Title: feat(companion): add optimistic updates to availability edit screens
Original PR Description:

Screen.Recording.2026-01-16.at.6.56.47.PM.mov

Fixes calcom#26908
Fixes CAL-7077

What does this PR do?

Implements optimistic updates for availability edit screens in the companion app (iOS, Android, browser extension). This is part of a larger refactoring effort to add auto-refresh/reload after save actions.

Phase 1 - EditAvailabilityNameScreen:

  • Enhanced useUpdateSchedule hook with full optimistic update support (onMutate, onSuccess, onError)
  • Refactored EditAvailabilityNameScreen.tsx and EditAvailabilityNameScreen.ios.tsx to use the mutation hook instead of direct API calls
  • Cache is now updated immediately on save, providing instant UI feedback
  • On error, cache automatically rolls back to previous state

Phase 2 - EditAvailabilityOverrideScreen:

  • Refactored EditAvailabilityOverrideScreen.tsx and EditAvailabilityOverrideScreen.ios.tsx to use useUpdateSchedule mutation hook
  • Replaced direct CalComAPIService.updateSchedule calls with the mutation hook
  • Removed local isSaving state in favor of isPending from the mutation hook
  • Override changes now update the cache automatically via optimistic updates

Phase 3 - EditAvailabilityDayScreen:

  • Refactored EditAvailabilityDayScreen.tsx and EditAvailabilityDayScreen.ios.tsx to use useUpdateSchedule mutation hook
  • Replaced direct CalComAPIService.updateSchedule calls with the mutation hook
  • Removed local isSaving state in favor of isPending from the mutation hook
  • Day availability changes now update the cache automatically via optimistic updates

Supporting Changes (by @dhairyashiil):

  • Refactored AvailabilityDetailScreen.tsx and AvailabilityDetailScreen.ios.tsx to use useScheduleById hook instead of direct API calls
  • This ensures the detail screen reads from React Query cache, enabling cache updates to propagate correctly
  • Added RefreshControl for pull-to-refresh support
  • Replaced direct API mutations with useSetScheduleAsDefault and useDeleteSchedule hooks

Updates since last revision

Fixed stale data issue on the Working Hours page (page 2 in the edit flow):

  • Refactored edit-availability-hours.tsx and edit-availability-hours.ios.tsx route files to use useScheduleById hook
  • Previously, these routes used direct CalComAPIService.getScheduleById() calls with local useState, bypassing the React Query cache
  • Now all 3 pages in the availability edit flow (detail → working hours → day edit) read from the same cache, ensuring updates propagate correctly

Fixed Date Overrides section not clickable when empty:

  • Previously, when there were no date overrides, the section was a plain View without any press handler
  • Users couldn't navigate to the edit override page to add new overrides
  • Now the "No Overrides" section is wrapped in AppPressable with navigation handler and chevron icon
  • Follows the same pattern as other sections (Weekly Schedule, Timezone) which are always clickable

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. N/A - internal companion app changes only.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

Phase 1 - Edit Name/Timezone:

  1. Open the companion app (iOS/Android/browser extension)
  2. Navigate to Availability settings
  3. Edit a schedule's name or timezone
  4. Save the changes
  5. Navigate back to the availability list
  6. Expected: List should show updated name/timezone immediately without manual refresh

Phase 2 - Edit Overrides:

  1. Navigate to a schedule's detail page
  2. Add, edit, or delete a date override
  3. Save the changes
  4. Navigate back to the detail/list page
  5. Expected: Changes should reflect immediately without manual refresh

Phase 3 - Edit Day Availability:

  1. Navigate to a schedule's detail page
  2. Tap "Edit Hours" to open the Working Hours page
  3. Tap a specific day (e.g., Monday)
  4. Toggle availability on/off or modify time slots
  5. Save and navigate back
  6. Expected: Working Hours page AND detail page should reflect changes immediately without manual refresh

Date Overrides Empty State:

  1. Navigate to a schedule that has NO date overrides
  2. Tap on the "Date Overrides" section (should show "No date overrides set")
  3. Expected: Should navigate to the edit override page where you can add new overrides

Error case: If network fails, UI should roll back to previous state and show error alert

Checklist

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • I have checked if my changes generate no new warnings

Human Review Checklist

  • Verify AvailabilityDetailScreen properly reads from React Query cache via useScheduleById (critical for cache updates to propagate)
  • Verify edit-availability-hours route files properly read from React Query cache via useScheduleById
  • Verify the optimistic update logic in useUpdateSchedule handles availability field correctly via spread operator
  • Confirm the rollback in onError restores both detail and list caches correctly
  • Verify Date Overrides section is clickable when empty (shows chevron, navigates to edit page)
  • Test on iOS, Android, and browser extension
  • Note: EventTypeDetail screen will be refactored in a subsequent phase

Link to Devin run: https://app.devin.ai/sessions/0e35f5cdab8943dabb66fc182e4241fe
Requested by: Dhairyashil Shinde (@dhairyashiil)
Original PR URL: calcom#26931

- Refactor useUpdateSchedule hook with optimistic updates
- Update EditAvailabilityNameScreen to use mutation hook instead of direct API call
- Update EditAvailabilityNameScreen.ios.tsx with same pattern
- Cache is updated immediately on save, then synced with server
- On error, cache is rolled back to previous state
…is empty

- Update list cache in onMutate even when detail cache doesn't exist
- Remove onSettled invalidation that was causing issues with staleTime: Infinity
- Add fallback invalidation in onSuccess when list cache doesn't exist
…ates

The previous optimistic updates implementation in EditAvailabilityNameScreen
correctly updated the React Query cache, but AvailabilityDetailScreen was
still using direct CalComAPIService.getScheduleById() calls with local
useState, completely bypassing the cache.

The disconnect:
- EditAvailabilityNameScreen → useUpdateSchedule → Updates React Query Cache ✓
- AvailabilityDetailScreen → CalComAPIService.getScheduleById → Local state ✗

This meant when a user saved changes to a schedule's name/timezone, the
cache was updated but the detail screen (and subsequently the list screen)
never saw those updates because they weren't reading from the cache.

Changes:
- Refactor AvailabilityDetailScreen.tsx to use useScheduleById hook
- Refactor AvailabilityDetailScreen.ios.tsx to use useScheduleById hook
- Replace direct API calls with React Query for cache synchronization
- Add RefreshControl for pull-to-refresh support
- Use mutation hooks (useSetScheduleAsDefault, useDeleteSchedule) for actions
- Derive availability/overrides data using useMemo from query result

Now when EditAvailabilityNameScreen updates the cache, AvailabilityDetailScreen
automatically reflects those changes because both read from the same cache.
…ion hook

- Replace direct CalComAPIService.updateSchedule calls with useUpdateSchedule hook
- Remove local isSaving state, use isPending from mutation hook instead
- Cache is now updated automatically via the mutation hook's optimistic updates
- Consistent pattern with EditAvailabilityNameScreen refactoring
- Replace direct CalComAPIService.updateSchedule calls with useUpdateSchedule hook
- Remove local isSaving state, use isPending from mutation hook instead
- Cache is now updated automatically via the mutation hook's optimistic updates
- Consistent pattern with EditAvailabilityNameScreen and EditAvailabilityOverrideScreen
…eduleById hook

The working hours page (page 2 in the flow) was showing stale data because
it used direct CalComAPIService.getScheduleById() calls with local useState,
bypassing the React Query cache.

The disconnect:
- EditAvailabilityDayScreen → useUpdateSchedule → Updates React Query Cache ✓
- edit-availability-hours route → CalComAPIService.getScheduleById → Local state ✗

This meant when a user saved changes to a day's availability, the cache was
updated but the working hours page never saw those updates because it wasn't
reading from the cache.

Changes:
- Refactor edit-availability-hours.tsx to use useScheduleById hook
- Refactor edit-availability-hours.ios.tsx to use useScheduleById hook
- Replace direct API calls with React Query for cache synchronization
- Now all 3 pages in the flow read from the same cache
Previously, when there were no date overrides, the Date Overrides section
was just a plain View without any press handler, making it impossible for
users to navigate to the edit override page to add new overrides.

This follows the same pattern as other sections (Weekly Schedule, Timezone)
which are always clickable regardless of their content state.

Changes:
- Wrap the 'No Overrides' section in AppPressable with navigation handler
- Add chevron-forward icon to indicate it's tappable
- Apply fix to both AvailabilityDetailScreen.tsx and .ios.tsx
- Change useMemo to useEffect for onActionsReady callback (side effect)
- Move error handling (showErrorAlert, router.back) to useEffect
- Keep early return for error state after useEffect hooks

Fixes Cubic AI review feedback (confidence 9/10)
@github-actions
Copy link

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

No release type found in pull request title "[CLEAN] Synthetic Benchmark PR #26931 - feat(companion): add optimistic updates to availability edit screens". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@github-actions
Copy link

This PR has been marked as stale due to inactivity. If you're still working on it or need any help, please let us know or update the PR to keep it active.

@github-actions github-actions bot added the Stale label Feb 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants