diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor index 9460e42e..120b99f9 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor @@ -223,27 +223,6 @@ -
- -
- -
- -
@(_currentZoom)%
-
- -
- -
- @if (_totalSignatures > 0) {
@@ -351,9 +330,11 @@
@if (_pdfDocumentContent is not null && _pdfDocumentContent.Length > 0) { - }
@@ -558,9 +539,11 @@ int _currentPage = 1; int _totalPages = 0; int _currentZoom = 150; + double _viewerZoomLevel = 1.5; bool _showThumbnails = true; bool _isLoggingOut = false; DotNetObjectReference? _dotNetRef; + DxPdfViewer? _pdfViewer; IReadOnlyList _signatures = []; EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver.EnvelopeReceiverDto? _envelopeReceiver; ClaimsPrincipal? _receiverUser; @@ -766,6 +749,21 @@ await SetZoom(requestedZoom); } + async Task OnViewerZoomLevelChanged(double newZoomLevel) + { + _viewerZoomLevel = newZoomLevel; + + if (newZoomLevel > 0) + { + _currentZoom = (int)Math.Round(newZoomLevel * 100, MidpointRounding.AwayFromZero); + } + + await JSRuntime.InvokeVoidAsync("pdfViewer.setViewState", _currentPage, _currentZoom); + await Task.Delay(150); + await RenderSignatureButtonsAsync(); + await InvokeAsync(StateHasChanged); + } + async Task NextPage() { if (_currentPage >= _totalPages) @@ -799,7 +797,12 @@ async Task SetZoom(int percentage) { _currentZoom = Math.Clamp(percentage, 50, 300); - await ApplyViewerStateAsync(); + _viewerZoomLevel = _currentZoom / 100d; + + await InvokeAsync(StateHasChanged); + await JSRuntime.InvokeVoidAsync("pdfViewer.setViewState", _currentPage, _currentZoom); + await Task.Delay(150); + await RenderSignatureButtonsAsync(); } async Task OnZoomSliderChanged(ChangeEventArgs e) @@ -824,8 +827,13 @@ async Task FitToWidth() { + _viewerZoomLevel = -2; _currentZoom = 150; - await ApplyViewerStateAsync(); + + await InvokeAsync(StateHasChanged); + await JSRuntime.InvokeVoidAsync("pdfViewer.setViewState", _currentPage, _currentZoom); + await Task.Delay(150); + await RenderSignatureButtonsAsync(); } async Task ToggleThumbnails() @@ -1191,6 +1199,11 @@ if (!_pdfLoaded) return; + if (_viewerZoomLevel > 0) + { + _viewerZoomLevel = _currentZoom / 100d; + } + await InvokeAsync(StateHasChanged); await JSRuntime.InvokeVoidAsync("pdfViewer.setViewState", _currentPage, _currentZoom); await Task.Delay(150); diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js index d48104c3..9a6ff107 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js @@ -78,28 +78,6 @@ window.pdfViewer = { return; } - if (!this.wheelHandler) { - this.wheelHandler = async (e) => { - if (!(e.ctrlKey || e.metaKey) || !this.dotNetReference) { - return; - } - - e.preventDefault(); - - const step = this.qualityOptions.zoomStepPercentage; - const nextZoom = e.deltaY < 0 - ? Math.min(this.currentZoom + step, 300) - : Math.max(this.currentZoom - step, 50); - - if (nextZoom !== this.currentZoom) { - this.currentZoom = nextZoom; - await this.dotNetReference.invokeMethodAsync('OnZoomGestureRequested', nextZoom); - } - }; - - host.addEventListener('wheel', this.wheelHandler, { passive: false }); - } - if (!this.resizeObserver && typeof ResizeObserver !== 'undefined') { this.resizeObserver = new ResizeObserver(() => this.requestOverlayRefresh()); this.resizeObserver.observe(host); diff --git a/RECEIVER_PDF_VIEWER_CONTEXT.md b/RECEIVER_PDF_VIEWER_CONTEXT.md index 28c56e57..ea46ee2c 100644 --- a/RECEIVER_PDF_VIEWER_CONTEXT.md +++ b/RECEIVER_PDF_VIEWER_CONTEXT.md @@ -939,6 +939,77 @@ That is the safest first vertical slice because: - it reduces uncertainty in overlay scaling - it does not yet require full signature flow completion +### 9. Verified DevExpress API findings and current progress + +The following points are now verified from DevExpress documentation for `DxPdfViewer`: + +- `ZoomLevel` is a real component parameter +- positive `ZoomLevel` values are interpreted as percentages +- `ZoomLevelChanged` is supported and should be used to synchronize viewer zoom with page state +- `CustomizeToolbar` can clear all built-in toolbar items through `ToolbarModel.AllItems.Clear()` +- `ActivePageIndex` is read-only information and is not a page-navigation parameter + +### 10. Recovery progress update + +The first functional recovery step is zoom restoration. + +Implemented direction: + +- bind custom receiver toolbar zoom controls to `DxPdfViewer.ZoomLevel` +- synchronize zoom changes back into `_currentZoom` through `ZoomLevelChanged` +- remove competing custom JS ctrl+wheel zoom logic +- keep overlay redraw pipeline after confirmed zoom changes + +Expected outcome after this step: + +- toolbar zoom buttons visibly affect the DevExpress viewer +- zoom slider visibly affects the DevExpress viewer +- overlay refresh is triggered from real viewer zoom state instead of guessed zoom state + +### 11. Additional findings after first zoom integration attempt + +After the first live integration attempt against the installed `DevExpress.Blazor.PdfViewer` package version `25.2.3`, one important runtime behavior was confirmed: + +- the `DxPdfViewer.ZoomLevel` value must be treated as a zoom factor for normal positive values in the live UI flow used here +- in practice, `1.5` corresponds to `150%` +- using `150` as the bound value causes the DevExpress viewer UI to display `15000%` + +This means the receiver page must keep two different zoom representations: + +- `_currentZoom` = receiver custom workflow percentage view, for example `150` +- `_viewerZoomLevel` = DevExpress viewer value, for example `1.5` + +### 12. Current UI decision for zoom controls + +The custom toolbar zoom section in `pdf-toolbar__zoom-section` is no longer considered desirable. + +Current decision: + +- remove the custom zoom buttons and slider from the receiver toolbar +- rely on the built-in DevExpress PDF Viewer zoom UI instead +- keep `ZoomLevelChanged` synchronization so overlay redraw logic can still react to viewer zoom changes + +Reason: + +- DevExpress already provides zoom UX +- duplicate zoom controls create confusing UX and unit mismatch risks +- the built-in viewer zoom UI is a better source of truth for the current zoom state + +### 13. Current UI decision for thumbnails + +The custom thumbnail sidebar is still kept for now. + +Reason: + +- current receiver workflow depends on custom thumbnail shell behavior +- width persistence and resizable splitter are already implemented in the custom sidebar +- no verified built-in `DxPdfViewer` thumbnail sidebar integration surface has yet been confirmed from the currently inspected API surface + +So the current decision is: + +- keep custom thumbnail sidebar for now +- revisit possible DevExpress-native thumbnail navigation later only if it supports the required receiver workflow behavior + --- ## Files Most Likely Relevant For Future Work