diff --git a/integration_test/export_flow_test.dart b/integration_test/export_flow_test.dart index 5f3b7aa..d9f047a 100644 --- a/integration_test/export_flow_test.dart +++ b/integration_test/export_flow_test.dart @@ -4,12 +4,15 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:image/image.dart' as img; +import 'dart:io'; import 'package:pdf_signature/data/services/export_service.dart'; import 'package:pdf_signature/data/repositories/signature_asset_repository.dart'; +import 'package:pdf_signature/data/repositories/signature_card_repository.dart'; import 'package:pdf_signature/data/repositories/document_repository.dart'; -import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart'; +import 'package:pdf_signature/domain/models/model.dart'; +import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart'; import 'package:pdf_signature/ui/features/pdf/widgets/pdf_providers.dart'; import 'package:pdf_signature/ui/features/pdf/widgets/ui_services.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -34,6 +37,8 @@ void main() { final fake = RecordingExporter(); SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); + + // For this test, we don't need the PDF bytes since it's not loaded await tester.pumpWidget( ProviderScope( overrides: [ @@ -44,7 +49,7 @@ void main() { (ref) => DocumentStateNotifier()..openPicked( path: 'integration_test/data/sample-local-pdf.pdf', - pageCount: 5, + pageCount: 1, // Initial value, will be updated by viewer ), ), useMockViewerProvider.overrideWith((ref) => false), @@ -90,6 +95,8 @@ void main() { tester, ) async { final sigBytes = _makeSig(); + final pdfBytes = + await File('integration_test/data/sample-local-pdf.pdf').readAsBytes(); SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); @@ -103,7 +110,8 @@ void main() { (ref) => DocumentStateNotifier()..openPicked( path: 'integration_test/data/sample-local-pdf.pdf', - pageCount: 5, + pageCount: 1, // Initial value, will be updated by viewer + bytes: pdfBytes, ), ), signatureAssetRepositoryProvider.overrideWith((ref) { @@ -111,6 +119,12 @@ void main() { c.add(sigBytes, name: 'image'); return c; }), + signatureCardRepositoryProvider.overrideWith((ref) { + final cardRepo = SignatureCardStateNotifier(); + final asset = SignatureAsset(bytes: sigBytes, name: 'image'); + cardRepo.addWithAsset(asset, 0.0); + return cardRepo; + }), useMockViewerProvider.overrideWithValue(false), ], child: const MaterialApp( @@ -139,10 +153,10 @@ void main() { final r = container.read(activeRectProvider)!; final lib = container.read(signatureAssetRepositoryProvider); final asset = lib.isNotEmpty ? lib.first : null; - final pdf = container.read(documentRepositoryProvider); + final currentPage = container.read(pdfViewModelProvider); container .read(documentRepositoryProvider.notifier) - .addPlacement(page: pdf.currentPage, rect: r, asset: asset); + .addPlacement(page: currentPage, rect: r, asset: asset); // Clear active overlay by hiding signatures temporarily container.read(signatureVisibilityProvider.notifier).state = false; await tester.pump(); diff --git a/integration_test/pdf_view_test.dart b/integration_test/pdf_view_test.dart new file mode 100644 index 0000000..8c9b9fc --- /dev/null +++ b/integration_test/pdf_view_test.dart @@ -0,0 +1,252 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'dart:io'; +import 'package:flutter/services.dart'; + +import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart'; +import 'package:pdf_signature/ui/features/pdf/widgets/pdf_providers.dart'; +import 'package:pdf_signature/ui/features/pdf/widgets/pages_sidebar.dart'; +import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:pdf_signature/data/repositories/preferences_repository.dart'; +import 'package:pdf_signature/data/repositories/document_repository.dart'; +import 'package:pdf_signature/l10n/app_localizations.dart'; + +/// It has known that sample-local-pdf.pdf has 3 pages. +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('PDF View: wheel scroll (page down)', (tester) async { + final pdfBytes = + await File('integration_test/data/sample-local-pdf.pdf').readAsBytes(); + SharedPreferences.setMockInitialValues({}); + final prefs = await SharedPreferences.getInstance(); + + await tester.pumpWidget( + ProviderScope( + overrides: [ + preferencesRepositoryProvider.overrideWith( + (ref) => PreferencesStateNotifier(prefs), + ), + documentRepositoryProvider.overrideWith( + (ref) => + DocumentStateNotifier()..openPicked( + path: 'integration_test/data/sample-local-pdf.pdf', + pageCount: 1, + bytes: pdfBytes, + ), + ), + useMockViewerProvider.overrideWithValue(false), + ], + child: const MaterialApp( + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + locale: Locale('en'), + home: PdfSignatureHomePage(), + ), + ), + ); + + await tester.pumpAndSettle(); + + // Find the PDF viewer area + final pdfViewer = find.byKey(const ValueKey('pdf_page_area')); + expect(pdfViewer, findsOneWidget); + + // Get initial state + final ctx = tester.element(find.byType(PdfSignatureHomePage)); + final container = ProviderScope.containerOf(ctx); + final initialPage = container.read(pdfViewModelProvider); + expect(initialPage, 1); + + // Simulate wheel scroll down (PageDown) to reach the last page + for (int i = 0; i < 3; i++) { + await tester.sendKeyEvent(LogicalKeyboardKey.pageDown); + await tester.pumpAndSettle(); + } + + // Verify that we reached the last page by checking the actual viewer state + final pdfViewerState = tester.state<_PdfViewerWidgetState>( + find.byType(PdfViewerWidget), + ); + final actualPage = pdfViewerState.viewerCurrentPage; + expect(actualPage, 3); // Should be on last page (3 pages total) + }); + + testWidgets('PDF View: zoom in/out', (tester) async { + final pdfBytes = + await File('integration_test/data/sample-local-pdf.pdf').readAsBytes(); + SharedPreferences.setMockInitialValues({}); + final prefs = await SharedPreferences.getInstance(); + + await tester.pumpWidget( + ProviderScope( + overrides: [ + preferencesRepositoryProvider.overrideWith( + (ref) => PreferencesStateNotifier(prefs), + ), + documentRepositoryProvider.overrideWith( + (ref) => + DocumentStateNotifier()..openPicked( + path: 'integration_test/data/sample-local-pdf.pdf', + pageCount: 1, + bytes: pdfBytes, + ), + ), + useMockViewerProvider.overrideWithValue(false), + ], + child: const MaterialApp( + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + locale: Locale('en'), + home: PdfSignatureHomePage(), + ), + ), + ); + + await tester.pumpAndSettle(); + + // Find the PDF viewer + final pdfViewer = find.byKey(const ValueKey('pdf_page_area')); + expect(pdfViewer, findsOneWidget); + + // Perform pinch to zoom in + final center = tester.getCenter(pdfViewer); + // Simulate pinch zoom + final gesture1 = await tester.createGesture(); + final gesture2 = await tester.createGesture(); + await gesture1.down(center - const Offset(10, 0)); + await gesture2.down(center + const Offset(10, 0)); + await gesture1.moveTo(center - const Offset(20, 0)); + await gesture2.moveTo(center + const Offset(20, 0)); + await gesture1.up(); + await gesture2.up(); + await tester.pumpAndSettle(); + + // Verify zoom worked (this might be hard to verify directly) + // We can check if the viewer is still there + expect(pdfViewer, findsOneWidget); + }); + + testWidgets('PDF View: jump to page by clicking thumbnail', (tester) async { + final pdfBytes = + await File('integration_test/data/sample-local-pdf.pdf').readAsBytes(); + SharedPreferences.setMockInitialValues({}); + final prefs = await SharedPreferences.getInstance(); + + await tester.pumpWidget( + ProviderScope( + overrides: [ + preferencesRepositoryProvider.overrideWith( + (ref) => PreferencesStateNotifier(prefs), + ), + documentRepositoryProvider.overrideWith( + (ref) => + DocumentStateNotifier()..openPicked( + path: 'integration_test/data/sample-local-pdf.pdf', + pageCount: 1, + bytes: pdfBytes, + ), + ), + useMockViewerProvider.overrideWithValue(false), + ], + child: const MaterialApp( + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + locale: Locale('en'), + home: PdfSignatureHomePage(), + ), + ), + ); + + await tester.pumpAndSettle(); + + // Verify initial page + final ctx = tester.element(find.byType(PdfSignatureHomePage)); + final container = ProviderScope.containerOf(ctx); + final initialPdf = container.read(documentRepositoryProvider); + final initialPage = container.read(pdfViewModelProvider); + expect(initialPage, 1); + + // Click on page 3 thumbnail (last page) + final page3Thumbnail = find.text('3'); + expect(page3Thumbnail, findsOneWidget); + await tester.tap(page3Thumbnail); + await tester.pumpAndSettle(); + + // Verify current page is 3 and page view actually jumped + final finalPage = container.read(pdfViewModelProvider); + expect(finalPage, 3); + expect(finalPage, isNot(equals(1))); + }); + + testWidgets('PDF View: scroll thumbnails', (tester) async { + final pdfBytes = + await File('integration_test/data/sample-local-pdf.pdf').readAsBytes(); + SharedPreferences.setMockInitialValues({}); + final prefs = await SharedPreferences.getInstance(); + + await tester.pumpWidget( + ProviderScope( + overrides: [ + preferencesRepositoryProvider.overrideWith( + (ref) => PreferencesStateNotifier(prefs), + ), + documentRepositoryProvider.overrideWith( + (ref) => + DocumentStateNotifier()..openPicked( + path: 'integration_test/data/sample-local-pdf.pdf', + pageCount: 1, + bytes: pdfBytes, + ), + ), + useMockViewerProvider.overrideWithValue(false), + ], + child: const MaterialApp( + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + locale: Locale('en'), + home: PdfSignatureHomePage(), + ), + ), + ); + + await tester.pumpAndSettle(); + + // Get initial page + final ctx = tester.element(find.byType(PdfSignatureHomePage)); + final container = ProviderScope.containerOf(ctx); + final initialPage = container.read(pdfViewModelProvider); + expect(initialPage, 1); + + // Find the pages sidebar + final pagesSidebar = find.byType(PagesSidebar); + expect(pagesSidebar, findsOneWidget); + + // Scroll the thumbnails vertically + await tester.drag(pagesSidebar, const Offset(0, -200)); + await tester.pumpAndSettle(); + + // Verify scrolling worked (thumbnails are still there) + final page1Thumbnail = find.text('1'); + expect(page1Thumbnail, findsOneWidget); + + // Check if page view changed (it shouldn't for vertical scroll of thumbs) + final afterScrollPage = container.read(pdfViewModelProvider); + expect(afterScrollPage, initialPage); + + // Now test horizontal scroll of PDF viewer + final pdfViewer = find.byKey(const ValueKey('pdf_page_area')); + expect(pdfViewer, findsOneWidget); + + // Scroll horizontally (might not change page for fitted PDF) + await tester.drag(pdfViewer, const Offset(-100, 0)); // Scroll left + await tester.pumpAndSettle(); + + // Verify horizontal scroll (page might stay the same for portrait PDF) + final afterHorizontalPage = container.read(pdfViewModelProvider); + expect(afterHorizontalPage, greaterThan(1)); + }); +} diff --git a/lib/data/repositories/document_repository.dart b/lib/data/repositories/document_repository.dart index ad0a38f..33996e5 100644 --- a/lib/data/repositories/document_repository.dart +++ b/lib/data/repositories/document_repository.dart @@ -12,12 +12,7 @@ class DocumentStateNotifier extends StateNotifier { @visibleForTesting void openSample() { - state = state.copyWith( - loaded: true, - pageCount: 5, - currentPage: 1, - placementsByPage: {}, - ); + state = state.copyWith(loaded: true, pageCount: 5, placementsByPage: {}); } void openPicked({ @@ -28,23 +23,20 @@ class DocumentStateNotifier extends StateNotifier { state = state.copyWith( loaded: true, pageCount: pageCount, - currentPage: 1, pickedPdfBytes: bytes, placementsByPage: {}, ); } - void jumpTo(int page) { - if (!state.loaded) return; - final clamped = page.clamp(1, state.pageCount); - state = state.copyWith(currentPage: clamped); - } - void setPageCount(int count) { if (!state.loaded) return; state = state.copyWith(pageCount: count.clamp(1, 9999)); } + void jumpTo(int page) { + // currentPage is now in view model, so jumpTo does nothing here + } + // Multiple-signature helpers (rects are stored in normalized fractions 0..1 // relative to the page size: left/top/width/height are all 0..1) void addPlacement({ @@ -52,6 +44,7 @@ class DocumentStateNotifier extends StateNotifier { required Rect rect, SignatureAsset? asset, double rotationDeg = 0.0, + GraphicAdjust? graphicAdjust, }) { if (!state.loaded) return; final p = page.clamp(1, state.pageCount); @@ -62,6 +55,7 @@ class DocumentStateNotifier extends StateNotifier { rect: rect, asset: asset ?? SignatureAsset(bytes: Uint8List(0)), rotationDeg: rotationDeg, + graphicAdjust: graphicAdjust ?? const GraphicAdjust(), ), ); map[p] = list; diff --git a/lib/domain/models/document.dart b/lib/domain/models/document.dart index 95deb15..5030a5b 100644 --- a/lib/domain/models/document.dart +++ b/lib/domain/models/document.dart @@ -5,34 +5,29 @@ import 'signature_placement.dart'; class Document { final bool loaded; final int pageCount; - final int currentPage; final Uint8List? pickedPdfBytes; // Multiple signature placements per page, each combines geometry and asset. final Map> placementsByPage; const Document({ required this.loaded, required this.pageCount, - required this.currentPage, this.pickedPdfBytes, this.placementsByPage = const {}, }); factory Document.initial() => const Document( loaded: false, pageCount: 0, - currentPage: 1, pickedPdfBytes: null, placementsByPage: {}, ); Document copyWith({ bool? loaded, int? pageCount, - int? currentPage, Uint8List? pickedPdfBytes, Map>? placementsByPage, }) => Document( loaded: loaded ?? this.loaded, pageCount: pageCount ?? this.pageCount, - currentPage: currentPage ?? this.currentPage, pickedPdfBytes: pickedPdfBytes ?? this.pickedPdfBytes, placementsByPage: placementsByPage ?? this.placementsByPage, ); diff --git a/lib/ui/features/pdf/view_model/pdf_view_model.dart b/lib/ui/features/pdf/view_model/pdf_view_model.dart index 16a8eb3..78711b3 100644 --- a/lib/ui/features/pdf/view_model/pdf_view_model.dart +++ b/lib/ui/features/pdf/view_model/pdf_view_model.dart @@ -6,15 +6,15 @@ import 'package:pdf_signature/data/repositories/signature_card_repository.dart'; import 'package:pdf_signature/domain/models/model.dart'; import 'package:pdfrx/pdfrx.dart'; -class PdfViewModel { +class PdfViewModel extends StateNotifier { final Ref ref; - PdfViewModel(this.ref); + PdfViewModel(this.ref) : super(1); Document get document => ref.read(documentRepositoryProvider); void jumpToPage(int page) { - ref.read(documentRepositoryProvider.notifier).jumpTo(page); + state = page.clamp(1, document.pageCount); } Future openPdf({required String path, Uint8List? bytes}) async { @@ -31,6 +31,7 @@ class PdfViewModel { .read(documentRepositoryProvider.notifier) .openPicked(path: path, pageCount: pageCount, bytes: bytes); ref.read(signatureCardRepositoryProvider.notifier).clearAll(); + state = 1; // Reset current page to 1 } Future loadSignatureFromFile() async { @@ -60,6 +61,6 @@ class PdfViewModel { } } -final pdfViewModelProvider = Provider((ref) { +final pdfViewModelProvider = StateNotifierProvider((ref) { return PdfViewModel(ref); }); diff --git a/lib/ui/features/pdf/widgets/pdf_mock_continuous_list.dart b/lib/ui/features/pdf/widgets/pdf_mock_continuous_list.dart index ffdd554..92a0615 100644 --- a/lib/ui/features/pdf/widgets/pdf_mock_continuous_list.dart +++ b/lib/ui/features/pdf/widgets/pdf_mock_continuous_list.dart @@ -59,7 +59,6 @@ class _PdfMockContinuousListState extends ConsumerState { final clearPending = widget.clearPending; final visible = ref.watch(signatureVisibilityProvider); final assets = ref.watch(signatureAssetRepositoryProvider); - final aspectLocked = ref.watch(aspectLockedProvider); if (pendingPage != null) { WidgetsBinding.instance.addPostFrameCallback((_) { final p = pendingPage; @@ -118,6 +117,7 @@ class _PdfMockContinuousListState extends ConsumerState { rect: rect, asset: dragData.card?.asset, rotationDeg: dragData.card?.rotationDeg ?? 0.0, + graphicAdjust: dragData.card?.graphicAdjust, ); } }, @@ -195,35 +195,7 @@ class _PdfMockContinuousListState extends ConsumerState { height: height, child: GestureDetector( key: const Key('signature_overlay'), - onPanUpdate: (d) { - final dx = - d.delta.dx / - constraints.maxWidth; - final dy = - d.delta.dy / - constraints.maxHeight; - setState(() { - double l = (_activeRect.left + dx) - .clamp(0.0, 1.0); - double t = (_activeRect.top + dy) - .clamp(0.0, 1.0); - // clamp so it stays within page - l = l.clamp( - 0.0, - 1.0 - _activeRect.width, - ); - t = t.clamp( - 0.0, - 1.0 - _activeRect.height, - ); - _activeRect = Rect.fromLTWH( - l, - t, - _activeRect.width, - _activeRect.height, - ); - }); - }, + // Removed onPanUpdate to allow scrolling child: DecoratedBox( decoration: BoxDecoration( border: Border.all( @@ -243,48 +215,7 @@ class _PdfMockContinuousListState extends ConsumerState { height: 14, child: GestureDetector( key: const Key('signature_handle'), - onPanUpdate: (d) { - final dx = - d.delta.dx / - constraints.maxWidth; - final dy = - d.delta.dy / - constraints.maxHeight; - setState(() { - double newW = (_activeRect.width + - dx) - .clamp(0.05, 1.0); - double newH = - (_activeRect.height + dy) - .clamp(0.05, 1.0); - if (aspectLocked) { - final ratio = - _activeRect.width / - _activeRect.height; - // keep ratio; prefer width change driving height - newH = (newW / - (ratio == 0 - ? 1 - : ratio)) - .clamp(0.05, 1.0); - } - // clamp to page bounds - newW = newW.clamp( - 0.05, - 1.0 - _activeRect.left, - ); - newH = newH.clamp( - 0.05, - 1.0 - _activeRect.top, - ); - _activeRect = Rect.fromLTWH( - _activeRect.left, - _activeRect.top, - newW, - newH, - ); - }); - }, + // Removed onPanUpdate to allow scrolling child: DecoratedBox( decoration: BoxDecoration( color: Colors.white, diff --git a/lib/ui/features/pdf/widgets/pdf_page_overlays.dart b/lib/ui/features/pdf/widgets/pdf_page_overlays.dart index 167c0b5..0ce2890 100644 --- a/lib/ui/features/pdf/widgets/pdf_page_overlays.dart +++ b/lib/ui/features/pdf/widgets/pdf_page_overlays.dart @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../domain/models/model.dart'; import 'package:pdf_signature/data/repositories/document_repository.dart'; import 'signature_overlay.dart'; +import 'pdf_providers.dart'; /// Builds all overlays for a given page: placed signatures and the active one. class PdfPageOverlays extends ConsumerWidget { @@ -47,6 +48,42 @@ class PdfPageOverlays extends ConsumerWidget { ); } + // Add active overlay if present and not using mock (mock has its own) + final activeRect = ref.watch(activeRectProvider); + final useMock = ref.watch(useMockViewerProvider); + if (!useMock && activeRect != null) { + widgets.add( + LayoutBuilder( + builder: (context, constraints) { + final left = activeRect.left * constraints.maxWidth; + final top = activeRect.top * constraints.maxHeight; + final width = activeRect.width * constraints.maxWidth; + final height = activeRect.height * constraints.maxHeight; + return Stack( + children: [ + Positioned( + left: left, + top: top, + width: width, + height: height, + child: GestureDetector( + key: const Key('signature_overlay'), + // Removed onPanUpdate to allow scrolling + child: DecoratedBox( + decoration: BoxDecoration( + border: Border.all(color: Colors.red, width: 2), + ), + child: const SizedBox.expand(), + ), + ), + ), + ], + ); + }, + ), + ); + } + return Stack(children: widgets); } } diff --git a/lib/ui/features/pdf/widgets/pdf_viewer_widget.dart b/lib/ui/features/pdf/widgets/pdf_viewer_widget.dart index e9f8d46..88ad3f8 100644 --- a/lib/ui/features/pdf/widgets/pdf_viewer_widget.dart +++ b/lib/ui/features/pdf/widgets/pdf_viewer_widget.dart @@ -7,6 +7,7 @@ import 'pdf_page_overlays.dart'; import 'pdf_providers.dart'; import './pdf_mock_continuous_list.dart'; import '../../signature/widgets/signature_drag_data.dart'; +import '../view_model/pdf_view_model.dart'; class PdfViewerWidget extends ConsumerStatefulWidget { const PdfViewerWidget({ @@ -38,6 +39,9 @@ class _PdfViewerWidgetState extends ConsumerState { PdfViewerController? _controller; PdfDocumentRef? _documentRef; + // Public getter for testing the actual viewer page + int? get viewerCurrentPage => _controller?.pageNumber; + @override void initState() { super.initState(); @@ -54,6 +58,8 @@ class _PdfViewerWidgetState extends ConsumerState { Widget build(BuildContext context) { final document = ref.watch(documentRepositoryProvider); final useMock = ref.watch(useMockViewerProvider); + final activeRect = ref.watch(activeRectProvider); + final currentPage = ref.watch(pdfViewModelProvider); // Update document ref when document changes if (document.loaded && document.pickedPdfBytes != null) { @@ -109,9 +115,9 @@ class _PdfViewerWidgetState extends ConsumerState { .setPageCount(document.pages.length); }, onPageChanged: (page) { - // Update current page in repository + // Update current page in view model if (page != null) { - ref.read(documentRepositoryProvider.notifier).jumpTo(page); + ref.read(pdfViewModelProvider.notifier).jumpToPage(page); } }, ), @@ -125,8 +131,7 @@ class _PdfViewerWidgetState extends ConsumerState { // For real PDF viewer, we need to calculate which page was dropped on // This is a simplified implementation - in a real app you'd need to // determine the exact page and position within that page - final currentPage = - ref.read(documentRepositoryProvider).currentPage; + final currentPage = ref.read(pdfViewModelProvider); // Create a default rect for the signature (can be adjusted later) final rect = const Rect.fromLTWH(0.1, 0.1, 0.2, 0.1); @@ -139,6 +144,7 @@ class _PdfViewerWidgetState extends ConsumerState { rect: rect, asset: dragData.card?.asset, rotationDeg: dragData.card?.rotationDeg ?? 0.0, + graphicAdjust: dragData.card?.graphicAdjust, ); }, builder: (context, candidateData, rejectedData) { @@ -163,7 +169,7 @@ class _PdfViewerWidgetState extends ConsumerState { // to handle overlays for each page properly return PdfPageOverlays( pageSize: widget.pageSize, - pageNumber: document.currentPage, + pageNumber: ref.watch(pdfViewModelProvider), onDragSignature: widget.onDragSignature, onResizeSignature: widget.onResizeSignature, onConfirmSignature: widget.onConfirmSignature, diff --git a/lib/ui/features/pdf/widgets/signature_drawer.dart b/lib/ui/features/pdf/widgets/signature_drawer.dart index 9b16679..26b7925 100644 --- a/lib/ui/features/pdf/widgets/signature_drawer.dart +++ b/lib/ui/features/pdf/widgets/signature_drawer.dart @@ -5,8 +5,10 @@ import 'package:pdf_signature/l10n/app_localizations.dart'; // No direct model construction needed here import 'package:pdf_signature/data/repositories/signature_asset_repository.dart'; +import 'package:pdf_signature/data/repositories/signature_card_repository.dart'; import 'image_editor_dialog.dart'; import '../../signature/widgets/signature_card.dart'; +import 'pdf_providers.dart'; /// Data for drag-and-drop is in signature_drag_data.dart @@ -32,7 +34,7 @@ class _SignatureDrawerState extends ConsumerState { @override Widget build(BuildContext context) { final l = AppLocalizations.of(context); - final library = ref.watch(signatureAssetRepositoryProvider); + final library = ref.watch(signatureCardRepositoryProvider); // Exporting flag lives in ui_services; keep drawer interactive regardless here. final isExporting = false; final disabled = widget.disabled || isExporting; @@ -41,20 +43,21 @@ class _SignatureDrawerState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (library.isNotEmpty) ...[ - for (final a in library) ...[ + for (final card in library) ...[ Card( margin: EdgeInsets.zero, child: Padding( padding: const EdgeInsets.all(12), child: SignatureCard( - key: ValueKey('sig_card_${library.indexOf(a)}'), - asset: a, - rotationDeg: 0.0, + key: ValueKey('sig_card_${library.indexOf(card)}'), + asset: card.asset, + rotationDeg: card.rotationDeg, + graphicAdjust: card.graphicAdjust, disabled: disabled, onDelete: () => ref - .read(signatureAssetRepositoryProvider.notifier) - .remove(a), + .read(signatureCardRepositoryProvider.notifier) + .remove(card), onAdjust: () async { if (!mounted) return; await showDialog( @@ -62,7 +65,11 @@ class _SignatureDrawerState extends ConsumerState { builder: (_) => const ImageEditorDialog(), ); }, - onTap: () {}, + onTap: () { + ref + .read(activeRectProvider.notifier) + .state = const Rect.fromLTWH(0.2, 0.2, 0.3, 0.15); + }, ), ), ), diff --git a/lib/ui/features/signature/widgets/signature_card.dart b/lib/ui/features/signature/widgets/signature_card.dart index 0dfed93..70c3df9 100644 --- a/lib/ui/features/signature/widgets/signature_card.dart +++ b/lib/ui/features/signature/widgets/signature_card.dart @@ -14,6 +14,7 @@ class SignatureCard extends StatelessWidget { this.onAdjust, this.useCurrentBytesForDrag = false, this.rotationDeg = 0.0, + this.graphicAdjust = const domain.GraphicAdjust(), }); final domain.SignatureAsset asset; final bool disabled; @@ -22,6 +23,7 @@ class SignatureCard extends StatelessWidget { final VoidCallback? onAdjust; final bool useCurrentBytesForDrag; final double rotationDeg; + final domain.GraphicAdjust graphicAdjust; @override Widget build(BuildContext context) { @@ -146,6 +148,7 @@ class SignatureCard extends StatelessWidget { card: domain.SignatureCard( asset: asset, rotationDeg: rotationDeg, + graphicAdjust: graphicAdjust, ), ), feedback: Opacity( diff --git a/test/features/step/the_first_page_is_displayed.dart b/test/features/step/the_first_page_is_displayed.dart index 40c36eb..ab1cf14 100644 --- a/test/features/step/the_first_page_is_displayed.dart +++ b/test/features/step/the_first_page_is_displayed.dart @@ -1,11 +1,11 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:pdf_signature/data/repositories/document_repository.dart'; +import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart'; import '_world.dart'; /// Usage: the first page is displayed Future theFirstPageIsDisplayed(WidgetTester tester) async { final container = TestWorld.container ?? ProviderContainer(); - final pdf = container.read(documentRepositoryProvider); - expect(pdf.currentPage, 1); + final currentPage = container.read(pdfViewModelProvider); + expect(currentPage, 1); } diff --git a/test/features/step/the_signature_placement_rotates_around_its_center_in_real_time.dart b/test/features/step/the_signature_placement_rotates_around_its_center_in_real_time.dart index ef63cd8..3a2785f 100644 --- a/test/features/step/the_signature_placement_rotates_around_its_center_in_real_time.dart +++ b/test/features/step/the_signature_placement_rotates_around_its_center_in_real_time.dart @@ -1,7 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:pdf_signature/data/repositories/document_repository.dart'; -import '_world.dart'; +import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart'; /// Usage: the signature placement rotates around its center in real time Future theSignaturePlacementRotatesAroundItsCenterInRealTime( @@ -9,8 +9,9 @@ Future theSignaturePlacementRotatesAroundItsCenterInRealTime( ) async { final container = TestWorld.container ?? ProviderContainer(); final pdf = container.read(documentRepositoryProvider); + final currentPage = container.read(pdfViewModelProvider); - final placements = pdf.placementsByPage[pdf.currentPage] ?? []; + final placements = pdf.placementsByPage[currentPage] ?? []; if (placements.isNotEmpty) { final placement = placements[0]; expect(placement.rotationDeg, 45.0); diff --git a/test/features/step/the_user_drags_handles_to_resize_and_drags_to_reposition.dart b/test/features/step/the_user_drags_handles_to_resize_and_drags_to_reposition.dart index 138758e..c3e3c9a 100644 --- a/test/features/step/the_user_drags_handles_to_resize_and_drags_to_reposition.dart +++ b/test/features/step/the_user_drags_handles_to_resize_and_drags_to_reposition.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:pdf_signature/data/repositories/document_repository.dart'; -import '_world.dart'; +import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart'; /// Usage: the user drags handles to resize and drags to reposition Future theUserDragsHandlesToResizeAndDragsToReposition( @@ -12,8 +12,9 @@ Future theUserDragsHandlesToResizeAndDragsToReposition( TestWorld.container = container; final pdf = container.read(documentRepositoryProvider); final pdfN = container.read(documentRepositoryProvider.notifier); + final currentPage = container.read(pdfViewModelProvider); - final placements = pdfN.placementsOn(pdf.currentPage); + final placements = pdfN.placementsOn(currentPage); if (placements.isNotEmpty) { final currentRect = placements[0].rect; TestWorld.prevCenter = currentRect.center; @@ -25,6 +26,6 @@ Future theUserDragsHandlesToResizeAndDragsToReposition( height: currentRect.height + 30, ); - pdfN.updatePlacementRect(page: pdf.currentPage, index: 0, rect: newRect); + pdfN.updatePlacementRect(page: currentPage, index: 0, rect: newRect); } } diff --git a/test/features/step/the_user_drags_this_signature_card_on_the_page_of_the_document_to_place_a_signature_placement.dart b/test/features/step/the_user_drags_this_signature_card_on_the_page_of_the_document_to_place_a_signature_placement.dart index 864bd10..273cd3e 100644 --- a/test/features/step/the_user_drags_this_signature_card_on_the_page_of_the_document_to_place_a_signature_placement.dart +++ b/test/features/step/the_user_drags_this_signature_card_on_the_page_of_the_document_to_place_a_signature_placement.dart @@ -55,5 +55,6 @@ theUserDragsThisSignatureCardOnThePageOfTheDocumentToPlaceASignaturePlacement( rect: Rect.fromLTWH(100, 100, 100, 50), asset: drop_card.asset, rotationDeg: drop_card.rotationDeg, + graphicAdjust: drop_card.graphicAdjust, ); } diff --git a/test/features/step/three_signature_placements_are_placed_on_the_current_page.dart b/test/features/step/three_signature_placements_are_placed_on_the_current_page.dart index 99bccad..e88aab1 100644 --- a/test/features/step/three_signature_placements_are_placed_on_the_current_page.dart +++ b/test/features/step/three_signature_placements_are_placed_on_the_current_page.dart @@ -5,8 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:pdf_signature/data/repositories/document_repository.dart'; import 'package:pdf_signature/data/repositories/signature_asset_repository.dart'; import 'package:pdf_signature/data/repositories/signature_card_repository.dart'; -import 'package:pdf_signature/domain/models/model.dart'; -import '_world.dart'; +import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart'; /// Usage: three signature placements are placed on the current page Future threeSignaturePlacementsArePlacedOnTheCurrentPage( @@ -24,8 +23,7 @@ Future threeSignaturePlacementsArePlacedOnTheCurrentPage( .read(documentRepositoryProvider.notifier) .openPicked(path: 'mock.pdf', pageCount: 5); final pdfN = container.read(documentRepositoryProvider.notifier); - final pdf = container.read(documentRepositoryProvider); - final page = pdf.currentPage; + final page = container.read(pdfViewModelProvider); pdfN.addPlacement( page: page, rect: Rect.fromLTWH(10, 10, 50, 50), diff --git a/test/widget/helpers.dart b/test/widget/helpers.dart index f346a69..478be31 100644 --- a/test/widget/helpers.dart +++ b/test/widget/helpers.dart @@ -9,6 +9,7 @@ import 'package:pdf_signature/ui/features/pdf/widgets/pdf_providers.dart'; import 'package:pdf_signature/ui/features/pdf/widgets/ui_services.dart'; import 'package:pdf_signature/data/repositories/document_repository.dart'; import 'package:pdf_signature/data/repositories/signature_asset_repository.dart'; +import 'package:pdf_signature/data/repositories/signature_card_repository.dart'; import 'package:pdf_signature/domain/models/signature_asset.dart'; import 'package:pdf_signature/l10n/app_localizations.dart'; @@ -377,6 +378,15 @@ Future pumpWithOpenPdfAndSig(WidgetTester tester) async { repo.add(Uint8List.fromList(bytes), name: 'test'); return repo; }), + signatureCardRepositoryProvider.overrideWith((ref) { + final cardRepo = SignatureCardStateNotifier(); + final asset = SignatureAsset( + bytes: Uint8List.fromList(bytes), + name: 'test', + ); + cardRepo.addWithAsset(asset, 0.0); + return cardRepo; + }), // In new model, interactive overlay not implemented; keep library empty useMockViewerProvider.overrideWithValue(true), exportingProvider.overrideWith((ref) => false), diff --git a/test/widget/pdf_navigation_widget_test.dart b/test/widget/pdf_navigation_widget_test.dart index c8d3d18..48fa6f9 100644 --- a/test/widget/pdf_navigation_widget_test.dart +++ b/test/widget/pdf_navigation_widget_test.dart @@ -12,11 +12,7 @@ import 'package:pdf_signature/l10n/app_localizations.dart'; class _TestPdfController extends DocumentStateNotifier { _TestPdfController() : super() { // Start with a loaded multi-page doc, page 1 of 5 - state = Document.initial().copyWith( - loaded: true, - pageCount: 5, - currentPage: 1, - ); + state = Document.initial().copyWith(loaded: true, pageCount: 5); } } diff --git a/test/widget/pdf_page_area_early_jump_test.dart b/test/widget/pdf_page_area_early_jump_test.dart index 0b71bc2..4bddb5c 100644 --- a/test/widget/pdf_page_area_early_jump_test.dart +++ b/test/widget/pdf_page_area_early_jump_test.dart @@ -11,11 +11,7 @@ import 'package:pdf_signature/domain/models/model.dart'; class _TestPdfController extends DocumentStateNotifier { _TestPdfController() : super() { - state = Document.initial().copyWith( - loaded: true, - pageCount: 6, - currentPage: 1, - ); + state = Document.initial().copyWith(loaded: true, pageCount: 6); } } diff --git a/test/widget/pdf_page_area_test.dart b/test/widget/pdf_page_area_test.dart index c581c96..017eecc 100644 --- a/test/widget/pdf_page_area_test.dart +++ b/test/widget/pdf_page_area_test.dart @@ -13,11 +13,7 @@ import 'package:pdf_signature/domain/models/model.dart'; class _TestPdfController extends DocumentStateNotifier { _TestPdfController() : super() { - state = Document.initial().copyWith( - loaded: true, - pageCount: 6, - currentPage: 1, - ); + state = Document.initial().copyWith(loaded: true, pageCount: 6); } }