Skip to content

Implement file sorting and enhance upload handling with fixes#2

Merged
Creeper19472 merged 5 commits into
mainfrom
perf-load
Jun 18, 2026
Merged

Implement file sorting and enhance upload handling with fixes#2
Creeper19472 merged 5 commits into
mainfrom
perf-load

Conversation

@Creeper19472

@Creeper19472 Creeper19472 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary by Sourcery

Introduce performant, worker-backed file sorting and virtualization for large directories while improving auth handling, upload protocol robustness, and document size semantics.

New Features:

  • Add web worker–based file sorting with main-thread fallback and integrate it into directory loading and sorting workflows.
  • Introduce virtualized rendering for the file table to efficiently display large directories.

Bug Fixes:

  • Handle null or missing document sizes end-to-end and improve byte formatting for unknown and zero sizes.
  • Allow zero-byte uploads to complete when the server responds with a stop signal and validate stop for non-empty uploads.
  • Ensure directory listing responses are deserialized with typed payloads and preserve unknown document sizes.
  • Fix selection logic to operate on the currently loaded folder and document lists instead of filtered views.
  • Rewrap and upload preference DEKs before password changes and roll back on failure to avoid losing server state.

Enhancements:

  • Refactor login handling to reuse shared helpers for sending requests and applying successful responses.
  • Reuse a generic typed action request helper for browsing commands to simplify response parsing.
  • Improve file name sorting to use locale-aware, numeric-aware collation and more accurate field comparisons.
  • Refine file table styling, including sticky headers and parent navigation visibility, and constrain scroll behavior for large lists.

Build:

  • Bump application version to 0.27.3 across Rust workspace, npm package, and iOS metadata.

Documentation:

  • Update in-app changelog with v0.27.3 release notes describing new file sorting, DEK handling, and size handling improvements.

Tests:

  • Add unit tests for upload ready-signal parsing, including stop behavior and chunk size handling.
  • Add unit tests to ensure list_directory preserves unknown document sizes in responses.

@sourcery-ai

sourcery-ai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors login handling and shared request utilities, adds safer DEK rewrap logic during password changes, introduces worker-backed and virtualized file table sorting with better handling of unknown file sizes, and tightens upload protocol handling and versioning.

Sequence diagram for worker-backed file sorting

sequenceDiagram
    actor User
    participant FilesPage as +page.svelte
    participant SortWorkerClient as sort-worker-client.ts
    participant FileSortWorker as file-sort.worker.ts
    participant Sorting as sorting.ts

    User->>FilesPage: loadDirectory / setSort
    FilesPage->>FilesPage: applyDirectoryEntries
    alt shouldDeferFileSort == false
        FilesPage->>Sorting: sortFileEntries
        Sorting-->>FilesPage: SortedFileEntries
        FilesPage->>FilesPage: update folders, documents
    else shouldDeferFileSort == true
        FilesPage->>FilesPage: queueDirectorySort
        FilesPage->>SortWorkerClient: sortFileEntriesAsync
        alt Worker unsupported or threshold not met
            SortWorkerClient->>Sorting: sortFileEntries
            Sorting-->>SortWorkerClient: SortedFileEntries
            SortWorkerClient-->>FilesPage: SortedFileEntries
        else Worker available
            SortWorkerClient->>SortWorkerClient: getSortWorker
            SortWorkerClient->>FileSortWorker: postMessage SortRequest
            FileSortWorker->>Sorting: sortFileEntries
            Sorting-->>FileSortWorker: SortedFileEntries
            FileSortWorker-->>SortWorkerClient: SortResponse
            SortWorkerClient-->>FilesPage: SortedFileEntries
        end
        FilesPage->>FilesPage: update folders, documents (if requestId matches)
    end
Loading

Sequence diagram for DEK rewrap during password change

sequenceDiagram
    actor User
    participant ChangePassword as change_password
    participant State as AppHandleState
    participant Auth as get_connection_auth
    participant Transfer as rewrap_and_upload_preference_dek
    participant Server as set_passwd

    User->>ChangePassword: change_password(old_password, new_password)
    ChangePassword->>State: read dek
    alt dek exists
        ChangePassword->>Auth: get_connection_auth
        alt auth matches username and token != pending_2fa
            ChangePassword->>Transfer: rewrap_and_upload_preference_dek(new_password)
            Transfer-->>ChangePassword: encrypted
            ChangePassword->>ChangePassword: prepared_dek_rewrap = Some(dek, encrypted,...)
        else
            ChangePassword->>ChangePassword: prepared_dek_rewrap = None
        end
    else dek missing
        ChangePassword->>ChangePassword: prepared_dek_rewrap = None
    end

    ChangePassword->>Server: set_passwd
    Server-->>ChangePassword: Response

    alt response.code != 200 and prepared_dek_rewrap
        ChangePassword->>Transfer: rewrap_and_upload_preference_dek(old_password)
        alt rollback ok
            Transfer-->>ChangePassword: encrypted_old
            ChangePassword->>State: remember_server_preference_dek(encrypted_old)
            ChangePassword-->>User: Err(response)
        else rollback failed
            Transfer-->>ChangePassword: Err
            ChangePassword-->>User: Err(response + rollback error)
        end
    else response.code != 200 and no prepared_dek_rewrap
        ChangePassword-->>User: Err(response)
    else response.code == 200 and prepared_dek_rewrap
        ChangePassword->>State: store dek
        ChangePassword->>State: remember_server_preference_dek(encrypted)
        ChangePassword-->>User: Ok
    else response.code == 200 and no prepared_dek_rewrap
        ChangePassword-->>User: Ok
    end
Loading

Flow diagram for upload ready signal parsing

flowchart TD
    A["parse_ready_signal(ready_str, file_size)"] --> B{"ready_str == stop?"}
    B -- Yes --> C{"file_size == 0?"}
    C -- Yes --> D["return Ok(None)<br/>(complete zero-byte upload)"]
    C -- No --> E["return Err(server sent stop<br/>for a non-empty upload)"]

    B -- No --> F["chunk_size = ready_str.strip_prefix('ready ')<br/>.split_whitespace().next()<br/>.parse().unwrap_or(8192)"]
    F --> G["return Ok(Some(chunk_size))"]
Loading

File-Level Changes

Change Details Files
Refactor login/auth request handling and shared typed responses
  • Extract login send/receive logic into send_login_request helper
  • Extract common login state mutation into apply_successful_login_response, with options to clear DEK and tasks
  • Introduce generic send_typed_action_request to deserialize typed response payloads
  • Update list_directory to use typed response deserialization
src-tauri/src/commands/auth_connection.rs
src-tauri/src/commands/shared_helpers.rs
src-tauri/src/commands/browsing.rs
Improve password change flow to safely rewrap and persist preference DEK
  • Precompute DEK rewrap before set_passwd when a DEK exists and auth matches
  • On password change failure, attempt to rewrap DEK back with old password and surface combined error
  • On success, restore in-memory DEK and remember server preference DEK using precomputed ciphertext
src-tauri/src/commands/auth_connection.rs
Implement performant file sorting with worker offload and table virtualization
  • Add sortFileEntries helper and name-aware comparators with Intl.Collator
  • Introduce sort-worker-client and file-sort.worker to offload large sorts, with threshold-based fallback
  • Wire directory loading and sort interactions to use async/background sorting with request IDs and cancellation
  • Virtualize FileTable rendering with scroll viewport, overscan, and sticky header for large directories
src/lib/files/sorting.ts
src/lib/files/sort-worker-client.ts
src/lib/components/files/FileTable.svelte
src/routes/home/files/+page.svelte
src/lib/files/file-sort.worker.ts
Handle unknown document sizes and improve size formatting
  • Change ServerDocumentEntry.size and related TS types to be nullable Option/number
  • Ensure list_directory preserves missing/null sizes via tests
  • Update formatBytes to handle null/undefined and show em dash or zero appropriately
  • Fix upload sheet to pass possibly-null size into formatter
crates/core/src/types.rs
src/lib/api/types.ts
src/lib/files/formatting.ts
src/routes/home/files/+page.svelte
Tighten upload ready/stop protocol handling
  • Extract parse_ready_signal to interpret server ready/stop messages and determine chunk size
  • Treat stop for zero-byte uploads as a successful short-circuit, but error for non-empty uploads
  • Add unit tests validating stop behavior and ready chunk size parsing
crates/transfer/src/upload.rs
Version and changelog updates for v0.27.3
  • Add v0.27.3 entry to in-app changelog documenting new features and fixes
  • Bump app version across workspace, package.json, iOS Info.plist, and Tauri config
src/lib/changelog/CHANGELOG.md
Cargo.toml
package.json
src-tauri/gen/ios/CFMS Client/Info.plist
Cargo.lock
src-tauri/tauri.conf.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

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

Hey - I've found 1 issue, and left some high level feedback:

  • The new send_typed_action_request helper is currently private to shared_helpers.rs, but browsing.rs calls it directly; you’ll need to make it visible (e.g. pub(crate) / pub(super) or re-expose it) to avoid a compile error.
  • In FileTable.svelte, .file-table-scroll-viewport has overflow: hidden when not virtualized, which can prevent vertical scrolling in constrained layouts; consider preserving overflow-y: auto (or matching the previous behavior) for non-virtualized lists as well.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `send_typed_action_request` helper is currently private to `shared_helpers.rs`, but `browsing.rs` calls it directly; you’ll need to make it visible (e.g. `pub(crate)` / `pub(super)` or re-expose it) to avoid a compile error.
- In `FileTable.svelte`, `.file-table-scroll-viewport` has `overflow: hidden` when not virtualized, which can prevent vertical scrolling in constrained layouts; consider preserving `overflow-y: auto` (or matching the previous behavior) for non-virtualized lists as well.

## Individual Comments

### Comment 1
<location path="crates/transfer/src/upload.rs" line_range="105-108" />
<code_context>
-        .and_then(|s| s.split_whitespace().next())
-        .and_then(|n| n.parse().ok())
-        .unwrap_or(8192);
+    let Some(chunk_size) = parse_ready_signal(&ready_str, file_size)? else {
+        on_progress(0, file_size);
+        return Ok(());
+    };
</code_context>
<issue_to_address>
**suggestion:** Zero-byte uploads report progress as (0, 0) instead of a completed state, which may be surprising for callers.

When `parse_ready_signal` returns `Ok(None)` (zero-byte upload), `send` invokes `on_progress(0, file_size)` and returns `Ok(())`. With `file_size == 0`, the callback always sees `(0, 0)`. If callers treat completion as `(total, total)`, this may not be recognized as finished.

Consider either:
- Invoking `on_progress(file_size, file_size)` specifically for zero-byte completion, or
- Clearly documenting `(0, 0)` as the "completed empty upload" state and ensuring callers handle it accordingly.

```suggestion
    let Some(chunk_size) = parse_ready_signal(&ready_str, file_size)? else {
        // Zero-byte upload: treat as completed and report (total, total)
        on_progress(file_size, file_size);
        return Ok(());
    };
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +105 to +108
let Some(chunk_size) = parse_ready_signal(&ready_str, file_size)? else {
on_progress(0, file_size);
return Ok(());
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: Zero-byte uploads report progress as (0, 0) instead of a completed state, which may be surprising for callers.

When parse_ready_signal returns Ok(None) (zero-byte upload), send invokes on_progress(0, file_size) and returns Ok(()). With file_size == 0, the callback always sees (0, 0). If callers treat completion as (total, total), this may not be recognized as finished.

Consider either:

  • Invoking on_progress(file_size, file_size) specifically for zero-byte completion, or
  • Clearly documenting (0, 0) as the "completed empty upload" state and ensuring callers handle it accordingly.
Suggested change
let Some(chunk_size) = parse_ready_signal(&ready_str, file_size)? else {
on_progress(0, file_size);
return Ok(());
};
let Some(chunk_size) = parse_ready_signal(&ready_str, file_size)? else {
// Zero-byte upload: treat as completed and report (total, total)
on_progress(file_size, file_size);
return Ok(());
};

@Creeper19472 Creeper19472 merged commit c1d676c into main Jun 18, 2026
7 checks passed
@Creeper19472 Creeper19472 deleted the perf-load branch June 18, 2026 02:47
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