Skip to content

fix(windows,ios): releaseCapture, error rejection, file:// URIs, podspec minimum#642

Merged
gre merged 3 commits intomasterfrom
fix/tier1-bugs
May 5, 2026
Merged

fix(windows,ios): releaseCapture, error rejection, file:// URIs, podspec minimum#642
gre merged 3 commits intomasterfrom
fix/tier1-bugs

Conversation

@gre
Copy link
Copy Markdown
Owner

@gre gre commented May 4, 2026

Summary

Tier 1 of a 3-PR cleanup pass — concrete bugs with low blast radius.

  • Windows releaseCapture — was a // TODO implement me stub, leaking temp files written to ApplicationData.Current.LocalFolder indefinitely. Now uses Path.GetRelativePath (not a string prefix check) to confirm the URI lives inside the local folder before deleting. Also handles file:// URIs (JS callers commonly pass them).
  • Windows error pathstcs.SetResult(ex.Message) was returning the exception string as if it were a successful capture URI, so JS promises never rejected on Windows. Switched to TrySetException(ex) (and TrySetResult on the success path) with TaskCreationOptions.RunContinuationsAsynchronously so continuations don't pile up on the dispatcher thread. Same shape for the unsupported-format early return (throw new ArgumentException instead of returning the message).
  • Podspecs.platform = :ios, "9.0" is misleading (the code already uses UIGraphicsImageRenderer, iOS 10+). Bumped to 15.1 to match the current React Native floor (the lib's existing peerDependency react-native >=0.76.0 already requires this, so no new floor for valid consumers — but worth a CHANGELOG line on release).

Reverted in this branch (Copilot round 1): the original draft also flipped ViewShot.proposeSize from Math.min to Math.max. Pre-existing JUnit tests pin the current Math.min behaviour, and the call site at ViewShot.java:245-247 discards the configured stream — so the formula change has no functional effect and only breaks the tests. Reverted; left as-is.

Test plan

  • npm run type-check — passes
  • npm run lint:ci — passes
  • npm test — 46 / 46
  • Verify Windows example: releaseCapture("file:///…/LocalState/foo.png") deletes the file
  • Verify Windows example: a thrown native error rejects the JS promise (instead of resolving with the error message string)

🤖 Generated with Claude Code

…dspec iOS

- Android: ViewShot.proposeSize now uses Math.max so the
  ReusableByteArrayOutputStream is sized to the bitmap (was capped at 32
  bytes, forcing a realloc on every capture).
- Windows: implement releaseCapture (was a TODO stub leaking temp files
  in ApplicationData.Current.LocalFolder).
- Windows: switch error paths from tcs.SetResult(ex.Message) to
  tcs.SetException so JS promises actually reject; same fix for the
  unsupported-format early return.
- Podspec: bump iOS minimum from 9.0 to 15.1 to match the React Native
  floor (the code already requires UIGraphicsImageRenderer / iOS 10+).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 4, 2026 09:16
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR is a low-blast-radius cleanup focusing on fixing platform-specific capture correctness/performance issues across Android, Windows, and iOS packaging.

Changes:

  • Android: fixes proposeSize calculation used to pre-size the reusable output buffer.
  • Windows: implements releaseCapture cleanup for tmpfiles and fixes promise rejection behavior on native errors / unsupported formats.
  • iOS: raises the podspec minimum iOS version to 15.1.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
windows/RNViewShot/RNViewShotModule.cs Implements Windows tmpfile cleanup and corrects error handling to reject JS promises on failure.
react-native-view-shot.podspec Updates iOS minimum deployment target declared by the podspec.
android/src/main/java/fr/greweb/reactnativeviewshot/ViewShot.java Adjusts Android buffer size proposal logic to avoid always choosing a tiny size.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread windows/RNViewShot/RNViewShotModule.cs Outdated
Comment thread android/src/main/java/fr/greweb/reactnativeviewshot/ViewShot.java Outdated
Address Copilot review feedback on PR #642.

- ViewShot.proposeSize: revert to Math.min. The function's return value
  is set on a ReusableByteArrayOutputStream that is immediately
  discarded (lines 245-247) — innerBuffer() returns the same array that
  was passed in, and the actual capture methods build their own streams
  from the static outputBuffer. So the buffer-sizing intent never
  reaches anything that matters; changing the formula has no functional
  effect. The existing ProposeSizeTest pinned this dead-code behaviour
  to flag any change as deliberate, which is exactly what it caught.
- Windows releaseCapture: replace the StartsWith prefix check with
  Path.GetRelativePath so sibling paths like "...\LocalState2\file.png"
  cannot match a "...\LocalState" prefix and slip through.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gre gre requested a review from Copilot May 4, 2026 12:00
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +25 to +36
if (string.IsNullOrEmpty(uri)) return;
// Only delete files that actually live inside the app's local
// folder (where ViewShot.GetStorageFile writes). Use
// Path.GetRelativePath rather than a string prefix check, which
// would otherwise let sibling paths like
// "...\LocalState2\file.png" pass a "...\LocalState" prefix.
var localFolder = ApplicationData.Current.LocalFolder.Path;
string fullPath;
try
{
fullPath = Path.GetFullPath(uri);
}
Comment thread windows/RNViewShot/RNViewShotModule.cs Outdated
throw new ArgumentException("Unsupported image format: " + format + ". Try one of: png | jpg | jpeg");
}

var tcs = new TaskCompletionSource<string>();
Comment thread windows/RNViewShot/RNViewShotModule.cs Outdated
catch (Exception ex)
{
tcs.SetResult(ex.Message);
tcs.SetException(ex);
Comment thread react-native-view-shot.podspec
Address Copilot feedback round 2 on PR #642.

- releaseCapture: parse file:// URIs first via Uri.TryCreate; previous
  Path.GetFullPath would throw on a `file:///...` input and silently
  leak the file. JS callers commonly pass URIs (matches Android
  Uri.parse).
- TaskCompletionSource: opt into RunContinuationsAsynchronously so
  promise continuations don't pile up on the dispatcher thread; switch
  SetResult/SetException to TrySetResult/TrySetException for idempotent
  completion.
- Drop verbose comments and collapse the trivial best-effort try/catch.
@gre gre changed the title fix: Android proposeSize, Windows releaseCapture & error handling, podspec iOS minimum fix(windows,ios): releaseCapture, error rejection, file:// URIs, podspec minimum May 4, 2026
@gre gre force-pushed the fix/tier1-bugs branch from a1c4285 to 0e93309 Compare May 5, 2026 06:00
@gre gre merged commit cda9bf7 into master May 5, 2026
10 checks passed
@gre gre deleted the fix/tier1-bugs branch May 5, 2026 06:02
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