Skip to content

feat: share local files by path to preserve original filenames - #14

Merged
AbassHammed merged 4 commits into
sudosylabs:masterfrom
karlingen:feat/share-local-file-paths
Aug 6, 2026
Merged

feat: share local files by path to preserve original filenames#14
AbassHammed merged 4 commits into
sudosylabs:masterfrom
karlingen:feat/share-local-file-paths

Conversation

@karlingen

Copy link
Copy Markdown
Contributor

Implements the optional filePaths field requested in #13, allowing files that already exist on disk to be shared directly while preserving the original filename.

Changes

  • JS API (guest-js/index.ts): added ShareData.filePaths with MAX_FILE_PATH_BYTES validation; combined files + filePaths count and size checks.
  • Rust (src/models.rs): added ShareOptions.file_paths and MAX_FILE_PATH_BYTES, counting file paths toward MAX_FILES.
  • macOS (src/platform/macos.rs): appends NSURL objects built from file_paths directly to the NSSharingServicePicker items.
  • iOS (ios/Sources/SharePlugin.swift & ShareCore): appends URL(fileURLWithPath:) objects from filePaths to UIActivityViewController, and validates filePaths length on the Swift side.
  • Windows (src/platform/windows.rs): resolves StorageFile from each file_paths entry via GetFileFromPathAsync and adds the IStorageItem to DataTransferManager.
  • README: documented the new filePaths option in both TypeScript and Rust examples.
  • Tests: updated Rust model test literals for the new file_paths field; regenerated api-iife.js and dist-js.

Verification

  • cargo +1.89.0-aarch64-apple-darwin test — 11 passed
  • cargo +1.89.0-aarch64-apple-darwin fmt --check — clean
  • cd ios && VNIDROP_SHARE_USE_TAURI_STUB=1 swift test — 5 passed
  • npm run builddist-js + api-iife.js regenerated
  • npm run test — 8 passed
  • npm run test:types — clean

Closes #13

@karlingen
karlingen marked this pull request as draft July 31, 2026 13:04
@karlingen

Copy link
Copy Markdown
Contributor Author

Marking it as a draft until I've verified it works as expected on all platforms

@karlingen
karlingen force-pushed the feat/share-local-file-paths branch 2 times, most recently from 4ca4001 to ec5daa9 Compare July 31, 2026 14:27
Add an optional `filePaths` field to `ShareData`/`ShareOptions` that accepts absolute filesystem paths. The files are shared directly from disk on macOS, iOS, and Windows instead of being copied from Base64, keeping the original filename intact.

- JS API: `ShareData.filePaths` and frontend validation.
- Rust: `ShareOptions.file_paths` with combined `MAX_FILES` and `MAX_FILE_PATH_BYTES` validation.
- macOS: converts paths to `NSURL` and appends them to the sharing picker items.
- iOS: converts paths to `URL` and appends them to `UIActivityViewController` activity items.
- Windows: uses `StorageFile::GetFileFromPathAsync` to obtain `IStorageItem`s for `DataTransferManager`.

Closes sudosylabs#13
@karlingen
karlingen force-pushed the feat/share-local-file-paths branch from ec5daa9 to d0e99bb Compare August 3, 2026 15:43
@karlingen
karlingen marked this pull request as ready for review August 3, 2026 15:59
@karlingen

Copy link
Copy Markdown
Contributor Author

@AbassHammed confirmed working on all platforms (after making a few tweaks). Ready for your review! Thanks.

@karlingen
karlingen marked this pull request as draft August 3, 2026 21:00
@karlingen

Copy link
Copy Markdown
Contributor Author

Found an additional issue on Windows. Please stand by

Switch `DataPackage.SetStorageItems` from `readOnly = false` to
`readOnly = true` so the share target receives a read-only token for
source files. This matches the standard `SetStorageItems` overload and
avoids failures when a source file cannot be opened for writing by the
target app.

Normalize any forward slashes to Windows backslashes before calling
`StorageFile::GetFileFromPathAsync`, because the WinRT API is stricter
about separator style than `std::path`.

Rewrite the Windows `DataRequested` handler to build the storage item
vector synchronously, add a `recv_timeout` so a missing deferral does
not hang forever, and parameterise `create_temp_file_for_data` by an
external `temp_dir` so it can be reused by the file-path handler. Add
unit tests for `create_temp_file_for_data`.

Also replace `eprintln!` with `log::error!` in the temp-file cleanup
and macOS logging paths, and apply `cargo fmt`.
@karlingen
karlingen force-pushed the feat/share-local-file-paths branch from efadd44 to eb76d56 Compare August 4, 2026 12:15
@karlingen
karlingen marked this pull request as ready for review August 4, 2026 12:43
@karlingen

Copy link
Copy Markdown
Contributor Author

@AbassHammed Fixed! Thanks for your patience.

@AbassHammed

Copy link
Copy Markdown
Contributor

Thanks for the update, Karl. The latest Windows approach looks good, but I found one blocker on Android.

filePaths are passed to FileProvider.getUriForFile(), while the provider currently exposes only <cache-path path="shares/" />. A normal existing file outside that cache directory will therefore throw IllegalArgumentException instead of being shared.

I also noticed that this change removes EXTRA_TEXT, EXTRA_TITLE, and EXTRA_SUBJECT from every Android share containing files, which appears to regress the existing mixed text + file API. Could you please address or clarify these two points before merging?

@karlingen

Copy link
Copy Markdown
Contributor Author

@AbassHammed, thanks for the review.

Both points are addressed in the latest commit:

  1. filePaths outside the configured FileProvider cache root
    SharePlugin.kt now first attempts FileProvider.getUriForFile with the original file. If the path is inside a declared FileProvider root, it's shared as-is; otherwise it falls back to copying into the plugin's cache shares/ directory, so it no longer throws IllegalArgumentException.

  2. Mixed text + file regression
    EXTRA_TEXT, EXTRA_TITLE, and EXTRA_SUBJECT are now added regardless of whether a file is attached, matching the previous behavior.

I also added a FileProvider and filePaths section to the README that explains how consumers can declare their own file_paths.xml roots for direct file sharing, and that the plugin will automatically fall back to a cache copy for paths outside any declared root. As long as the consumer's file_paths.xml keeps a cache-path entry (e.g. shares/), temporary/fallback files remain accessible.

Please re-review when you have a moment.

…ack and restore text extras

- Honor caller-declared FileProvider roots for filePaths on Android; fall
  back to copying files into the plugin's cache share directory when a
  path is outside a declared root, avoiding IllegalArgumentException.
- Restore EXTRA_TEXT, EXTRA_TITLE, and EXTRA_SUBJECT for shares that
  include files, fixing the mixed text + file API regression.
- Document the FileProvider/filePaths behavior in the README, including
  how consumers can declare custom roots and why a cache-path entry is
  still needed.
@karlingen
karlingen force-pushed the feat/share-local-file-paths branch from c47654b to 0404531 Compare August 4, 2026 15:23
@karlingen

Copy link
Copy Markdown
Contributor Author

@AbassHammed any plans on completing this PR and publishing a new RC? Thanks

@AbassHammed
AbassHammed merged commit d8186a7 into sudosylabs:master Aug 6, 2026
4 checks passed
@karlingen
karlingen deleted the feat/share-local-file-paths branch August 6, 2026 18:07
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.

Feature request: share local files by path (filePaths) to preserve original filenames

2 participants