Skip to content

Fix workmanager download bugs#316

Open
dishit-wednesday wants to merge 6 commits into
mainfrom
fix-workmanager-download-bugs
Open

Fix workmanager download bugs#316
dishit-wednesday wants to merge 6 commits into
mainfrom
fix-workmanager-download-bugs

Conversation

@dishit-wednesday

Copy link
Copy Markdown
Collaborator

Summary

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (code change that neither fixes a bug nor adds a feature)
  • Chore (build process, CI, dependency updates, etc.)

Screenshots / Screen Recordings

Android

Before After

iOS

Before After

Checklist

General

  • My code follows the project's coding style and conventions
  • I have performed a self-review of my code
  • I have added/updated comments where the logic isn't self-evident
  • My changes generate no new warnings or errors

Testing

  • I have tested on Android (physical device or emulator)
  • I have tested on iOS (physical device or simulator)
  • I have tested in light mode and dark mode
  • Existing tests pass locally (npm test)
  • I have added tests that prove my fix is effective or my feature works

React Native Specific

  • No new native module without corresponding platform implementation (Android + iOS)
  • New native modules are added to the Xcode project build target (project.pbxproj)
  • No hardcoded pixel values — uses SPACING / TYPOGRAPHY constants from the theme
  • Styles use useThemedStyles pattern (not inline or static StyleSheet.create)
  • Animations/gestures work smoothly on both platforms
  • Large lists use FlatList / FlashList (not .map() inside ScrollView)
  • No unnecessary re-renders introduced (check with React DevTools Profiler if unsure)

Performance & Models

  • Downloads / long-running tasks report progress to the UI
  • File paths are resolved correctly on both platforms (no hardcoded / vs \\)
  • Large files (models, assets) are not committed to the repository

Security

  • No secrets, API keys, or credentials are included in the code
  • User input is validated/sanitized where applicable

Related Issues

Additional Notes

…le collision, persist failed state, improve quant matching

- Remove pull-to-refresh from Download Manager (redundant, screen syncs via events)
- Show alert when starting a 3rd+ download; user can cancel or proceed
- Prefix Android temp download filenames with downloadId to prevent concurrent
  mmproj downloads from clobbering each other's file (root cause of "Downloaded
  file not found" on devices downloading multiple vision model quantizations)
- Add idempotent move: if source missing but target exists, skip re-move
- On download failure, keep entry in UI with failed status instead of removing it;
  only user-initiated cancel clears it
- Fix mmproj quant matching: prefer exact model-quant match before falling back to F16

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request removes manual refresh from the Download Manager, implements a two-download concurrency limit with user alerts, and improves Android file naming and error handling. It also refines vision projection file selection and updates download status tracking. A review comment suggests memoizing the active download count calculation for better performance.

Comment on lines +175 to 183
const activeDownloadCount = Object.entries(text.downloadProgress).filter(([key, progress]) => {
const status = progress?.status;
if (status === 'failed' || status === 'completed') return false;
if (key.startsWith('image:')) {
const imageId = key.split('/').slice(0, -1).join('/').replace('image:', '');
return !image.downloadedImageModels.some(m => m.id === imageId);
}
return true;
}).length;

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.

medium

The activeDownloadCount calculation iterates over text.downloadProgress and performs string manipulations on every render. This should be wrapped in useMemo to prevent unnecessary recalculations, especially since it depends on text.downloadProgress and image.downloadedImageModels which are external state dependencies.

Suggested change
const activeDownloadCount = Object.entries(text.downloadProgress).filter(([key, progress]) => {
const status = progress?.status;
if (status === 'failed' || status === 'completed') return false;
if (key.startsWith('image:')) {
const imageId = key.split('/').slice(0, -1).join('/').replace('image:', '');
return !image.downloadedImageModels.some(m => m.id === imageId);
}
return true;
}).length;
const activeDownloadCount = useMemo(() => Object.entries(text.downloadProgress).filter(([key, progress]) => {
const status = progress?.status;
if (status === 'failed' || status === 'completed') return false;
if (key.startsWith('image:')) {
const imageId = key.split('/').slice(0, -1).join('/').replace('image:', '');
return !image.downloadedImageModels.some(m => m.id === imageId);
}
return true;
}).length, [text.downloadProgress, image.downloadedImageModels]);

…eserve downloadId on failure

- activeDownloadCount now filters out failed/completed entries so failed
  downloads sitting in UI don't wrongly block new downloads or trigger
  the concurrent limit alert
- onError preserves ownerDownloadId from existing progress so retry
  in Download Manager can still look up the download metadata
and
When a vision model downloads successfully but the mmproj file could
not be moved to its final path, show an alert telling the user to use
"Repair Vision" in Download Manager instead of silently showing success
@codecov

codecov Bot commented Apr 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 52.72727% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.76%. Comparing base (9a0b2b5) to head (68b4b2a).

Files with missing lines Patch % Lines
src/screens/ModelsScreen/useModelsScreen.ts 26.66% 8 Missing and 3 partials ⚠️
src/services/modelManager/download.ts 70.83% 4 Missing and 3 partials ⚠️
src/screens/ModelsScreen/useTextModels.ts 25.00% 3 Missing and 3 partials ⚠️
src/services/huggingface.ts 75.00% 0 Missing and 2 partials ⚠️

❌ Your patch check has failed because the patch coverage (52.72%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #316      +/-   ##
==========================================
- Coverage   83.83%   83.76%   -0.07%     
==========================================
  Files         223      223              
  Lines       11460    11482      +22     
  Branches     3144     3154      +10     
==========================================
+ Hits         9607     9618      +11     
- Misses       1070     1078       +8     
- Partials      783      786       +3     
Files with missing lines Coverage Δ
src/screens/DownloadManagerScreen/index.tsx 93.33% <ø> (ø)
...creens/DownloadManagerScreen/useDownloadManager.ts 58.07% <ø> (-1.59%) ⬇️
src/services/modelManager/restore.ts 88.33% <ø> (ø)
src/services/huggingface.ts 87.03% <75.00%> (-0.24%) ⬇️
src/screens/ModelsScreen/useTextModels.ts 77.29% <25.00%> (-1.64%) ⬇️
src/services/modelManager/download.ts 81.34% <70.83%> (+1.68%) ⬆️
src/screens/ModelsScreen/useModelsScreen.ts 82.75% <26.66%> (-5.82%) ⬇️

... and 2 files with indirect coverage changes

🚀 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.

…nt before delete

Two related mmproj bugs, each silent and user-visible:

1. deleteModel unconditionally unlinked the shared mmproj file. Since sibling
   quantizations of the same vision family share one mmproj path (by design,
   to avoid re-downloading ~500MB per quant), deleting any one quant broke
   vision on every remaining sibling. Now we scan the remaining models and
   only unlink when no other entry still references the path.

2. When mmproj post-download move failed, the code checked only RNFS.exists
   on the target and kept using it if present. That target could be a partial
   or corrupt leftover, leaving the model registered as vision-capable with
   a broken mmproj. Reuse checkMmProjExists (size check + GGUF magic-byte
   check, same pattern as llmSafetyChecks.validateModelFile) to validate
   before trusting. Invalid target is unlinked and the model saves as
   text-only, surfacing the existing "Repair Vision" recovery path.
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
15.4% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

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