Skip to content

Add Garmin dump imports and progress on iOS - #1603

Merged
Asherlc merged 16 commits into
mainfrom
Asherlc/garmin-ios-import-progress
Jul 14, 2026
Merged

Asherlc merged 16 commits into
mainfrom
Asherlc/garmin-ios-import-progress

Conversation

@Asherlc

@Asherlc Asherlc commented Jul 14, 2026 •

Copy link
Copy Markdown
Owner

Summary

Previously, iOS classified every shared ZIP as Apple Health and could not resume import progress; this adds explicit Garmin Dump ZIP selection through the native document picker.
Garmin files now use the existing upload/status endpoints, while a user-scoped active-import query restores and polls provider-card progress across navigation or relaunch and surfaces queue errors.
The change includes the Expo document-picker dependency, a Storybook Garmin import state, and TDD coverage for mobile and server flows.

Validation

Validated with pnpm test:mobile (825 tests), focused server router tests (108 tests), pnpm typecheck, and the full pnpm lint pipeline.


Summary by cubic

Adds Garmin dump and Kaya CSV imports with a shared file-import flow on iOS and web. Uploads are chunked and resumable with per-user progress; final chunks return processing, and provider stats show server-computed totals.

  • New Features

    • iOS: Native picker via expo-document-picker for Apple Health ZIP/XML, Garmin ZIP, and Strong/Cronometer/Kaya CSVs; explicit provider selection; shared provider card with “Import file”; resumable uploads with status polling (Apple Health progress prioritized).
    • Web: Shared file‑import provider card with drag‑and‑drop plus an “Import file” button; provider stats display totalRecords from the server.
    • Server: Unified upload/status endpoints for Apple Health, Strong, Cronometer, Garmin, and Kaya; chunked/resumable uploads with jobId=uploadId; final chunk assembles, enqueues, then responds with processing; max upload size enforced via chunk byte tracking; sync.activeImports returns current‑user jobs with progress/messages.
  • Bug Fixes

    • Refresh chunk‑byte TTL on each received chunk to prevent in‑progress uploads from expiring.

Written for commit 91b582f. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added Garmin Dump ZIP importing from the mobile provider screen, including shared-file imports with explicit provider selection.
    • Added resumable file-import progress for active imports, with provider-specific status messaging (including shared-file imports).
    • Expanded file-import card support across providers, including an “Import file” action and improved import-provider handling.
  • Bug Fixes
    • Improved shared-file import cleanup safety, progress messaging, and standardized error display.
    • Scoped active-import status to the current account and refined refresh/polling behavior.

@sourcery-ai sourcery-ai Bot left a comment

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.

Sorry @Asherlc, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@codereviewbot-ai

codereviewbot-ai Bot commented Jul 14, 2026 •

Copy link
Copy Markdown

Pull Request Review Summary

The implementation of the Garmin Dump Import feature is very clean, well-structured, and has excellent test coverage.

I have identified one minor issue regarding the progress bar rendering for active imports:

  • Progress Bar Mismatch: In packages/mobile/app/providers/index.tsx, the activeImport object returned from the activeImports query has a progress property, but ProviderCard expects syncProgress to have a percentage property. This mismatch prevents the progress bar from rendering for active imports. I have posted an inline comment with a suggested fix to map activeImport to the expected shape.

Aside from this, the code looks great and is ready to merge once that minor fix is applied! LGTM!


🤖 Reviewed by codereviewbot.ai - Catch bugs before your team does.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

An error occurred during the review process. Please try again later.

📝 Walkthrough

Walkthrough

Adds Garmin ZIP importing and generalizes file-import handling across server upload routes, shared mobile uploads, active-import progress, and web/mobile provider cards. The change also adds chunked-upload coverage, provider-card stories, and integration tests.

Changes

File import flow

Layer / File(s) Summary
Shared server upload pipeline
packages/server/src/routes/upload.ts, packages/server/src/routes/upload.test.ts
Consolidates configured upload and status routes, supports shared chunked uploads for Garmin and other providers, generates unique paths, and enqueues assembled imports.
Provider selection and shared uploads
packages/mobile/lib/share-import.ts, packages/mobile/lib/share-import.test.ts
Adds garmin-dump provider typing, inference, explicit provider selection, Garmin endpoints, unified chunked upload headers, and progress propagation.
Active import status
packages/server/src/routers/sync.ts, packages/server/src/routers/sync.test.ts
Adds sync.activeImports with user filtering, provider mapping, normalized progress, queue-state messages, and error handling.
Mobile provider integration
packages/mobile/app/providers/*, packages/mobile/package.json
Adds document-picker imports, provider-specific file actions, active-import polling, merged progress rendering, provider-card actions, and related tests and stories.
Web provider cards
packages/web/src/components/*, packages/web/src/pages/ProviderDetailPage.tsx, packages/web/src/components/DataSourcesPanel.tsx
Adds reusable file-import buttons/cards, configuration lookup, chunked provider settings, shared rendering, Storybook stories, and coverage for provider stats and import controls.

Estimated code review effort: 5 (Critical) | ~100 minutes

Possibly related PRs

  • Asherlc/dofek#1405: Overlaps with Apple Health provider-card and import UI wiring.
  • Asherlc/dofek#1572: Extends the Kaya import pipeline and provider mapping used by active-import progress.
  • Asherlc/dofek#1599: Changes Garmin import jobs whose progress is surfaced by this client and active-import flow.

Suggested labels: area/mobile, area/web, area/server, type/feature

🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title is relevant and imperative, but it is missing the required area prefix for this mobile-focused change. Prefix the title with the relevant area, for example: "[mobile] Add Garmin dump imports and progress on iOS".
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

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

@github-actions

github-actions Bot commented Jul 14, 2026 •

Copy link
Copy Markdown
Contributor

Storybook previews for 436f59d1 are ready:

This comment updates automatically on each PR push.

Comment thread packages/mobile/app/providers/index.tsx Outdated
@github-actions

github-actions Bot commented Jul 14, 2026 •

Copy link
Copy Markdown
Contributor

Mobile Preview

Scan to open on device:

QR code for dofek://preview/pr-1603

Channel pr-1603
Deep Link dofek://preview/pr-1603
Commit 436f59d

To test on device:

  1. Build and install the preview client: PREVIEW_CHANNEL=pr-1603 pnpm expo prebuild --clean -p ios
  2. Or tap deep link on an existing preview build: dofek://preview/pr-1603

Each PR gets its own channel. Build a preview client with PREVIEW_CHANNEL=pr-{N} to test.

@Asherlc
Asherlc marked this pull request as ready for review July 14, 2026 17:10
Copilot AI review requested due to automatic review settings July 14, 2026 17:10
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@greptile-apps greptile-apps 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.

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@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: 4

Caution

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

⚠️ Outside diff range comments (1)
packages/mobile/lib/share-import.ts (1)

167-196: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Garmin share imports still resolve to Apple Health. In packages/mobile/app/providers/index.tsx:321-327, the shared-file path calls importFile(sharedFileUri) without a providerId, so packages/mobile/lib/share-import.ts:167-196 falls back to inferImportProviderFromFile(). That helper returns apple-health for any .zip before it can ever reach garmin-dump, so shared Garmin exports are routed to the wrong upload endpoint. Move Garmin detection ahead of the generic ZIP check or pass the provider through the share flow.

🤖 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 `@packages/mobile/lib/share-import.ts` around lines 167 - 196, Update
inferImportProviderFromFile so Garmin exports are detected before the generic
isAppleHealthLike ZIP check and return garmin-dump when the filename, extension,
MIME type, or other existing Garmin identifying metadata matches. Preserve Apple
Health detection for remaining ZIP imports, and ensure the shared-file import
flow uses the corrected provider result.
🤖 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 `@packages/mobile/app/providers/index.test.tsx`:
- Around line 1278-1311: Strengthen the in-flight Garmin import test around
renderProvidersScreen and the garmin-card progress UI by asserting that the
progress bar reflects the mocked 64% activeImport.progress value. Query the
progress track or fill using the existing accessible/test identifier and verify
its rendered percentage or width, while retaining the current message and
import-only assertions.

In `@packages/mobile/app/providers/index.tsx`:
- Around line 678-702: Update the import progress mapping in the providerList
render around activeImportByProvider so server-resumed activeImport values are
converted to the ProviderCard syncProgress shape, mapping activeImport.progress
to percentage while preserving message. Keep localImportProgress precedence and
ensure both local and resumed imports provide percentage and message
consistently.

In `@packages/mobile/lib/share-import.ts`:
- Line 1: Update inferImportProviderFromFile to detect Garmin export ZIP/XML
files before the generic Apple Health classification, ensuring shared Garmin
archives resolve to "garmin-dump" while preserving "apple-health" for non-Garmin
ZIP/XML files.

In `@packages/server/src/routers/sync.ts`:
- Line 795: Replace the Date.now()-based fallback in the jobId construction with
a deterministic value incorporating providerId, while continuing to prefer
job.id when present. Ensure repeated polls of the same provider produce the same
fallback ID.

---

Outside diff comments:
In `@packages/mobile/lib/share-import.ts`:
- Around line 167-196: Update inferImportProviderFromFile so Garmin exports are
detected before the generic isAppleHealthLike ZIP check and return garmin-dump
when the filename, extension, MIME type, or other existing Garmin identifying
metadata matches. Preserve Apple Health detection for remaining ZIP imports, and
ensure the shared-file import flow uses the corrected provider 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0cb144ca-5f4d-445e-9be9-ec7c783c5b31

📥 Commits

Reviewing files that changed from the base of the PR and between fed1472 and 60ed18a.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • packages/mobile/app/providers/index.stories.tsx
  • packages/mobile/app/providers/index.test.tsx
  • packages/mobile/app/providers/index.tsx
  • packages/mobile/app/providers/provider-card.tsx
  • packages/mobile/lib/share-import.test.ts
  • packages/mobile/lib/share-import.ts
  • packages/mobile/package.json
  • packages/server/src/routers/sync.test.ts
  • packages/server/src/routers/sync.ts

Comment thread packages/mobile/app/providers/index.test.tsx
Comment thread packages/mobile/app/providers/index.tsx
Comment thread packages/mobile/lib/share-import.ts
Comment thread packages/server/src/routers/sync.ts Outdated
@cursor

cursor Bot commented Jul 14, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@codereviewbot-ai

codereviewbot-ai Bot commented Jul 14, 2026 •

Copy link
Copy Markdown

Pull Request Review Summary

This is an exceptionally high-quality, well-tested, and clean Pull Request! It successfully implements the Garmin Dump import flow, detects Garmin ZIP/XML exports correctly, and adds robust support for server-resumed active imports.

Key Highlights:

  1. Garmin ZIP/XML Detection: Correctly prioritizes Garmin-specific checks before falling back to Apple Health for generic .zip/.xml files.
  2. Server-Resumed Progress Mapping: Properly maps activeImport.progress to syncProgress.percentage to ensure the progress bar renders correctly for resumed imports.
  3. Robust Server-Side Query: The activeImports query on the server is highly efficient, filtering jobs by userId before performing any asynchronous job.getState() calls.
  4. Excellent Test Coverage: Includes comprehensive unit tests for both the server-side router and the mobile-side screens/helpers.

Suggested Improvement:

  • Apple Health Card Progress: Since the Apple Health card is rendered manually as a separate card (outside of providerList.map), it currently does not show active import progress (either local or server-resumed). I have posted a suggestion to define appleHealthImportProgress and pass it to the Apple Health ProviderCard to ensure consistency.

Once that minor improvement is addressed, this PR is fully ready to merge! LGTM! 🚀


🤖 Reviewed by codereviewbot.ai - Catch bugs before your team does.

@greptile-apps greptile-apps 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.

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@Asherlc

Asherlc commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

Addressed CodeRabbit review summary: #1603 (review)

Fixed.

  • Mapped active import progress to the ProviderCard { percentage, message } shape.
  • Added a 64% resumed-import progress assertion.
  • Routed Garmin-named ZIP/XML shared imports to garmin-dump before the generic Apple Health fallback.
  • Replaced the active-import Date.now() fallback job ID with deterministic job-${providerId}.
  • Validation: pnpm lint, pnpm tsc --noEmit, pnpm -C packages/server exec tsc --noEmit, pnpm -C packages/mobile exec tsc --noEmit, focused mobile/server tests, and full pnpm test.
  • Commit: 5f19496

Comment thread packages/mobile/app/providers/index.tsx
@codereviewbot-ai

Copy link
Copy Markdown

[Review Note] File: packages/mobile/app/providers/index.tsx Line: 636

Pass appleHealthImportProgress to the Apple Health ProviderCard so that both local and server-resumed Apple Health imports display progress correctly.

      <ProviderCard
        provider={{
          ...appleHealthProvider,
        }}
        stats={statsMap.apple_health}
        syncing={healthKitSyncing || appleHealthImportProgress !== undefined}
        syncProgress={
          appleHealthImportProgress ??
          (healthKitSyncing || healthKitProgress ? { message: healthKitProgress } : undefined)
        }
        onSync={() => handleHealthKitSync()}

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours. Upgrade to a paid plan for unlimited reviews.

@greptile-apps greptile-apps 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.

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours. Upgrade to a paid plan for unlimited reviews.

@Asherlc

Asherlc commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

Addressed PR-level review note: #1603 (comment)

Fixed.

  • Apple Health now prioritizes appleHealthImportProgress before falling back to HealthKit sync progress, matching the generic provider-card import behavior.
  • This ensures active local/server-resumed Apple Health imports are not hidden by stale or concurrent HealthKit progress text.
  • Validation: pnpm vitest run packages/mobile/app/providers/index.test.tsx, pnpm tsc --noEmit, pnpm -C packages/mobile exec tsc --noEmit, pnpm -C packages/server exec tsc --noEmit, pnpm -C packages/web exec tsc --noEmit, and CLICKHOUSE_URL=http://default:health@127.0.0.1:55108 pnpm lint.
  • Commit: d8cd1b2

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours. Upgrade to a paid plan for unlimited reviews.

@greptile-apps greptile-apps 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.

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps 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.

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026 •

Copy link
Copy Markdown

@Asherlc Triggering a full re-review of all changes in the PR.

✅ Action performed

Full review finished.


Your plan includes PR reviews subject to rate limits. More reviews will be available in 59 minutes.

@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: 12

Caution

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

⚠️ Outside diff range comments (2)
packages/mobile/app/providers/index.tsx (2)

288-317: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Run temporary-file cleanup when authentication is missing.

Line 290 returns before finally, leaving document-picker cache copies on disk when the session is absent or expires. Move the token check inside try so cleanup always runs and surface an actionable error.

Proposed fix
   const importFile = useCallback(
     async (fileUri: string, providerId?: ImportProviderId) => {
-      if (!sessionToken) return;
       try {
+        if (!sessionToken) {
+          throw new Error("Sign in before importing a file");
+        }
         await importSharedFile(
🤖 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 `@packages/mobile/app/providers/index.tsx` around lines 288 - 317, Update
importFile so the sessionToken check occurs inside its try block, ensuring
deleteSharedFile runs through finally even when authentication is missing.
Replace the early silent return with an actionable authentication error that
follows the existing error handling path and preserves cleanup.

546-548: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use the required query-state component for active-import failures.

Avoid consuming (activeImports.data ?? []) when activeImports.error exists, and render the failure through QueryStatePanel rather than bespoke text.

As per coding guidelines, “Treat loading, error, and empty as separate UI states” and use QueryStatePanel.tsx for screens and cards. As per path instructions, do not use query.data ?? [] when query.error exists.

Also applies to: 618-622

🤖 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 `@packages/mobile/app/providers/index.tsx` around lines 546 - 548, Update the
active-import UI flow around activeImportByProvider and its rendering branches
to handle loading, error, and empty states separately. When activeImports.error
exists, avoid reading activeImports.data via a fallback and render the failure
with QueryStatePanel; retain normal data mapping and existing non-error behavior
for successful queries.

Sources: Coding guidelines, Path instructions

🤖 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 `@packages/mobile/app/providers/provider-card.tsx`:
- Around line 114-138: Update the provider card action rendering to track import
and sync activity separately: use the sync-specific state for the Sync button’s
disabled and loading behavior, and pass the import-specific importing state to
FileImportButton’s disabled/loading props. Anchor the changes to the
canRunManualSync/canImport action block and FileImportButton, preserving each
action’s own spinner behavior for dual-capability providers.

In `@packages/mobile/components/ProviderStatsBreakdown.tsx`:
- Around line 51-54: Update ProviderStatsBreakdown to display the total-record
value supplied by the API instead of calculating it on mobile. Update
packages/mobile/app/providers/index.test.tsx at lines 738-739 to provide and
assert the server-returned total.

In `@packages/mobile/lib/share-import.test.ts`:
- Around line 184-207: Update the chunk request assertions in the share import
test so the second request’s x-upload-id is compared directly with the first
request’s captured x-upload-id, while retaining the existing share-999 prefix
validation on the first request.

In `@packages/mobile/lib/share-import.ts`:
- Around line 282-286: Update the Garmin handling in
packages/mobile/lib/share-import.ts:282-286 to preserve .xml in x-file-ext
instead of relabeling Garmin XML as .zip. Update the Garmin provider picker
configuration in packages/mobile/app/providers/file-import-providers.ts:37-40 to
accept XML MIME types, keeping Garmin XML support consistent through inference,
selection, and upload.

In `@packages/server/src/routes/upload.ts`:
- Around line 551-569: Update the upload assembly flow around assembleChunks,
enqueueImport, and the catch handler so chunkDir cleanup is best-effort and
cannot prevent enqueueImport from running. In the failure path, also remove the
partially assembled assembledFile with best-effort cleanup, while preserving the
existing upload error status handling.
- Around line 541-549: Update the upload completion flow around
uploadStateStore.deleteUploadSession, setUploadStatus, and the following async
assembly block so it durably persists the assembly job or retains an idempotent
terminal upload session until queue insertion succeeds. Do not delete the
session or return the assembling response until the durable handoff is complete,
and ensure retries and process restarts can still resume or enqueue the import
without creating duplicates.
- Around line 443-447: Update the upload error handlers at the shown catch
blocks, including those around the handlers near lines 559-570 and 572-588, to
call captureException with the caught error before logging or returning the
failure response. Preserve the existing cleanup, logging, and HTTP response
behavior while ensuring every unexpected upload exception is reported to
telemetry.
- Around line 482-507: Update the chunked-upload flow around the upload session
and streamToFile calls to enforce limits across the entire upload: cap
chunkTotal, track each chunk’s actual byte count in the session, and atomically
reserve/reject cumulative bytes against config.maxUploadBytes before accepting
data. Ensure concurrent chunk requests cannot exceed the limit, and preserve the
existing forbidden and mismatched-total responses.
- Around line 399-408: Update the ownership check in the job-status handler
using getImportJobStatus so access is allowed only when status.userId is present
and matches userId. Return the existing 403 Forbidden response for missing or
mismatched ownership metadata, while preserving the 404 response for unknown
jobs and the success response for owned jobs.

In `@packages/web/src/components/file-import-configs.ts`:
- Around line 48-51: Update getFileImportConfig to verify providerId is an own
key of fileImportConfigs using Object.hasOwn before indexing it; preserve the
special apple_health handling and return undefined for inherited or unknown
properties.

In `@packages/web/src/components/FileImportProviderCard.tsx`:
- Around line 5-10: Update FileImportProviderCardProps to intersect
FileImportZoneProps only with the required providerId property, removing the
redundant stats, recentLogs, and showDetailsLink declarations.

In `@packages/web/src/components/FileImportZone.tsx`:
- Around line 219-226: Replace the non-form grouping element in the
FileImportZone drag-and-drop container with a div, preserving its aria-label and
drag event handlers; also update the matching closing fieldset tag to div.

---

Outside diff comments:
In `@packages/mobile/app/providers/index.tsx`:
- Around line 288-317: Update importFile so the sessionToken check occurs inside
its try block, ensuring deleteSharedFile runs through finally even when
authentication is missing. Replace the early silent return with an actionable
authentication error that follows the existing error handling path and preserves
cleanup.
- Around line 546-548: Update the active-import UI flow around
activeImportByProvider and its rendering branches to handle loading, error, and
empty states separately. When activeImports.error exists, avoid reading
activeImports.data via a fallback and render the failure with QueryStatePanel;
retain normal data mapping and existing non-error behavior for successful
queries.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: adf19b75-9f73-4c65-a99f-da4075ca30cc

📥 Commits

Reviewing files that changed from the base of the PR and between e8dc8a5 and 6342130.

📒 Files selected for processing (23)
  • packages/mobile/app/providers/file-import-button.tsx
  • packages/mobile/app/providers/file-import-provider-card.tsx
  • packages/mobile/app/providers/file-import-providers.ts
  • packages/mobile/app/providers/index.test.tsx
  • packages/mobile/app/providers/index.tsx
  • packages/mobile/app/providers/provider-card.tsx
  • packages/mobile/app/providers/styles.ts
  • packages/mobile/components/ProviderStatsBreakdown.tsx
  • packages/mobile/lib/share-import.test.ts
  • packages/mobile/lib/share-import.ts
  • packages/server/src/routes/upload.test.ts
  • packages/server/src/routes/upload.ts
  • packages/web/src/components/DataSourcesPanel.test.tsx
  • packages/web/src/components/DataSourcesPanel.tsx
  • packages/web/src/components/FileImportButton.stories.tsx
  • packages/web/src/components/FileImportButton.tsx
  • packages/web/src/components/FileImportProviderCard.stories.tsx
  • packages/web/src/components/FileImportProviderCard.tsx
  • packages/web/src/components/FileImportZone.test.tsx
  • packages/web/src/components/FileImportZone.tsx
  • packages/web/src/components/file-import-configs.ts
  • packages/web/src/pages/ProviderDetailPage.test.tsx
  • packages/web/src/pages/ProviderDetailPage.tsx

Comment thread packages/mobile/app/providers/provider-card.tsx
Comment thread packages/mobile/components/ProviderStatsBreakdown.tsx
Comment thread packages/mobile/lib/share-import.test.ts
Comment thread packages/mobile/lib/share-import.ts
Comment thread packages/server/src/routes/upload.ts
Comment thread packages/server/src/routes/upload.ts Outdated
Comment thread packages/server/src/routes/upload.ts Outdated
Comment thread packages/web/src/components/file-import-configs.ts
Comment thread packages/web/src/components/FileImportProviderCard.tsx Outdated
Comment thread packages/web/src/components/FileImportZone.tsx Outdated
@codereviewbot-ai

codereviewbot-ai Bot commented Jul 14, 2026 •

Copy link
Copy Markdown

Summary of Review

The changes in this Pull Request are exceptionally clean, robust, and well-tested. All previously identified issues and edge cases have been fully addressed:

  1. Dual-Capability Provider Card Actions: The ProviderCard now tracks syncing and importing states separately, ensuring that the correct button shows the loading spinner and is disabled during active operations.
  2. Server-Resumed Import Progress Mapping: The mapping of activeImport.progress to syncProgress.percentage is correctly implemented for both standard and Apple Health providers, ensuring the progress bar renders correctly for resumed imports.
  3. Garmin ZIP/XML Routing: Garmin dump ZIP files are correctly inferred as garmin-dump before falling back to Apple Health, and Garmin XML is not misclassified.
  4. Secure Job Ownership Checks: The job status endpoint fails closed securely when status.userId is missing or mismatched.
  5. Atomic Chunk Tracking: The new recordReceivedChunk method tracks chunk byte totals atomically in Redis via a Lua script, preventing oversized uploads and race conditions.
  6. Telemetry & Error Handling: Caught upload exceptions are properly reported to telemetry via captureException before returning failure responses.
  7. Prototype Pollution Guard: Direct indexing of fileImportConfigs is safely guarded with Object.hasOwn.

Excellent work! This PR is ready to be merged. LGTM 🚀


🤖 Reviewed by codereviewbot.ai - Catch bugs before your team does.

@greptile-apps greptile-apps 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.

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@Asherlc

Asherlc commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

Follow-up for the latest CodeRabbit PR-level/outside-diff review items.\n\nFixed.\n- Moved the mobile missing-session check inside the import try/finally path so shared/document-picker temp files are still deleted and the user sees "Sign in before importing a file".\n- Active import query failures now render through QueryStatePanel and no longer read activeImports.data when activeImports.error exists.\n- Provider stats now include server-supplied totalRecords; shared providerStatsTotal prefers that API value and mobile tests provide/assert it.\n- Upload status now forbids missing ownership metadata instead of treating it as public.\n- Chunked uploads now cap chunk count, record actual per-chunk byte counts atomically, reject cumulative uploads over the route limit, and keep the upload session until assembly/enqueue completes.\n- Final chunk handling now enqueues before returning processing, makes chunk cleanup best-effort after enqueue, and removes partial assembled files on assembly/enqueue failure.\n- Verified older PR-level items remain addressed: Garmin ZIP inference happens before generic Apple Health ZIP handling, active import progress maps to ProviderCard percentage/message, and active import fallback job IDs are deterministic by provider.\n\nCommit: 680f293

@greptile-apps greptile-apps 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.

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@codereviewbot-ai

codereviewbot-ai Bot commented Jul 14, 2026 •

Copy link
Copy Markdown

Pull Request Review Summary

The changes in packages/server/src/lib/upload-state-store.ts are extremely well-designed, robust, and correct.

Key Observations:

  1. Atomic Chunk Tracking: The introduction of recordReceivedChunk using a Redis Lua script ensures that chunk byte totals are tracked atomically and oversized uploads are rejected reliably without race conditions.
  2. Graceful Fallbacks: Both the in-memory and Redis implementations handle non-existent keys/fields gracefully and correctly account for retries or updates of existing chunks.
  3. Excellent Test Coverage: The corresponding unit tests in upload-state-store.test.ts and upload-state-store-redis.test.ts are comprehensive and verify the behavior of both store implementations under normal and oversized upload scenarios.

Minor Suggestion:

  • In RedisUploadStateStore.saveUploadSession, consider also expiring uploadChunkBytesKey(uploadId) to keep its TTL perfectly synchronized with the session TTL if the session is updated/extended.

Overall, the code is clean, secure, and ready to be merged! LGTM!


🤖 Reviewed by codereviewbot.ai - Catch bugs before your team does.

@codereviewbot-ai

Copy link
Copy Markdown

[Review Note] File: packages/server/src/lib/upload-state-store.ts Line: 210

To ensure that the chunk bytes TTL is kept in sync with the session TTL (especially if the session TTL is extended or updated), it would be a good idea to also expire the uploadChunkBytesKey(uploadId) here.

    await client.expire(uploadChunksKey(uploadId), ttlSeconds(timeToLiveMs));
    await client.expire(uploadChunkBytesKey(uploadId), ttlSeconds(timeToLiveMs));

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours. Upgrade to a paid plan for unlimited reviews.

@greptile-apps greptile-apps 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.

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours. Upgrade to a paid plan for unlimited reviews.

@greptile-apps greptile-apps 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.

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours. Upgrade to a paid plan for unlimited reviews.

@greptile-apps greptile-apps 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.

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@codereviewbot-ai

Copy link
Copy Markdown

🤖 Review skipped: Rate limit exceeded. Free accounts are limited to 2 reviews per 4 hours. Upgrade to a paid plan for unlimited reviews.

@Asherlc

Asherlc commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

Addressed CodeReviewBot comment #1603 (comment).

Fixed.

  • RedisUploadStateStore.saveUploadSession() now refreshes the TTL for uploadChunkBytesKey(uploadId) alongside uploadChunksKey(uploadId), keeping chunk-byte state aligned when the session TTL is refreshed.
  • Added a Redis upload-state test assertion covering the chunk-byte TTL refresh.
  • Validation: pnpm exec vitest run packages/server/src/lib/upload-state-store-redis.test.ts, pnpm exec biome check packages/server/src/lib/upload-state-store.ts packages/server/src/lib/upload-state-store-redis.test.ts, pnpm --filter @dofek/server typecheck.
  • Commit: 91b582f

@greptile-apps greptile-apps 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.

Asherlc has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@Asherlc
Asherlc merged commit 66e878a into main Jul 14, 2026
104 checks passed
@Asherlc
Asherlc deleted the Asherlc/garmin-ios-import-progress branch July 14, 2026 21:43
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.

3 participants