Skip to content

feat(PdfReader): add OnDownloadAsync function#727

Merged
ArgoZhang merged 2 commits intomasterfrom
feat-download
Nov 27, 2025
Merged

feat(PdfReader): add OnDownloadAsync function#727
ArgoZhang merged 2 commits intomasterfrom
feat-download

Conversation

@ArgoZhang
Copy link
Copy Markdown
Member

@ArgoZhang ArgoZhang commented Nov 26, 2025

Link issues

fixes #726

Summary By Copilot

Regression?

  • Yes
  • No

Risk

  • High
  • Medium
  • Low

Verification

  • Manual (required)
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

☑️ Self Check before Merge

⚠️ Please check all items below before review. ⚠️

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • Merge the latest code from the main branch

Summary by Sourcery

Expose PdfReader configuration as direct component parameters and add hooks for toolbar behavior.

New Features:

  • Add component parameters to control toolbar visibility, download button, thumbnails, page number, zoom, fit-to-page mode, and two-pages-one-view mode directly on PdfReader.
  • Introduce callback parameters for page initialization, page load completion, page changes, and toggling two-pages-one-view mode on PdfReader.
  • Add an optional OnDownloadAsync callback to allow custom handling of the download button action.

Enhancements:

  • Refactor PdfReader to remove the PdfReaderOptions class in favor of strongly-typed parameters and synchronize these with the underlying JS viewer state.
  • Improve two-pages-one-view handling in JavaScript by centralizing spread mode toggling and reusing a shared resetTwoPagesOneView helper.

Copilot AI review requested due to automatic review settings November 26, 2025 10:50
@bb-auto bb-auto Bot added the enhancement New feature or request label Nov 26, 2025
@bb-auto bb-auto Bot added this to the v9.2.0 milestone Nov 26, 2025
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Nov 26, 2025

Reviewer's Guide

Refactors the PdfReader component to inline its configuration parameters instead of using PdfReaderOptions, adds new toolbar and download customization parameters, and updates the JS interop to support two-page view toggling and a customizable download action.

Sequence diagram for PdfReader download click handling

sequenceDiagram
    actor User
    participant PdfReader
    participant OnDownloadAsync_delegate as OnDownloadAsync

    User->>PdfReader: click download button
    PdfReader->>PdfReader: OnDownload()
    alt Custom download handler provided
        PdfReader->>OnDownloadAsync_delegate: Invoke()
        OnDownloadAsync_delegate-->>PdfReader: Task completed
    else No custom handler
        PdfReader->>PdfReader: Use default component behavior
    end
Loading

Sequence diagram for two-page view initialization and toggling

sequenceDiagram
    actor User
    participant PdfReader
    participant JsModule as PdfReaderJs
    participant PdfViewer

    rect rgb(230,230,250)
    PdfReader->>JsModule: InvokeInitAsync(init, options)
    JsModule->>PdfViewer: create viewer with options
    PdfReader->>JsModule: setPages(id, EnableTwoPagesOneView)
    JsModule->>PdfViewer: set spreadMode (0 or 1)
    JsModule->>JsModule: resetTwoPagesOneView(el, pdfViewer)
    JsModule->>PdfViewer: attach click handler for .dropdown-item-pages
    end

    rect rgb(220,245,220)
    User->>PdfReader: click dropdown-item-pages (Blazor onclick)
    PdfReader->>PdfReader: OnToggleTwoPagesOneView()
    PdfReader->>JsModule: setPages(id, EnableTwoPagesOneView)
    JsModule->>PdfViewer: update spreadMode
    end

    rect rgb(220,235,245)
    User->>JsModule: click .dropdown-item-pages (DOM handler)
    JsModule->>PdfViewer: toggle spreadMode between 0 and 1
    end
Loading

Class diagram for updated PdfReader component

classDiagram
    class PdfReader {
        +bool ShowToolbar
        +bool ShowDownload
        +bool EnableThumbnails
        +string Url
        +string ViewHeight
        +uint CurrentPage
        +string CurrentScale
        +bool IsFitToPage
        +bool ShowTwoPagesOneViewButton
        +bool EnableTwoPagesOneView
        +string MoreButtonIcon
        +Func_int_Task_ OnPagesInitAsync
        +Func_int_Task_ OnPagesLoadedAsync
        +Func_uint_Task_ OnPageChangedAsync
        +Func_bool_Task_ OnTwoPagesOneViewAsync
        +Func_Task_ OnDownloadAsync
        -string _docTitle
        -bool _isFitToPage
        -uint _currentPage
        -string _url
        -string _currentScale
        -bool _enableTwoPagesOneView
        -bool _showTwoPagesOneViewButton
        -string _twoPagesOneViewIcon
        -HashSet_string_ AllowedScaleValues
        +void FitToPage()
        +void FitToWidth()
        +Task RotateLeft()
        +Task RotateRight()
        +Task PagesInit(int pagesCount)
        +Task PagesLoaded(int pagesCount)
        +Task PageChanged(uint pageIndex)
        -Task OnDownload()
        -Task InvokeInitAsync()
        -Task TriggerFit(string methodName)
    }

    class DownloadService

    PdfReader --> DownloadService : injects

    class PdfReaderOptions {
    }
    <<removed>> PdfReaderOptions
Loading

File-Level Changes

Change Details Files
Refactor PdfReader to use direct component parameters instead of PdfReaderOptions and wire them through lifecycle, JS interop, and UI.
  • Replace PdfReaderOptions parameter with individual parameters for toolbar visibility, download visibility, thumbnails, URL, view height, current page/scale, fit-to-page, and two-page view options.
  • Update computed CSS class and style builders to use the new parameters for view height and fit-to-page mode.
  • Adjust OnParametersSet to initialize defaults such as current page, document title, and icons using the new parameters.
  • Synchronize backing fields with the new parameters in OnAfterRenderAsync and trigger JS interop calls when values change.
  • Update helper methods like FitToPage, FitToWidth, and JSInvokable callbacks to read/write the new parameters and invoke the provided callbacks.
src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs
src/components/BootstrapBlazor.PdfReader/PdfReader.razor
Introduce a customizable download action on the PdfReader toolbar.
  • Add an OnDownloadAsync callback parameter that, when provided, is invoked when the download button is clicked.
  • Wire the toolbar download icon to call OnDownload via @OnClick instead of being a passive icon.
  • Inject DownloadService for future/alternative download implementations (not yet used in this diff).
src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs
src/components/BootstrapBlazor.PdfReader/PdfReader.razor
Adjust JS interop logic for two-page view toggling and event binding.
  • Rename setPages parameter to enableTwoPagesOneView and ensure it calls a new resetTwoPagesOneView helper to (re)bind the click handler.
  • Extract the two-page view click binding into resetTwoPagesOneView and reuse it from both setPages and addEventListener.
  • Keep pdfViewer.spreadMode toggling behavior but centralize it so it can be refreshed when state changes from .NET.
src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js
Remove the PdfReaderOptions configuration class in favor of the new parameterized API.
  • Delete PdfReaderOptions.cs, as its properties and callbacks are now represented directly on the PdfReader component.
src/components/BootstrapBlazor.PdfReader/PdfReaderOptions.cs

Assessment against linked issues

Issue Objective Addressed Explanation
#726 Add an OnDownloadAsync callback function to the PdfReader component that can be invoked when the download button is clicked.

Possibly linked issues


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

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes and found some issues that need to be addressed.

  • The new OnDownloadAsync parameter currently short-circuits the default behavior (no download occurs when it is null) and the injected DownloadService is unused, so either wire up the built-in download logic when OnDownloadAsync is null or remove the misleading XML doc/comment and the unused service.
  • resetTwoPagesOneView is called from both setPages and addEventListener, which can result in multiple click handlers being attached to .dropdown-item-pages; consider calling it only once or using a guard to avoid handler duplication.
  • Some of the XML summaries still reference the old PdfReaderOptions semantics (e.g., the comment on OnPageChangedAsync says '页面初始化回调方法'); updating these to accurately reflect the new parameter meanings will make the API clearer.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `OnDownloadAsync` parameter currently short-circuits the default behavior (no download occurs when it is null) and the injected `DownloadService` is unused, so either wire up the built-in download logic when `OnDownloadAsync` is null or remove the misleading XML doc/comment and the unused service.
- `resetTwoPagesOneView` is called from both `setPages` and `addEventListener`, which can result in multiple click handlers being attached to `.dropdown-item-pages`; consider calling it only once or using a guard to avoid handler duplication.
- Some of the XML summaries still reference the old `PdfReaderOptions` semantics (e.g., the comment on `OnPageChangedAsync` says '页面初始化回调方法'); updating these to accurately reflect the new parameter meanings will make the API clearer.

## Individual Comments

### Comment 1
<location> `src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs:107-110` </location>
<code_context>
     public string? MoreButtonIcon { get; set; }

+    /// <summary>
+    /// 点击下载按钮回调方法 默认 null 使用组件内置下载功能
+    /// </summary>
+    [Parameter]
+    public Func<Task>? OnDownloadAsync { get; set; }
+
+    [Inject, NotNull]
</code_context>

<issue_to_address>
**issue (bug_risk):** Default download behavior is effectively a no-op and `DownloadService` is unused.

The summary comment says `OnDownloadAsync == null` should trigger the built-in download and you inject a `DownloadService`, but `OnDownload()` currently only invokes the callback and otherwise does nothing. As a result, the default download button does nothing unless a consumer wires a handler. Either wire the default behavior via `DownloadService` when `OnDownloadAsync` is null, or change the parameter semantics/docs to require a handler. If you keep a built-in behavior, also guard against null/empty `Url` before invoking the download.
</issue_to_address>

### Comment 2
<location> `src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js:91-100` </location>
<code_context>
 }

-export function setPages(id, enableTowPagesOnView) {
+export function setPages(id, enableTwoPagesOneView) {
     const { el, pdfViewer } = Data.get(id);
     if (pdfViewer) {
-        if (enableTowPagesOnView) {
+        if (enableTwoPagesOneView) {
             pdfViewer.spreadMode = 1;
         }
         else {
             pdfViewer.spreadMode = 0;
         }
     }
+
+    resetTwoPagesOneView(el, pdfViewer);
+}
+
</code_context>

<issue_to_address>
**issue (bug_risk):** Repeated calls to `setPages` will re-register the same click handler, potentially causing multiple toggles per click.

`resetTwoPagesOneView` registers a `click` handler on `.dropdown-item-pages`, and it’s now called from both `addEventListener` and `setPages`. Without cleanup or a guard, each `setPages` call will add another listener, so one click can toggle `spreadMode` multiple times. Please either move this listener setup to a one-time initialization path or make `resetTwoPagesOneView` idempotent (e.g., remove existing handlers or ensure you only attach once per element).
</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 +107 to +110
/// 点击下载按钮回调方法 默认 null 使用组件内置下载功能
/// </summary>
[Parameter]
public Func<Task>? OnDownloadAsync { get; set; }
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Default download behavior is effectively a no-op and DownloadService is unused.

The summary comment says OnDownloadAsync == null should trigger the built-in download and you inject a DownloadService, but OnDownload() currently only invokes the callback and otherwise does nothing. As a result, the default download button does nothing unless a consumer wires a handler. Either wire the default behavior via DownloadService when OnDownloadAsync is null, or change the parameter semantics/docs to require a handler. If you keep a built-in behavior, also guard against null/empty Url before invoking the download.

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 refactors the PdfReader component by removing the PdfReaderOptions class and promoting all its properties to direct component parameters. This change simplifies the API by eliminating the need for a separate options object. Additionally, the PR adds the OnDownloadAsync callback function to allow customization of download behavior, and adds a ShowDownload parameter to control download button visibility.

Key changes:

  • Removed PdfReaderOptions class and converted all options to direct component parameters
  • Added ShowDownload parameter and OnDownloadAsync callback for customizable download behavior
  • Fixed naming inconsistency from "Tow" to "Two" in parameter and method names (e.g., ShowTwoPagesOneViewButton)

Reviewed changes

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

File Description
PdfReaderOptions.cs Removed - entire class deleted as options are now direct component parameters
PdfReader.razor.cs Converted from using PdfReaderOptions to direct parameters; added ShowDownload, OnDownloadAsync, and DownloadService injection
PdfReader.razor Updated to reference component properties directly instead of through Options object; added conditional rendering for download button
PdfReader.razor.js Fixed typo in parameter name from enableTowPagesOnView to enableTwoPagesOneView; refactored event handler setup
Comments suppressed due to low confidence (1)

src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js:305

  • Variable name towPagesOneView should be twoPagesOneView to be consistent with the naming corrections made elsewhere in this PR (e.g., line 91 parameter enableTwoPagesOneView, line 105 function resetTwoPagesOneView).
        const towPagesOneView = el.querySelector(".dropdown-item-pages");
        if (towPagesOneView) {
            EventHandler.off(towPagesOneView, "click");

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

Comment thread src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs
Comment thread src/components/BootstrapBlazor.PdfReader/PdfReader.razor.cs
Comment thread src/components/BootstrapBlazor.PdfReader/PdfReader.razor.js
@ArgoZhang ArgoZhang merged commit dffb489 into master Nov 27, 2025
9 checks passed
@ArgoZhang ArgoZhang deleted the feat-download branch November 27, 2025 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(PdfReader): add OnDownloadAsync function

2 participants