Skip to content

Changes :#40

Merged
aryansoni-dev merged 1 commit into
mainfrom
dev
Jun 22, 2026
Merged

Changes :#40
aryansoni-dev merged 1 commit into
mainfrom
dev

Conversation

@aryansoni-dev

@aryansoni-dev aryansoni-dev commented Jun 22, 2026

Copy link
Copy Markdown
Owner

[+] Implemented Reliable Global Error Handling, Offline Awareness, and Sync Status Clarity.
[+] Implemented code-rabbit's suggested bug fixes and improvements.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added offline/online connectivity detection with visual indicators across the app.
    • Implemented feature-level error boundaries for graceful failure recovery.
    • Added data sync status visibility showing last sync time and pending changes.
  • Bug Fixes

    • Fixed account deletion link navigation from settings.
    • Improved social sign-in error messaging based on provider and authentication mode.
    • Enhanced AI Chat and Reflection features to block operations when offline.
  • Improvements

    • Better error messages throughout the app with retry functionality.
    • Improved automatic data synchronization with retry logic.
    • Clearer offline notifications for features requiring internet connection.

  [+] Implemented Reliable Global Error Handling, Offline Awareness, and Sync Status Clarity.
  [+] Implemented code-rabbit's suggested bug fixes and improvements.
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces a full connectivity and error-handling layer: a ConnectivityProvider using NetInfo, structured AppError types, normalizeAppError/reportAppError utilities, and FeatureErrorBoundary/RootErrorFallback components. Sync is centralized into a new requestSync orchestrator with per-user deduplication, while AutoSyncManager gains reconnect/retry effects. All major screens (AI Chat, Journal Editor, AI Insight, Profile) integrate offline gating and error boundaries. Account deletion gains request timeouts and cleanup-error propagation.

Changes

Connectivity, Error Handling, and Sync System

Layer / File(s) Summary
Core type contracts: errors, connectivity, sync
types/appError.ts, types/connectivity.ts, types/syncResult.ts, types/syncStatus.ts
Defines AppErrorCategory, AppErrorSeverity, AppErrorCode, AppError, ConnectivityStatus, ConnectivityState, SyncResult discriminated union, UserSyncStatus, and SyncStatusSnapshot.
Error normalization, reporting, and boundary components
lib/errors/normalizeAppError.ts, lib/errors/reportAppError.ts, components/errors/FeatureErrorBoundary.tsx, components/errors/InlineErrorMessage.tsx, components/errors/RootErrorFallback.tsx, app/_layout.tsx
normalizeAppError maps unknown errors to typed AppError; reportAppError dev-logs with context; FeatureErrorBoundary catches render errors and shows a retry fallback; InlineErrorMessage shows retryable inline alerts; RootErrorFallback navigates to home; root layout exports ErrorBoundary and wraps with ConnectivityProvider.
Connectivity provider, hook, and offline notice
package.json, providers/ConnectivityProvider.tsx, hooks/useConnectivity.ts, components/connectivity/OfflineNotice.tsx
Adds @react-native-community/netinfo; ConnectivityProvider debounces offline state and supplies ConnectivityContext; useConnectivity reads the context; OfflineNotice renders a Wi-Fi-off card.
Sync store extension and requestSync orchestrator
store/useSyncStore.ts, lib/sync/requestSync.ts
useSyncStore adds lastAttemptAt, lastSyncErrorCode, updated setSyncAttempt/setSyncFailure (now takes AppErrorCode), and persistence sanitization. requestSync deduplicates per-user, runs preflight checks, then sequences profile → journal two-way sync → achievements with error normalization and journal failure marking.
Sync status computation and UI
hooks/useSyncStatus.ts, components/sync/SyncStatusIndicator.tsx, components/sync/SyncStatusRow.tsx
useSyncStatus derives SyncStatusSnapshot from store/connectivity/deletion state. SyncStatusIndicator renders a themed pill per UserSyncStatus. SyncStatusRow shows title, description, last-synced/pending details, and a conditional retry button.
AutoSyncManager reconnect/retry and useAutoSync refactor
components/sync/auto-sync-manager.tsx, hooks/useAutoSync.ts
AutoSyncManager adds reconnect-after-online and exponential-retry effects; broadens pending-entry detection to all non-synced entries. useAutoSync delegates to requestSync, extends AutoSyncReason with reconnect/retry, and blocks when offline.
Offline gating and error boundaries in screens
components/ai-chat/ai-chat-screen.tsx, app/(tabs)/ai-chat/index.tsx, components/journal-editor/journal-editor-screen.tsx, app/insights/report/[periodType].tsx, hooks/useAIInsightReport.ts, components/profile/profile-screen.tsx
AI Chat adds offline pill, send-block dialog, and OfflineNotice card; Journal Editor gates save/reflection on connectivity, wraps reflection card in FeatureErrorBoundary, and adds save-fail error handling. AI Insight Report wraps content in FeatureErrorBoundary and blocks refresh/generation when offline. Profile replaces manual sync with requestSync, adds SyncStatusRow, gates on offline/isManualSyncing, and clears sync state on sign-out.

Account Deletion Hardening and Backend Fixes

Layer / File(s) Summary
Account deletion service: timeouts and cleanup propagation
lib/account/accountDeletionService.ts, lib/account/clearLocalUserData.ts
Adds 30s AbortController timeout on the remote delete call; wraps clearLocalUserData in try/catch returning failLocalCleanupAfterRemoteDeletion on failure; adds isAbortError helper; clearLocalUserData now rethrows unexpected notification-cleanup errors.
Supabase edge function: paginated storage and Clerk timeout
supabase/functions/delete-account/index.ts, supabase/config.toml
deleteStoragePrefix refactored to paginate storage listing and batch-remove files; deleteClerkUser enforces a 10s AbortController timeout; verify_jwt enabled in config.
Privacy navigation fix, auth provider label, legal screen styling
app/settings/privacy.tsx, components/auth/auth-screen.tsx, components/legal/legal-document-screen.tsx
Fixes privacy settings routing to accountDeletionUrl; auth screen social sign-in error now uses a dynamic provider/mode label via getSocialProviderLabel; LegalDocumentScreen centralizes styling into local constants.

Sequence Diagram(s)

sequenceDiagram
  participant NetInfo
  participant ConnectivityProvider
  participant useConnectivity
  participant AiChatScreen
  participant requestSync
  participant useSyncStore

  NetInfo->>ConnectivityProvider: onNetInfoChange(state)
  ConnectivityProvider->>ConnectivityProvider: debounce offline confirmation (offlineConfirmationMs)
  ConnectivityProvider-->>useConnectivity: ConnectivityState {status}

  AiChatScreen->>useConnectivity: connectivity.status
  AiChatScreen-->>AiChatScreen: isOffline → block send, show OfflineNotice

  Note over AiChatScreen,requestSync: When device comes back online
  ConnectivityProvider-->>AutoSyncManager: connectivity.status = "online"
  AutoSyncManager->>requestSync: requestSync({reason:"reconnect", userId, ...})
  requestSync->>useSyncStore: setSyncAttempt(timestamp, userId)
  requestSync->>requestSync: profile → journal → achievements
  requestSync->>useSyncStore: setSyncSuccess or setSyncFailure(errorCode)
  useSyncStore-->>SyncStatusRow: SyncStatusSnapshot updated
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • aryansoni-dev/dear-diary#28: Directly overlaps with this PR's auto-sync-manager.tsx, useAutoSync.ts, and useSyncStore.ts changes—the retrieved PR established the sync pipeline that this PR refactors with connectivity gating and requestSync delegation.
  • aryansoni-dev/dear-diary#39: Both PRs modify accountDeletionService.ts, clearLocalUserData.ts, profile-screen.tsx, supabase/functions/delete-account/index.ts, and app/settings/privacy.tsx—the retrieved PR introduced the deletion flow that this PR hardens with timeouts and cleanup-error propagation.
  • aryansoni-dev/dear-diary#30: Both PRs modify handleSendMessage in ai-chat-screen.tsx—the retrieved PR refactored request handling while this PR adds offline gating at the same entry point.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Changes :' is vague and generic, providing no meaningful information about the primary changes in the changeset despite the PR introducing significant features like error handling, offline awareness, and sync status clarity. Replace the title with a clear, specific description of the main changes, such as 'Add error handling, offline detection, and sync status features' or 'Implement global error boundaries, connectivity awareness, and sync status indicators'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
supabase/functions/delete-account/index.ts (1)

335-388: 🧹 Nitpick | 🔵 Trivial | ⚖️ Poor tradeoff

Consider batching large file deletions for resilience.

The function accumulates all files across all pages into filesToRemove before calling remove() once. For users with very large storage (thousands of files), this could cause issues with payload size limits or request timeouts on the bulk delete call.

Consider batching deletions in chunks (e.g., 1000 files per call) to improve reliability for edge cases:

♻️ Suggested batching approach
+const REMOVE_BATCH_SIZE = 1000;
+
 async function deleteStoragePrefix(
   client: ReturnType<typeof createClient>,
   bucket: string,
   userId: string,
   path: string,
 ): Promise<{ ok: true } | { ok: false }> {
   const prefix = path ? `${userId}/${path}` : userId;
   const filesToRemove: string[] = [];
   const nestedPaths: string[] = [];
   let offset = 0;

   while (true) {
     // ... pagination logic unchanged ...

     for (const item of items) {
       const itemPath = path ? `${path}/${item.name}` : item.name;

       if (item.id === null) {
         nestedPaths.push(itemPath);
         continue;
       }

       filesToRemove.push(`${userId}/${itemPath}`);
     }

+    // Batch delete when threshold reached
+    if (filesToRemove.length >= REMOVE_BATCH_SIZE) {
+      const removeResult = await client.storage.from(bucket).remove(filesToRemove);
+      if (removeResult.error) {
+        return { ok: false };
+      }
+      filesToRemove.length = 0;
+    }

     if (items.length < storageListPageLimit) {
       break;
     }

     offset += storageListPageLimit;
   }

   // ... rest unchanged, handles remaining files ...
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@supabase/functions/delete-account/index.ts` around lines 335 - 388, The
current implementation accumulates all files in the `filesToRemove` array before
calling `client.storage.from(bucket).remove()` once with the entire batch, which
can cause payload size or timeout issues for large deletions. Instead of the
single `removeResult` call at the end, implement a batching approach that
processes `filesToRemove` in chunks (e.g., 1000 files per batch). Loop through
the accumulated files, slicing them into smaller batches and calling the remove
operation for each batch separately, ensuring all batches complete successfully
before returning the final result.
hooks/useAutoSync.ts (1)

124-126: ⚠️ Potential issue | 🟡 Minor

Remove unused startOfLocalDay helper function from hooks/useAutoSync.ts.

The function at line 124 is not called anywhere within this file. Since the PR removed its consumers (getReflectionStreak and getLocalDateKey), this helper is now dead code and should be deleted.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hooks/useAutoSync.ts` around lines 124 - 126, The startOfLocalDay helper
function is no longer being used in the file since its consumers
(getReflectionStreak and getLocalDateKey) were removed in this PR. Delete the
entire startOfLocalDay function definition to remove the dead code and keep the
file clean.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/ai-chat/ai-chat-screen.tsx`:
- Around line 351-363: Extract the hardcoded hex color values used for the
online/offline connection state indicator in the ai-chat-screen component into
shared color tokens or constants. The colors being repeated are the background
colors (for the View with the rounded-full styling), the indicator dot colors,
and the text colors for both offline and online states. Create centralized
constants (such as CONNECTION_STATE_COLORS with offline and online properties)
and replace all hardcoded hex values with references to these constants. This
includes the backgroundColor properties in the View components and the color
properties in the Text component, as well as any similar color usage mentioned
in lines 400-407.

In `@components/connectivity/OfflineNotice.tsx`:
- Around line 6-10: The OfflineNotice component contains multiple hardcoded
color values (bg-[`#FFF7ED`], bg-white, color="`#C2410C`", text-[`#9A3412`]) that are
scattered across the View and Text elements as well as the WifiOff icon color
prop. Extract these hardcoded colors along with their related typography tokens
into shared constants or theme classes in your design system. Replace each
hardcoded value in the OfflineNotice component's className attributes and the
WifiOff color prop with references to these shared constants to ensure
consistent styling across the application.

In `@components/errors/FeatureErrorBoundary.tsx`:
- Around line 42-43: The FeatureErrorBoundary component contains hardcoded color
values (bg-[`#FFF1F5`] and text-[`#9F1239`]) in the Tailwind classes that are
repeated across multiple error components including InlineErrorMessage and
RootErrorFallback. Extract these literal color values into shared design token
constants (either in a constants file or global styles configuration) with
semantic names that describe their purpose (e.g., errorBackgroundColor,
errorTextColor), then replace all occurrences of the hardcoded hex colors in
FeatureErrorBoundary, InlineErrorMessage, and RootErrorFallback with references
to these shared tokens to maintain consistency and simplify future updates.
- Around line 50-63: The Retry button in the FeatureErrorBoundary component is
conditionally rendered only when onRetry is provided, leaving users with no
recovery path when this prop is absent. Remove the conditional check around the
Pressable component so the button is always rendered. Keep the existing onPress
logic that resets the error state with this.setState({ error: null }) and calls
this.props.onRetry?.() which will safely handle cases where onRetry is not
provided through optional chaining. This ensures users always have a local
recovery action available, even when the boundary is used without an onRetry
handler.

In `@components/journal-editor/journal-editor-screen.tsx`:
- Around line 829-835: The conditional logic for determining the save label has
incorrect precedence. When checking `entrySyncStatus === "synced"` second, it
can incorrectly return "Synced" even when there are unsaved edits (wasSaved is
false) on a previously synced entry. Reorder the conditions so that the check
for unsaved edits takes precedence. The `entrySyncStatus === "synced"` condition
should only be evaluated and returned after confirming that all changes have
been saved (wasSaved is true). This ensures that "Save" is returned whenever
there are unsaved edits, regardless of the current sync status.

In `@components/legal/legal-document-screen.tsx`:
- Around line 18-48: The constants legalDocumentColors, legalDocumentSpacing,
and legalDocumentLayout define inline style values that should be moved to the
Tailwind theme configuration instead. Add these color and spacing values to your
Tailwind config under the appropriate theme sections (extend colors and
spacing), then replace usage of these constants with NativeWind className
utilities (e.g., bg-legal-card, text-legal-heading) instead of style props. The
shadow constants legalDocumentCardShadow and legalDocumentIconShadow can remain
as inline styles per the guidelines exception for shadows.

In `@components/profile/profile-screen.tsx`:
- Around line 147-154: The setSupabaseAccessTokenProvider(null) call is being
executed before the signOut() operation completes. If signOut() fails and throws
an exception caught by the catch block, the token provider will already be
nulled, breaking subsequent authenticated requests. Move the
setSupabaseAccessTokenProvider(null) call into the try block after the await
signOut() call succeeds so that the token is only cleared when sign-out is
guaranteed to succeed. This ensures that if the operation fails, the token
provider remains intact and the user can attempt authentication again.

In `@components/sync/auto-sync-manager.tsx`:
- Around line 125-160: The `retryAttemptRef.current += 1` statement executes
synchronously when the effect runs, but if the effect re-runs before the timeout
fires (due to dependency changes like connectivity status or pending changes),
the cleanup cancels the timeout while the counter has already been incremented.
Move the increment statement from before the setTimeout call into the callback
function passed to setTimeout, so that the retry counter only increments when
the retry actually executes rather than when the effect re-runs.

In `@components/sync/SyncStatusIndicator.tsx`:
- Around line 18-52: The statusTheme object contains hardcoded hex color values
that are repeated across the sync UI components, creating maintenance and
consistency risks. Extract all backgroundColor and color hex values from the
statusTheme object (for the failed, idle, paused, saved_locally, synced,
syncing, and waiting_for_network status states) into centralized design token
constants in a shared location (such as a design tokens file or constants
directory). Then replace each hardcoded hex value in statusTheme with references
to the corresponding constants to ensure consistency and reduce drift across
sync UI components.

In `@components/sync/SyncStatusRow.tsx`:
- Around line 22-31: The SyncStatusRow component hardcodes multiple color values
throughout its styling, including `#27272A`, `#71717B`, and `#FF2056` (mentioned at
lines 22-31, 58-59, and 75-79). Extract all these hardcoded color literals into
shared color constants or global style definitions, then replace each hardcoded
color value in the component with references to the appropriate constant to
maintain consistency across sync surfaces and follow the coding guidelines for
reusable design tokens.
- Around line 55-65: The accessibilityLabel prop on the button element is static
and always set to copy.retryLabel, but screen readers should announce the actual
state of the button when isRetrying is true. Update the accessibilityLabel to be
conditional so that it displays "Syncing..." when isRetrying is true and
copy.retryLabel when isRetrying is false, matching the visual text content
changes already implemented in the Text component.

In `@lib/sync/requestSync.ts`:
- Around line 105-113: In the block where isSupabaseConfigured is false, the
retryable field in the returned object is currently set to true, but
configuration errors cannot be resolved by retrying. Change the retryable
property from true to false in the return statement within the
!isSupabaseConfigured condition to properly reflect that this is a configuration
or environment issue that requires manual intervention, not a transient failure
that can be automatically retried.
- Around line 199-211: The filter operation has a performance issue where
pendingEntryIds.includes(entry.id) performs an O(n) lookup for each entry being
filtered, resulting in O(n×m) complexity. To fix this, convert pendingEntryIds
to a Set before the filter operation (store it in a new variable like
pendingEntryIdsSet), then use the Set.has() method instead of Array.includes()
within the filter predicate to achieve O(1) lookup time per entry.

In `@store/useSyncStore.ts`:
- Around line 183-187: The isRecord type guard function is duplicated in
multiple files including useSyncStore.ts, lib/errors/normalizeAppError.ts,
lib/account/accountDeletionService.ts, and
supabase/functions/delete-account/index.ts. Create a new shared utility file at
lib/utils/typeGuards.ts and move the isRecord function definition there, then
update all four files to import and use the isRecord function from this
centralized utility instead of maintaining their own local copies.
- Around line 189-202: The hardcoded appErrorCodes array in useSyncStore.ts can
become out of sync with the AppErrorCode type definition in types/appError.ts.
Move the appErrorCodes array to types/appError.ts and use a const assertion on
it, then derive the AppErrorCode type from the array using (typeof
appErrorCodes)[number] pattern. This ensures TypeScript will verify the type and
array stay synchronized. Finally, import appErrorCodes from types/appError.ts in
useSyncStore.ts instead of maintaining a duplicate array declaration.

In `@types/appError.ts`:
- Around line 16-28: The AppErrorCode union type contains two error codes,
sync_conflict and invalid_data, that are never actually used or produced
anywhere in the codebase. Remove these two unused string literals from the
AppErrorCode type definition to keep the union type accurate and maintainable.
Verify after removal that no other code references these specific error code
strings.

---

Outside diff comments:
In `@hooks/useAutoSync.ts`:
- Around line 124-126: The startOfLocalDay helper function is no longer being
used in the file since its consumers (getReflectionStreak and getLocalDateKey)
were removed in this PR. Delete the entire startOfLocalDay function definition
to remove the dead code and keep the file clean.

In `@supabase/functions/delete-account/index.ts`:
- Around line 335-388: The current implementation accumulates all files in the
`filesToRemove` array before calling `client.storage.from(bucket).remove()` once
with the entire batch, which can cause payload size or timeout issues for large
deletions. Instead of the single `removeResult` call at the end, implement a
batching approach that processes `filesToRemove` in chunks (e.g., 1000 files per
batch). Loop through the accumulated files, slicing them into smaller batches
and calling the remove operation for each batch separately, ensuring all batches
complete successfully before returning the final result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1113f796-4b15-424b-9caf-e7179bfeb2bc

📥 Commits

Reviewing files that changed from the base of the PR and between 4f28281 and af468e9.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (34)
  • app/(tabs)/ai-chat/index.tsx
  • app/_layout.tsx
  • app/insights/report/[periodType].tsx
  • app/settings/privacy.tsx
  • components/ai-chat/ai-chat-screen.tsx
  • components/auth/auth-screen.tsx
  • components/connectivity/OfflineNotice.tsx
  • components/errors/FeatureErrorBoundary.tsx
  • components/errors/InlineErrorMessage.tsx
  • components/errors/RootErrorFallback.tsx
  • components/journal-editor/journal-editor-screen.tsx
  • components/legal/legal-document-screen.tsx
  • components/profile/profile-screen.tsx
  • components/sync/SyncStatusIndicator.tsx
  • components/sync/SyncStatusRow.tsx
  • components/sync/auto-sync-manager.tsx
  • hooks/useAIInsightReport.ts
  • hooks/useAutoSync.ts
  • hooks/useConnectivity.ts
  • hooks/useSyncStatus.ts
  • lib/account/accountDeletionService.ts
  • lib/account/clearLocalUserData.ts
  • lib/errors/normalizeAppError.ts
  • lib/errors/reportAppError.ts
  • lib/sync/requestSync.ts
  • package.json
  • providers/ConnectivityProvider.tsx
  • store/useSyncStore.ts
  • supabase/config.toml
  • supabase/functions/delete-account/index.ts
  • types/appError.ts
  • types/connectivity.ts
  • types/syncResult.ts
  • types/syncStatus.ts

Comment thread components/ai-chat/ai-chat-screen.tsx
Comment thread components/connectivity/OfflineNotice.tsx
Comment thread components/errors/FeatureErrorBoundary.tsx
Comment thread components/errors/FeatureErrorBoundary.tsx
Comment thread components/journal-editor/journal-editor-screen.tsx
Comment thread lib/sync/requestSync.ts
Comment thread lib/sync/requestSync.ts
Comment thread store/useSyncStore.ts
Comment thread store/useSyncStore.ts
Comment thread types/appError.ts
@aryansoni-dev aryansoni-dev merged commit aae9a4d into main Jun 22, 2026
1 check passed
This was referenced Jun 23, 2026
Merged
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.

1 participant