From c5a10b9d6e21b3e74f35b0309cad11a5ad08312e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Wed, 21 Aug 2024 21:36:43 +0200 Subject: [PATCH 01/34] Prepare DocumentValidationStrip --- lib/l10n/app_localizations.dart | 24 +++ lib/l10n/app_localizations_sk.dart | 22 +++ lib/l10n/app_sk.arb | 5 + lib/ui/widgets/document_validation_strip.dart | 123 +++++++++++++ lib/ui/widgets/loading_indicator.dart | 4 +- lib/ui/widgets/markdown_text.dart | 3 + lib/widgetbook_app.directories.g.dart | 168 ++++++++++-------- pubspec.lock | 2 +- 8 files changed, 271 insertions(+), 80 deletions(-) create mode 100644 lib/ui/widgets/document_validation_strip.dart diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 58b4080..81d5102 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -451,6 +451,30 @@ abstract class AppLocalizations { /// **'Neviem vizualizovať {type} typ.'** String documentVisualizationCannotVisualizeTypeError(Object type); + /// No description provided for @documentValidationLoadingLabel. + /// + /// In sk, this message translates to: + /// **'Prebieha overovanie podpisov'** + String get documentValidationLoadingLabel; + + /// No description provided for @documentValidationNoSignaturesLabel. + /// + /// In sk, this message translates to: + /// **'Dokument neobsahuje **žiadny podpis**'** + String get documentValidationNoSignaturesLabel; + + /// No description provided for @documentValidationHasInvalidSignaturesLabel. + /// + /// In sk, this message translates to: + /// **'Dokument obsahuje **neplatné podpisy**'** + String get documentValidationHasInvalidSignaturesLabel; + + /// No description provided for @documentValidationHasValidSignaturesLabel. + /// + /// In sk, this message translates to: + /// **'{count, plural, one {Dokument obsahuje **1 podpis**} few {Dokument obsahuje **{count} podpisy**} many {Dokument obsahuje {count} podpisov**} other {Dokument obsahuje **{count} podpisov**}}'** + String documentValidationHasValidSignaturesLabel(num count); + /// No description provided for @selectCertificateTitle. /// /// In sk, this message translates to: diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index c869bf9..1ba140d 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -234,6 +234,28 @@ class AppLocalizationsSk extends AppLocalizations { return 'Neviem vizualizovať $type typ.'; } + @override + String get documentValidationLoadingLabel => 'Prebieha overovanie podpisov'; + + @override + String get documentValidationNoSignaturesLabel => 'Dokument neobsahuje **žiadny podpis**'; + + @override + String get documentValidationHasInvalidSignaturesLabel => 'Dokument obsahuje **neplatné podpisy**'; + + @override + String documentValidationHasValidSignaturesLabel(num count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'Dokument obsahuje **$count podpisov**', + many: 'Dokument obsahuje $count podpisov**', + few: 'Dokument obsahuje **$count podpisy**', + one: 'Dokument obsahuje **1 podpis**', + ); + return '$_temp0'; + } + @override String get selectCertificateTitle => 'Výber typu podpisu'; diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index 5ddffae..aa8b8e9 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -73,6 +73,11 @@ "documentVisualizationCannotVisualizeTypeError": "Neviem vizualizovať {type} typ.", + "documentValidationLoadingLabel": "Prebieha overovanie podpisov", + "documentValidationNoSignaturesLabel": "Dokument neobsahuje **žiadny podpis**", + "documentValidationHasInvalidSignaturesLabel": "Dokument obsahuje **neplatné podpisy**", + "documentValidationHasValidSignaturesLabel": "{count, plural, one {Dokument obsahuje **1 podpis**} few {Dokument obsahuje **{count} podpisy**} many {Dokument obsahuje {count} podpisov**} other {Dokument obsahuje **{count} podpisov**}}", + "selectCertificateTitle": "Výber typu podpisu", "selectSigningCertificateTitle": "Nastavenie certifikátu", "selectSigningCertificateBody": "Na podpisovanie mobilom potrebujete disponovať vhodným podpisovým certifikátom. Podpisový certifikát si môžete nastaviť aj neskôr počas podpisovania prvého dokumentu.\n\n\nAk si prajete nastaviť podpisový certifikát teraz, pripravte si, prosím, občiansky preukaz a nasledujte inštrukcie na obrazovke.", diff --git a/lib/ui/widgets/document_validation_strip.dart b/lib/ui/widgets/document_validation_strip.dart new file mode 100644 index 0000000..99d7fba --- /dev/null +++ b/lib/ui/widgets/document_validation_strip.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; + +import '../../strings_context.dart'; +import 'loading_indicator.dart'; +import 'markdown_text.dart'; + +/// Presents state of document validation - displays text and ptionally +/// loading indicator on the left or arrow on the right when has any signatures. +class DocumentValidationStrip extends StatelessWidget { + final DocumentValidationStripValue value; + final VoidCallback? onTap; + + const DocumentValidationStrip({ + super.key, + required this.value, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + final data = (value.isLoading, value.validCount, value.invalidCount); + final isLoading = value.isLoading; + final hasSignatures = (value.validCount > 0 || value.invalidCount > 0); + + final strings = context.strings; + + final String text = switch (data) { + (true, _, _) => strings.documentValidationLoadingLabel, + (false, _, > 0) => strings.documentValidationHasInvalidSignaturesLabel, + (false, > 0, _) => + strings.documentValidationHasValidSignaturesLabel(value.validCount), + (false, 0, 0) => strings.documentValidationNoSignaturesLabel, + (_, _, _) => "" // technically invalid case + }; + final Color backgroundColor = switch (data) { + (true, _, _) => const Color(0xFF126DFF), + (false, _, > 0) => const Color(0xFFC3112B), + (false, > 0, _) => const Color(0xFF078814), + (false, 0, 0) => const Color(0xFF126DFF), + (_, _, _) => Colors.transparent, + }; + const foregroundColor = Colors.white; + + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 4), + color: backgroundColor, + child: Row( + children: [ + if (isLoading) const LoadingIndicator(size: 16), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 6), + child: MarkdownText(text, textColor: foregroundColor), + ), + ), + if (hasSignatures) + const Icon( + Icons.arrow_right_alt_outlined, + color: foregroundColor, + ) + ], + ), + ), + ); + } +} + +/// Value for [DocumentValidationStrip] widget. +class DocumentValidationStripValue { + final bool isLoading; + final int validCount; + final int invalidCount; + + const DocumentValidationStripValue.loading() + : isLoading = true, + validCount = 0, + invalidCount = 0; + + const DocumentValidationStripValue.value({ + required this.validCount, + required this.invalidCount, + }) : isLoading = false; +} + +@widgetbook.UseCase( + path: '[Core]', + name: 'loading', + type: DocumentValidationStrip, +) +Widget previewLoadingDocumentValidationStrip(BuildContext context) { + return const DocumentValidationStrip( + value: DocumentValidationStripValue.loading(), + ); +} + +@widgetbook.UseCase( + path: '[Core]', + name: 'signatures', + type: DocumentValidationStrip, +) +Widget previewOtherDocumentValidationStrip(BuildContext context) { + final validCount = context.knobs.list( + label: 'Valid count', + options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + initialOption: 0, + ); + final invalidCount = context.knobs.list( + label: 'Invalid count', + options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + initialOption: 0, + ); + + return DocumentValidationStrip( + value: DocumentValidationStripValue.value( + validCount: validCount, + invalidCount: invalidCount, + ), + ); +} diff --git a/lib/ui/widgets/loading_indicator.dart b/lib/ui/widgets/loading_indicator.dart index 75bcf9b..504434f 100644 --- a/lib/ui/widgets/loading_indicator.dart +++ b/lib/ui/widgets/loading_indicator.dart @@ -3,13 +3,13 @@ import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; /// Widget for displaying indeterminate loading indicator. class LoadingIndicator extends StatelessWidget { - static const size = 24; - + final int size; final Color color; final Color backgroundColor; const LoadingIndicator({ super.key, + this.size = 24, this.color = const Color(0xFF126dff), this.backgroundColor = const Color(0xFFc3d9f9), }); diff --git a/lib/ui/widgets/markdown_text.dart b/lib/ui/widgets/markdown_text.dart index 9f957f7..7c9364f 100644 --- a/lib/ui/widgets/markdown_text.dart +++ b/lib/ui/widgets/markdown_text.dart @@ -11,11 +11,13 @@ import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; /// By default, links are open in external application. class MarkdownText extends StatelessWidget { final String data; + final Color? textColor; final MarkdownTapLinkCallback onLinkTap; const MarkdownText( this.data, { super.key, + this.textColor, this.onLinkTap = _defaultOnLinkTap, }); @@ -23,6 +25,7 @@ class MarkdownText extends StatelessWidget { Widget build(BuildContext context) { final colors = Theme.of(context).colorScheme; final styleSheet = MarkdownStyleSheet( + p: (textColor != null ? TextStyle(color: textColor) : null), a: TextStyle( color: colors.primary, fontWeight: FontWeight.bold, diff --git a/lib/widgetbook_app.directories.g.dart b/lib/widgetbook_app.directories.g.dart index 48145ed..c12dc4e 100644 --- a/lib/widgetbook_app.directories.g.dart +++ b/lib/widgetbook_app.directories.g.dart @@ -11,45 +11,46 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:autogram/ui/app_theme.dart' as _i3; import 'package:autogram/ui/assets.dart' as _i4; -import 'package:autogram/ui/fragment/show_web_page_fragment.dart' as _i22; -import 'package:autogram/ui/screens/about_screen.dart' as _i25; -import 'package:autogram/ui/screens/main_menu_screen.dart' as _i26; +import 'package:autogram/ui/fragment/show_web_page_fragment.dart' as _i23; +import 'package:autogram/ui/screens/about_screen.dart' as _i26; +import 'package:autogram/ui/screens/main_menu_screen.dart' as _i27; import 'package:autogram/ui/screens/main_screen.dart' as _i2; import 'package:autogram/ui/screens/onboarding_accept_document_screen.dart' - as _i27; -import 'package:autogram/ui/screens/onboarding_finished_screen.dart' as _i28; + as _i28; +import 'package:autogram/ui/screens/onboarding_finished_screen.dart' as _i29; import 'package:autogram/ui/screens/onboarding_select_signing_certificate_screen.dart' - as _i29; -import 'package:autogram/ui/screens/open_document_screen.dart' as _i30; -import 'package:autogram/ui/screens/paired_device_list_screen.dart' as _i31; + as _i30; +import 'package:autogram/ui/screens/open_document_screen.dart' as _i31; +import 'package:autogram/ui/screens/paired_device_list_screen.dart' as _i32; import 'package:autogram/ui/screens/present_signed_document_screen.dart' - as _i32; -import 'package:autogram/ui/screens/preview_document_screen.dart' as _i33; -import 'package:autogram/ui/screens/qr_code_scanner_screen.dart' as _i20; -import 'package:autogram/ui/screens/select_certificate_screen.dart' as _i34; -import 'package:autogram/ui/screens/settings_screen.dart' as _i35; -import 'package:autogram/ui/screens/show_document_screen.dart' as _i36; -import 'package:autogram/ui/screens/sign_document_screen.dart' as _i37; + as _i33; +import 'package:autogram/ui/screens/preview_document_screen.dart' as _i34; +import 'package:autogram/ui/screens/qr_code_scanner_screen.dart' as _i21; +import 'package:autogram/ui/screens/select_certificate_screen.dart' as _i35; +import 'package:autogram/ui/screens/settings_screen.dart' as _i36; +import 'package:autogram/ui/screens/show_document_screen.dart' as _i37; +import 'package:autogram/ui/screens/sign_document_screen.dart' as _i38; import 'package:autogram/ui/screens/start_remote_document_signing_screen.dart' - as _i38; + as _i39; import 'package:autogram/ui/widgets/app_version_text.dart' as _i9; import 'package:autogram/ui/widgets/autogram_logo.dart' as _i5; import 'package:autogram/ui/widgets/buttons.dart' as _i6; -import 'package:autogram/ui/widgets/certificate_picker.dart' as _i23; +import 'package:autogram/ui/widgets/certificate_picker.dart' as _i24; import 'package:autogram/ui/widgets/close_button.dart' as _i7; -import 'package:autogram/ui/widgets/dialogs.dart' as _i21; -import 'package:autogram/ui/widgets/document_visualization.dart' as _i10; -import 'package:autogram/ui/widgets/error_content.dart' as _i11; -import 'package:autogram/ui/widgets/html_preview.dart' as _i12; -import 'package:autogram/ui/widgets/loading_content.dart' as _i13; +import 'package:autogram/ui/widgets/dialogs.dart' as _i22; +import 'package:autogram/ui/widgets/document_validation_strip.dart' as _i10; +import 'package:autogram/ui/widgets/document_visualization.dart' as _i11; +import 'package:autogram/ui/widgets/error_content.dart' as _i12; +import 'package:autogram/ui/widgets/html_preview.dart' as _i13; +import 'package:autogram/ui/widgets/loading_content.dart' as _i14; import 'package:autogram/ui/widgets/loading_indicator.dart' as _i8; -import 'package:autogram/ui/widgets/markdown_text.dart' as _i14; -import 'package:autogram/ui/widgets/option_picker.dart' as _i15; -import 'package:autogram/ui/widgets/preference_tile.dart' as _i16; -import 'package:autogram/ui/widgets/result_view.dart' as _i17; -import 'package:autogram/ui/widgets/retry_view.dart' as _i18; -import 'package:autogram/ui/widgets/signature_type_picker.dart' as _i24; -import 'package:autogram/ui/widgets/step_indicator.dart' as _i19; +import 'package:autogram/ui/widgets/markdown_text.dart' as _i15; +import 'package:autogram/ui/widgets/option_picker.dart' as _i16; +import 'package:autogram/ui/widgets/preference_tile.dart' as _i17; +import 'package:autogram/ui/widgets/result_view.dart' as _i18; +import 'package:autogram/ui/widgets/retry_view.dart' as _i19; +import 'package:autogram/ui/widgets/signature_type_picker.dart' as _i25; +import 'package:autogram/ui/widgets/step_indicator.dart' as _i20; import 'package:widgetbook/widgetbook.dart' as _i1; final directories = <_i1.WidgetbookNode>[ @@ -156,53 +157,66 @@ final directories = <_i1.WidgetbookNode>[ builder: _i9.previewAppVersionText, ), ), + _i1.WidgetbookComponent( + name: 'DocumentValidationStrip', + useCases: [ + _i1.WidgetbookUseCase( + name: 'loading', + builder: _i10.previewLoadingDocumentValidationStrip, + ), + _i1.WidgetbookUseCase( + name: 'signatures', + builder: _i10.previewOtherDocumentValidationStrip, + ), + ], + ), _i1.WidgetbookLeafComponent( name: 'DocumentVisualization', useCase: _i1.WidgetbookUseCase( name: 'DocumentVisualization', - builder: _i10.previewDocumentVisualization, + builder: _i11.previewDocumentVisualization, ), ), _i1.WidgetbookLeafComponent( name: 'ErrorContent', useCase: _i1.WidgetbookUseCase( name: 'ErrorContent', - builder: _i11.previewErrorContent, + builder: _i12.previewErrorContent, ), ), _i1.WidgetbookLeafComponent( name: 'HtmlPreview', useCase: _i1.WidgetbookUseCase( name: 'HtmlPreview', - builder: _i12.previewHtmlPreview, + builder: _i13.previewHtmlPreview, ), ), _i1.WidgetbookLeafComponent( name: 'LoadingContent', useCase: _i1.WidgetbookUseCase( name: 'LoadingContent', - builder: _i13.previewLoadingContent, + builder: _i14.previewLoadingContent, ), ), _i1.WidgetbookLeafComponent( name: 'MarkdownText', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i14.previewMarkdownText, + builder: _i15.previewMarkdownText, ), ), _i1.WidgetbookLeafComponent( name: 'OptionPicker', useCase: _i1.WidgetbookUseCase( name: 'OptionPicker', - builder: _i15.previewOptionPicker, + builder: _i16.previewOptionPicker, ), ), _i1.WidgetbookLeafComponent( name: 'PreferenceTile', useCase: _i1.WidgetbookUseCase( name: 'PreferenceTile', - builder: _i16.previewPreferenceTile, + builder: _i17.previewPreferenceTile, ), ), _i1.WidgetbookComponent( @@ -210,19 +224,19 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'custom', - builder: _i17.previewCustomResultView, + builder: _i18.previewCustomResultView, ), _i1.WidgetbookUseCase( name: 'error', - builder: _i17.previewErrorResultView, + builder: _i18.previewErrorResultView, ), _i1.WidgetbookUseCase( name: 'info', - builder: _i17.previewInfoResultView, + builder: _i18.previewInfoResultView, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i17.previewSuccessResultView, + builder: _i18.previewSuccessResultView, ), ], ), @@ -230,21 +244,21 @@ final directories = <_i1.WidgetbookNode>[ name: 'RetryView', useCase: _i1.WidgetbookUseCase( name: 'RetryView', - builder: _i18.previewRetryView, + builder: _i19.previewRetryView, ), ), _i1.WidgetbookLeafComponent( name: 'StepIndicator', useCase: _i1.WidgetbookUseCase( name: 'StepIndicator', - builder: _i19.previewStepIndicator, + builder: _i20.previewStepIndicator, ), ), _i1.WidgetbookLeafComponent( name: '_ViewFinder', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i20.previewViewFinder, + builder: _i21.previewViewFinder, ), ), ], @@ -256,7 +270,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'BottomSheet', useCase: _i1.WidgetbookUseCase( name: 'NotificationsPermissionRationale', - builder: _i21.previewNotificationsPermissionRationaleModal, + builder: _i22.previewNotificationsPermissionRationaleModal, ), ) ], @@ -268,7 +282,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'ShowWebPageFragment', useCase: _i1.WidgetbookUseCase( name: 'ShowWebPageFragment', - builder: _i22.previewShowWebPageFragment, + builder: _i23.previewShowWebPageFragment, ), ) ], @@ -280,14 +294,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'CertificatePicker', useCase: _i1.WidgetbookUseCase( name: 'CertificatePicker', - builder: _i23.previewCertificatePicker, + builder: _i24.previewCertificatePicker, ), ), _i1.WidgetbookLeafComponent( name: 'SignatureTypePicker', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i24.previewSignatureTypePicker, + builder: _i25.previewSignatureTypePicker, ), ), ], @@ -299,14 +313,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'AboutScreen', useCase: _i1.WidgetbookUseCase( name: 'AboutScreen', - builder: _i25.previewAboutScreen, + builder: _i26.previewAboutScreen, ), ), _i1.WidgetbookLeafComponent( name: 'MainMenuScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i26.previewMainMenuScreen, + builder: _i27.previewMainMenuScreen, ), ), _i1.WidgetbookLeafComponent( @@ -320,14 +334,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'OnboardingAcceptDocumentScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i27.previewOnboardingAcceptDocumentScreen, + builder: _i28.previewOnboardingAcceptDocumentScreen, ), ), _i1.WidgetbookLeafComponent( name: 'OnboardingFinishedScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i28.previewOnboardingFinishedScreen, + builder: _i29.previewOnboardingFinishedScreen, ), ), _i1.WidgetbookComponent( @@ -335,20 +349,20 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'canceled', - builder: _i29.previewCanceledOnboardingSelectSigningCertificateBody, + builder: _i30.previewCanceledOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'initial', - builder: _i29.previewInitialOnboardingSelectSigningCertificateBody, + builder: _i30.previewInitialOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'no certificate', builder: - _i29.previewNoCertificateOnboardingSelectSigningCertificateBody, + _i30.previewNoCertificateOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i29.previewSuccessOnboardingSelectSigningCertificateBody, + builder: _i30.previewSuccessOnboardingSelectSigningCertificateBody, ), ], ), @@ -357,11 +371,11 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i30.previewErrorOpenDocumentScreen, + builder: _i31.previewErrorOpenDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i30.previewLoadingOpenDocumentScreen, + builder: _i31.previewLoadingOpenDocumentScreen, ), ], ), @@ -369,7 +383,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'PairedDeviceListScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i31.previewPairedDeviceListScreen, + builder: _i32.previewPairedDeviceListScreen, ), ), _i1.WidgetbookComponent( @@ -377,19 +391,19 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i32.previewErrorPresentSignedDocumentScreen, + builder: _i33.previewErrorPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'initial', - builder: _i32.previewInitialPresentSignedDocumentScreen, + builder: _i33.previewInitialPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i32.previewLoadingPresentSignedDocumentScreen, + builder: _i33.previewLoadingPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i32.previewSuccessPresentSignedDocumentScreen, + builder: _i33.previewSuccessPresentSignedDocumentScreen, ), ], ), @@ -398,15 +412,15 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i33.previewErrorPreviewDocumentScreen, + builder: _i34.previewErrorPreviewDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i33.previewLoadingPreviewDocumentScreen, + builder: _i34.previewLoadingPreviewDocumentScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i33.previewSuccessPreviewDocumentScreen, + builder: _i34.previewSuccessPreviewDocumentScreen, ), ], ), @@ -414,7 +428,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'QRCodeScannerScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i20.previewQRCodeScannerScreen, + builder: _i21.previewQRCodeScannerScreen, ), ), _i1.WidgetbookComponent( @@ -422,23 +436,23 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'canceled', - builder: _i34.previewCanceledSelectCertificateScreen, + builder: _i35.previewCanceledSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'error', - builder: _i34.previewErrorSelectCertificateScreen, + builder: _i35.previewErrorSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i34.previewLoadingSelectCertificateScreen, + builder: _i35.previewLoadingSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'no certificate', - builder: _i34.previewNoCertificateSelectCertificateScreen, + builder: _i35.previewNoCertificateSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i34.previewSuccessSelectCertificateScreen, + builder: _i35.previewSuccessSelectCertificateScreen, ), ], ), @@ -446,14 +460,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'SettingsScreen', useCase: _i1.WidgetbookUseCase( name: 'SettingsScreen', - builder: _i35.previewSettingsScreen, + builder: _i36.previewSettingsScreen, ), ), _i1.WidgetbookLeafComponent( name: 'ShowDocumentScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i36.previewShowDocumentScreen, + builder: _i37.previewShowDocumentScreen, ), ), _i1.WidgetbookComponent( @@ -461,15 +475,15 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i37.previewErrorSignDocumentScreen, + builder: _i38.previewErrorSignDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i37.previewLoadingSignDocumentScreen, + builder: _i38.previewLoadingSignDocumentScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i37.previewSuccessSignDocumentScreen, + builder: _i38.previewSuccessSignDocumentScreen, ), ], ), @@ -477,7 +491,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'StartRemoteDocumentSigningScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i38.previewStartRemoteDocumentSigningScreen, + builder: _i39.previewStartRemoteDocumentSigningScreen, ), ), ], diff --git a/pubspec.lock b/pubspec.lock index b66192c..6fd73dd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -63,7 +63,7 @@ packages: path: "../autogram_sign" relative: true source: path - version: "0.4.1" + version: "0.4.2" barcode: dependency: transitive description: From 41c945e15982c6a4f3fc71e49a3c9a278937108e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 24 Aug 2024 20:24:08 +0200 Subject: [PATCH 02/34] Update change log + version --- CHANGELOG.md | 5 +++++ pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f2a5e5..ca48e06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## NEXT - 1.1.0(36) + +- #35 +- #39 + ## 2024-07-16 - v1.0.4(35) - #32 | Cleanup diff --git a/pubspec.yaml b/pubspec.yaml index c0c0b63..ed18856 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: autogram description: "Autogram v mobile" publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 1.0.4+35 +version: 1.1.0+36 environment: sdk: '>=3.2.3 <4.0.0' From 34acf0bed6403831737aed97242367f70f1cf6bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 24 Aug 2024 20:25:54 +0200 Subject: [PATCH 03/34] pub upgrade --- pubspec.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 6fd73dd..6888750 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -84,10 +84,10 @@ packages: dependency: transitive description: name: bidi - sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63" + sha256: "9a712c7ddf708f7c41b1923aa83648a3ed44cfd75b04f72d598c45e5be287f9d" url: "https://pub.dev" source: hosted - version: "2.0.10" + version: "2.0.12" bloc: dependency: transitive description: @@ -735,10 +735,10 @@ packages: dependency: "direct main" description: name: path_provider - sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" path_provider_android: dependency: transitive description: @@ -783,10 +783,10 @@ packages: dependency: "direct main" description: name: pdf - sha256: "81d5522bddc1ef5c28e8f0ee40b71708761753c163e0c93a40df56fd515ea0f0" + sha256: "05df53f8791587402493ac97b9869d3824eccbc77d97855f4545cf72df3cae07" url: "https://pub.dev" source: hosted - version: "3.11.0" + version: "3.11.1" pdf_widget_wrapper: dependency: transitive description: @@ -879,10 +879,10 @@ packages: dependency: transitive description: name: qs_dart - sha256: bc7ec5dab9b4d92b5404146736f99953d38a68ffe65cafe0ebb6952e20261571 + sha256: ba7ba5c18f2f0b9356776b4a4641b1166bfe58d681abae75022bfc42cfdb5d1e url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" recase: dependency: transitive description: @@ -959,18 +959,18 @@ packages: dependency: transitive description: name: shared_preferences_linux - sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + sha256: "2ba0510d3017f91655b7543e9ee46d48619de2a2af38e5c790423f7007c7ccc1" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.0" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "034650b71e73629ca08a0bd789fd1d83cc63c2d1e405946f7cef7bc37432f93a" + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" shared_preferences_web: dependency: transitive description: @@ -983,10 +983,10 @@ packages: dependency: transitive description: name: shared_preferences_windows - sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + sha256: "398084b47b7f92110683cac45c6dc4aae853db47e470e5ddcd52cab7f7196ab2" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.0" shelf: dependency: transitive description: From 67e34f8f97a6981adf91f5b6487f3b6985ed2205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 24 Aug 2024 21:24:19 +0200 Subject: [PATCH 04/34] Prepare separate DocumentValidationFragment --- lib/bloc/document_validation_cubit.dart | 46 ++++++++++++++++ lib/bloc/document_validation_state.dart | 46 ++++++++++++++++ lib/di.config.dart | 21 ++++---- .../document_validation_fragment.dart | 54 +++++++++++++++++++ lib/ui/screens/preview_document_screen.dart | 19 ++++++- lib/ui/widgets/document_validation_strip.dart | 2 +- 6 files changed, 176 insertions(+), 12 deletions(-) create mode 100644 lib/bloc/document_validation_cubit.dart create mode 100644 lib/bloc/document_validation_state.dart create mode 100644 lib/ui/fragment/document_validation_fragment.dart diff --git a/lib/bloc/document_validation_cubit.dart b/lib/bloc/document_validation_cubit.dart new file mode 100644 index 0000000..7870d2b --- /dev/null +++ b/lib/bloc/document_validation_cubit.dart @@ -0,0 +1,46 @@ +import 'dart:async'; + +import 'package:autogram_sign/autogram_sign.dart'; +import 'package:flutter_bloc/flutter_bloc.dart' show Cubit; +import 'package:injectable/injectable.dart'; +import 'package:logging/logging.dart'; + +import '../ui/fragment/document_validation_fragment.dart'; +import 'document_validation_state.dart'; + +export 'document_validation_state.dart'; + +/// Cubit for the [DocumentValidationFragment] with only [createDocument] function. +/// +/// See also: +/// - [PreviewDocumentCubit] +@injectable +class DocumentValidationCubit extends Cubit { + static final _log = Logger((DocumentValidationCubit).toString()); + + final IAutogramService _service; + + DocumentValidationCubit({ + required IAutogramService service, + }) : _service = service, + super(const DocumentValidationInitialState()); + + Future validateDocument(String documentId) async { + _log.info("Requesting to validate Document Id: '$documentId'."); + emit(const DocumentValidationLoadingState()); + + try { + final response = await _service.getDocumentValidation(documentId); + + _log.info("Got Document validation: $response."); + + emit(DocumentValidationSuccessState(response)); + } catch (error, stackTrace) { + // TODO When not signed document, it returns HTTP 204 and null body - update it in service impl. + add test + // _TypeError: type 'Null' is not a subtype of type 'DocumentValidationResponseBody' in type cast + _log.severe("Error validating Document.", error, stackTrace); + + emit(DocumentValidationErrorState(error)); + } + } +} diff --git a/lib/bloc/document_validation_state.dart b/lib/bloc/document_validation_state.dart new file mode 100644 index 0000000..744ceb6 --- /dev/null +++ b/lib/bloc/document_validation_state.dart @@ -0,0 +1,46 @@ +import 'package:autogram_sign/autogram_sign.dart' + show DocumentValidationResponseBody; +import 'package:flutter/foundation.dart'; + +import 'document_validation_cubit.dart'; + +/// State for [DocumentValidationCubit]. +@immutable +sealed class DocumentValidationState { + const DocumentValidationState(); + + @override + String toString() { + return "$runtimeType()"; + } +} + +class DocumentValidationInitialState extends DocumentValidationState { + const DocumentValidationInitialState(); +} + +class DocumentValidationLoadingState extends DocumentValidationState { + const DocumentValidationLoadingState(); +} + +class DocumentValidationErrorState extends DocumentValidationState { + final Object error; + + const DocumentValidationErrorState(this.error); + + @override + String toString() { + return "$runtimeType(error: $error)"; + } +} + +class DocumentValidationSuccessState extends DocumentValidationState { + final DocumentValidationResponseBody response; + + const DocumentValidationSuccessState(this.response); + + @override + String toString() { + return "$runtimeType(response: $response)"; + } +} diff --git a/lib/di.config.dart b/lib/di.config.dart index 2fc7be1..ad074a0 100644 --- a/lib/di.config.dart +++ b/lib/di.config.dart @@ -20,16 +20,17 @@ import 'package:injectable/injectable.dart' as _i2; import 'app_service.dart' as _i3; import 'bloc/create_document_cubit.dart' as _i15; -import 'bloc/get_document_signature_type_cubit.dart' as _i20; +import 'bloc/document_validation_cubit.dart' as _i19; +import 'bloc/get_document_signature_type_cubit.dart' as _i21; import 'bloc/paired_device_list_cubit.dart' as _i8; import 'bloc/present_signed_document_cubit.dart' as _i9; import 'bloc/preview_document_cubit.dart' as _i10; import 'bloc/select_signing_certificate_cubit.dart' as _i11; import 'bloc/sign_document_cubit.dart' as _i14; import 'data/pdf_signing_option.dart' as _i18; -import 'di.dart' as _i21; +import 'di.dart' as _i22; import 'services/encryption_key_registry.dart' as _i5; -import 'use_case/get_document_signature_type_use_case.dart' as _i19; +import 'use_case/get_document_signature_type_use_case.dart' as _i20; import 'use_case/get_document_version_use_case.dart' as _i6; extension GetItInjectableX on _i1.GetIt { @@ -99,17 +100,19 @@ extension GetItInjectableX on _i1.GetIt { file: file, pdfSigningOption: pdfSigningOption, )); - gh.lazySingleton<_i19.GetDocumentSignatureTypeUseCase>( - () => _i19.GetDocumentSignatureTypeUseCase(gh<_i7.IAutogramService>())); - gh.factory<_i20.GetDocumentSignatureTypeCubit>(() => - _i20.GetDocumentSignatureTypeCubit( + gh.factory<_i19.DocumentValidationCubit>(() => + _i19.DocumentValidationCubit(service: gh<_i7.IAutogramService>())); + gh.lazySingleton<_i20.GetDocumentSignatureTypeUseCase>( + () => _i20.GetDocumentSignatureTypeUseCase(gh<_i7.IAutogramService>())); + gh.factory<_i21.GetDocumentSignatureTypeCubit>(() => + _i21.GetDocumentSignatureTypeCubit( getDocumentSignatureType: - gh<_i19.GetDocumentSignatureTypeUseCase>())); + gh<_i20.GetDocumentSignatureTypeUseCase>())); return this; } } -class _$ExtrernalModule extends _i21.ExtrernalModule { +class _$ExtrernalModule extends _i22.ExtrernalModule { @override _i4.Eidmsdk get eidmsdk => _i4.Eidmsdk(); } diff --git a/lib/ui/fragment/document_validation_fragment.dart b/lib/ui/fragment/document_validation_fragment.dart new file mode 100644 index 0000000..3dfa415 --- /dev/null +++ b/lib/ui/fragment/document_validation_fragment.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../bloc/document_validation_cubit.dart'; +import '../../di.dart'; +import '../widgets/document_validation_strip.dart'; + +/// Executes Document validation and displays output in [DocumentValidationStrip] +/// +/// Uses [DocumentValidationCubit]. +class DocumentValidationFragment extends StatelessWidget { + // TODO Stateful widget? + final String documentId; + + const DocumentValidationFragment({ + super.key, + required this.documentId, + }); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) { + return getIt.get() + ..validateDocument(documentId); + }, + child: BlocBuilder( + builder: _buildContent, + ), + ); + } + + Widget _buildContent(BuildContext context, DocumentValidationState state) { + return switch (state) { + DocumentValidationInitialState _ => const DocumentValidationStrip( + value: DocumentValidationStripValue.loading(), + ), + DocumentValidationLoadingState _ => const DocumentValidationStrip( + value: DocumentValidationStripValue.loading(), + ), + DocumentValidationSuccessState state => DocumentValidationStrip( + value: DocumentValidationStripValue.value( + validCount: state.response.signatures?.length ?? 0, + invalidCount: state.response.signatures?.length ?? 0, + ), + onTap: () { + // TODO Handle tap event + }, + ), + DocumentValidationErrorState _ => + const SizedBox.shrink(), // TODO Show error in this panel. + }; + } +} diff --git a/lib/ui/screens/preview_document_screen.dart b/lib/ui/screens/preview_document_screen.dart index df411fc..9f6b821 100644 --- a/lib/ui/screens/preview_document_screen.dart +++ b/lib/ui/screens/preview_document_screen.dart @@ -14,6 +14,7 @@ import '../../data/document_signing_type.dart'; import '../../file_system_entity_extensions.dart'; import '../../strings_context.dart'; import '../app_theme.dart'; +import '../fragment/document_validation_fragment.dart'; import '../widgets/document_visualization.dart'; import '../widgets/error_content.dart'; import '../widgets/loading_content.dart'; @@ -49,6 +50,7 @@ class PreviewDocumentScreen extends StatelessWidget { child: BlocBuilder( builder: (context, state) { return _Body( + documentId: documentId, state: state, onSignRequested: () { _onSignRequested(context); @@ -104,14 +106,21 @@ class PreviewDocumentScreen extends StatelessWidget { /// [PreviewDocumentScreen] body. class _Body extends StatelessWidget { + final String documentId; final PreviewDocumentState state; final VoidCallback? onSignRequested; - const _Body({required this.state, required this.onSignRequested}); + const _Body({ + this.documentId = '', + required this.state, + required this.onSignRequested, + }); @override Widget build(BuildContext context) { - final child = switch (state) { + final child1 = DocumentValidationFragment(documentId: documentId); + // TODO Extract whole child2 as Fragment, so it can have separate preview + final child2 = switch (state) { PreviewDocumentInitialState _ => const LoadingContent(), PreviewDocumentLoadingState _ => const LoadingContent(), PreviewDocumentSuccessState state => _SuccessContent( @@ -123,6 +132,12 @@ class _Body extends StatelessWidget { error: state.error, ), }; + final child = Column( + children: [ + child1, + Expanded(child: child2), + ], + ); return Padding( padding: kScreenMargin, diff --git a/lib/ui/widgets/document_validation_strip.dart b/lib/ui/widgets/document_validation_strip.dart index 99d7fba..68c8106 100644 --- a/lib/ui/widgets/document_validation_strip.dart +++ b/lib/ui/widgets/document_validation_strip.dart @@ -6,7 +6,7 @@ import '../../strings_context.dart'; import 'loading_indicator.dart'; import 'markdown_text.dart'; -/// Presents state of document validation - displays text and ptionally +/// Presents state of document validation - displays text and potentionally /// loading indicator on the left or arrow on the right when has any signatures. class DocumentValidationStrip extends StatelessWidget { final DocumentValidationStripValue value; From 81ac68314280e1227050e39ce5538aeb27d809c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 24 Aug 2024 21:40:58 +0200 Subject: [PATCH 05/34] Made response nullable --- lib/bloc/document_validation_cubit.dart | 2 -- lib/bloc/document_validation_state.dart | 2 +- pubspec.lock | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/bloc/document_validation_cubit.dart b/lib/bloc/document_validation_cubit.dart index 7870d2b..82e2dcd 100644 --- a/lib/bloc/document_validation_cubit.dart +++ b/lib/bloc/document_validation_cubit.dart @@ -36,8 +36,6 @@ class DocumentValidationCubit extends Cubit { emit(DocumentValidationSuccessState(response)); } catch (error, stackTrace) { - // TODO When not signed document, it returns HTTP 204 and null body - update it in service impl. + add test - // _TypeError: type 'Null' is not a subtype of type 'DocumentValidationResponseBody' in type cast _log.severe("Error validating Document.", error, stackTrace); emit(DocumentValidationErrorState(error)); diff --git a/lib/bloc/document_validation_state.dart b/lib/bloc/document_validation_state.dart index 744ceb6..c60a0b2 100644 --- a/lib/bloc/document_validation_state.dart +++ b/lib/bloc/document_validation_state.dart @@ -35,7 +35,7 @@ class DocumentValidationErrorState extends DocumentValidationState { } class DocumentValidationSuccessState extends DocumentValidationState { - final DocumentValidationResponseBody response; + final DocumentValidationResponseBody? response; const DocumentValidationSuccessState(this.response); diff --git a/pubspec.lock b/pubspec.lock index 6888750..4393c13 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -63,7 +63,7 @@ packages: path: "../autogram_sign" relative: true source: path - version: "0.4.2" + version: "0.4.3" barcode: dependency: transitive description: From e8448b1a314b812f88f53605ffc1974c3ad125c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 24 Aug 2024 21:47:37 +0200 Subject: [PATCH 06/34] Update value getter --- .../fragment/document_validation_fragment.dart | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/ui/fragment/document_validation_fragment.dart b/lib/ui/fragment/document_validation_fragment.dart index 3dfa415..76d9649 100644 --- a/lib/ui/fragment/document_validation_fragment.dart +++ b/lib/ui/fragment/document_validation_fragment.dart @@ -1,3 +1,6 @@ +import 'package:autogram_sign/autogram_sign.dart' + show + DocumentValidationResponseBody; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -40,8 +43,8 @@ class DocumentValidationFragment extends StatelessWidget { ), DocumentValidationSuccessState state => DocumentValidationStrip( value: DocumentValidationStripValue.value( - validCount: state.response.signatures?.length ?? 0, - invalidCount: state.response.signatures?.length ?? 0, + validCount: state.response?.validSignaturesCount ?? 0, + invalidCount: state.response?.invalidSignaturesCount ?? 0, ), onTap: () { // TODO Handle tap event @@ -52,3 +55,13 @@ class DocumentValidationFragment extends StatelessWidget { }; } } + +extension _DocumentValidationResponseBodyExtensions + on DocumentValidationResponseBody { + int? get validSignaturesCount => signatures + ?.where((s) => s.validationResult.code == 0 /* TOTAL_PASSED */) + .length; + int? get invalidSignaturesCount => signatures + ?.where((s) => s.validationResult.code != 0 /* NOT TOTAL_PASSED */) + .length; +} From ae861ad1cdc9dcbc1f5e4ad10704fc73c08376c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Mon, 9 Sep 2024 17:44:36 +0200 Subject: [PATCH 07/34] pub upgrade --- pubspec.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 4393c13..6d829ae 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -347,10 +347,10 @@ packages: dependency: transitive description: name: firebase_core_platform_interface - sha256: "3c3a1e92d6f4916c32deea79c4a7587aa0e9dbbe5889c7a16afcf005a485ee02" + sha256: f7d7180c7f99babd4b4c517754d41a09a4943a0f7a69b65c894ca5c68ba66315 url: "https://pub.dev" source: hosted - version: "5.2.0" + version: "5.2.1" firebase_core_web: dependency: transitive description: @@ -647,10 +647,10 @@ packages: dependency: transitive description: name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.6" mobile_scanner: dependency: "direct main" description: @@ -879,10 +879,10 @@ packages: dependency: transitive description: name: qs_dart - sha256: ba7ba5c18f2f0b9356776b4a4641b1166bfe58d681abae75022bfc42cfdb5d1e + sha256: "8dddeaf1d32fe407e253840b2c25c9ab5bf347d2761d82cb4ce010096565c9ff" url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.3" recase: dependency: transitive description: @@ -1212,10 +1212,10 @@ packages: dependency: transitive description: name: uuid - sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90" + sha256: f33d6bb662f0e4f79dcd7ada2e6170f3b3a2530c28fc41f49a411ddedd576a77 url: "https://pub.dev" source: hosted - version: "4.4.2" + version: "4.5.0" vector_graphics: dependency: transitive description: From 8b097fa5b02b601d886ceb44c627af34ddfec7ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Mon, 9 Sep 2024 17:58:30 +0200 Subject: [PATCH 08/34] Impl. also case for not signed document --- lib/bloc/document_validation_cubit.dart | 10 ++++++++-- lib/bloc/document_validation_state.dart | 4 ++++ lib/ui/fragment/document_validation_fragment.dart | 8 +++++--- lib/ui/widgets/document_validation_strip.dart | 5 +++++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/bloc/document_validation_cubit.dart b/lib/bloc/document_validation_cubit.dart index 82e2dcd..da78104 100644 --- a/lib/bloc/document_validation_cubit.dart +++ b/lib/bloc/document_validation_cubit.dart @@ -7,10 +7,11 @@ import 'package:logging/logging.dart'; import '../ui/fragment/document_validation_fragment.dart'; import 'document_validation_state.dart'; +import 'preview_document_cubit.dart'; export 'document_validation_state.dart'; -/// Cubit for the [DocumentValidationFragment] with only [createDocument] function. +/// Cubit for the [DocumentValidationFragment] with [validateDocument] function. /// /// See also: /// - [PreviewDocumentCubit] @@ -38,7 +39,12 @@ class DocumentValidationCubit extends Cubit { } catch (error, stackTrace) { _log.severe("Error validating Document.", error, stackTrace); - emit(DocumentValidationErrorState(error)); + if (error is ServiceException && + error.errorCode == "DOCUMENT_NOT_SIGNED") { + emit(const DocumentValidationNotSignedState()); + } else { + emit(DocumentValidationErrorState(error)); + } } } } diff --git a/lib/bloc/document_validation_state.dart b/lib/bloc/document_validation_state.dart index c60a0b2..5b5d6a3 100644 --- a/lib/bloc/document_validation_state.dart +++ b/lib/bloc/document_validation_state.dart @@ -34,6 +34,10 @@ class DocumentValidationErrorState extends DocumentValidationState { } } +class DocumentValidationNotSignedState extends DocumentValidationState { + const DocumentValidationNotSignedState(); +} + class DocumentValidationSuccessState extends DocumentValidationState { final DocumentValidationResponseBody? response; diff --git a/lib/ui/fragment/document_validation_fragment.dart b/lib/ui/fragment/document_validation_fragment.dart index 76d9649..3d20c85 100644 --- a/lib/ui/fragment/document_validation_fragment.dart +++ b/lib/ui/fragment/document_validation_fragment.dart @@ -1,6 +1,5 @@ import 'package:autogram_sign/autogram_sign.dart' - show - DocumentValidationResponseBody; + show DocumentValidationResponseBody; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -12,7 +11,7 @@ import '../widgets/document_validation_strip.dart'; /// /// Uses [DocumentValidationCubit]. class DocumentValidationFragment extends StatelessWidget { - // TODO Stateful widget? + // TODO Consider migrating to to stateful widget becasue of Cubit final String documentId; const DocumentValidationFragment({ @@ -41,6 +40,9 @@ class DocumentValidationFragment extends StatelessWidget { DocumentValidationLoadingState _ => const DocumentValidationStrip( value: DocumentValidationStripValue.loading(), ), + DocumentValidationNotSignedState _ => const DocumentValidationStrip( + value: DocumentValidationStripValue.none(), + ), DocumentValidationSuccessState state => DocumentValidationStrip( value: DocumentValidationStripValue.value( validCount: state.response?.validSignaturesCount ?? 0, diff --git a/lib/ui/widgets/document_validation_strip.dart b/lib/ui/widgets/document_validation_strip.dart index 68c8106..05a947e 100644 --- a/lib/ui/widgets/document_validation_strip.dart +++ b/lib/ui/widgets/document_validation_strip.dart @@ -84,6 +84,11 @@ class DocumentValidationStripValue { required this.validCount, required this.invalidCount, }) : isLoading = false; + + const DocumentValidationStripValue.none() + : validCount = 0, + invalidCount = 0, + isLoading = false; } @widgetbook.UseCase( From ccaa04d87884deb307d2aaa668712f5ca0e6735b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Mon, 9 Sep 2024 18:03:43 +0200 Subject: [PATCH 09/34] Update impl. to work with latest API client --- lib/bloc/document_validation_state.dart | 2 +- .../document_validation_fragment.dart | 29 +++++++++++++------ lib/ui/screens/preview_document_screen.dart | 7 ++++- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/lib/bloc/document_validation_state.dart b/lib/bloc/document_validation_state.dart index 5b5d6a3..c0afe79 100644 --- a/lib/bloc/document_validation_state.dart +++ b/lib/bloc/document_validation_state.dart @@ -39,7 +39,7 @@ class DocumentValidationNotSignedState extends DocumentValidationState { } class DocumentValidationSuccessState extends DocumentValidationState { - final DocumentValidationResponseBody? response; + final DocumentValidationResponseBody response; const DocumentValidationSuccessState(this.response); diff --git a/lib/ui/fragment/document_validation_fragment.dart b/lib/ui/fragment/document_validation_fragment.dart index 3d20c85..2bef582 100644 --- a/lib/ui/fragment/document_validation_fragment.dart +++ b/lib/ui/fragment/document_validation_fragment.dart @@ -1,5 +1,7 @@ import 'package:autogram_sign/autogram_sign.dart' - show DocumentValidationResponseBody; + show + DocumentValidationResponseBody, + DocumentValidationResponseBody$Signatures$ItemValidationResult; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -7,16 +9,19 @@ import '../../bloc/document_validation_cubit.dart'; import '../../di.dart'; import '../widgets/document_validation_strip.dart'; -/// Executes Document validation and displays output in [DocumentValidationStrip] +/// Executes Document validation and displays output in [DocumentValidationStrip]. /// /// Uses [DocumentValidationCubit]. class DocumentValidationFragment extends StatelessWidget { // TODO Consider migrating to to stateful widget becasue of Cubit final String documentId; + final ValueSetter + onShowDocumentValidationInfoRequested; const DocumentValidationFragment({ super.key, required this.documentId, + required this.onShowDocumentValidationInfoRequested, }); @override @@ -45,15 +50,15 @@ class DocumentValidationFragment extends StatelessWidget { ), DocumentValidationSuccessState state => DocumentValidationStrip( value: DocumentValidationStripValue.value( - validCount: state.response?.validSignaturesCount ?? 0, - invalidCount: state.response?.invalidSignaturesCount ?? 0, + validCount: state.response.validSignaturesCount ?? 0, + invalidCount: state.response.invalidSignaturesCount ?? 0, ), onTap: () { - // TODO Handle tap event + onShowDocumentValidationInfoRequested.call(state.response); }, ), - DocumentValidationErrorState _ => - const SizedBox.shrink(), // TODO Show error in this panel. + DocumentValidationErrorState _ => const SizedBox.shrink(), + // TODO Show error in this panel - red background with message }; } } @@ -61,9 +66,15 @@ class DocumentValidationFragment extends StatelessWidget { extension _DocumentValidationResponseBodyExtensions on DocumentValidationResponseBody { int? get validSignaturesCount => signatures - ?.where((s) => s.validationResult.code == 0 /* TOTAL_PASSED */) + ?.where((s) => + s.validationResult == + DocumentValidationResponseBody$Signatures$ItemValidationResult + .totalPassed) .length; int? get invalidSignaturesCount => signatures - ?.where((s) => s.validationResult.code != 0 /* NOT TOTAL_PASSED */) + ?.where((s) => + s.validationResult != + DocumentValidationResponseBody$Signatures$ItemValidationResult + .totalPassed) .length; } diff --git a/lib/ui/screens/preview_document_screen.dart b/lib/ui/screens/preview_document_screen.dart index 9f6b821..ddc1d71 100644 --- a/lib/ui/screens/preview_document_screen.dart +++ b/lib/ui/screens/preview_document_screen.dart @@ -118,7 +118,12 @@ class _Body extends StatelessWidget { @override Widget build(BuildContext context) { - final child1 = DocumentValidationFragment(documentId: documentId); + final child1 = DocumentValidationFragment( + documentId: documentId, + onShowDocumentValidationInfoRequested: (response) { + // TODO Show popup with details + }, + ); // TODO Extract whole child2 as Fragment, so it can have separate preview final child2 = switch (state) { PreviewDocumentInitialState _ => const LoadingContent(), From c3be32e7a59147a45dccfb4b0b8469d9661bcaca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Mon, 9 Sep 2024 18:53:45 +0200 Subject: [PATCH 10/34] Update UI to display signatures --- lib/l10n/app_localizations.dart | 6 + lib/l10n/app_localizations_sk.dart | 3 + lib/l10n/app_sk.arb | 1 + .../document_validation_info_fragment.dart | 99 ++++++++++ lib/ui/screens/preview_document_screen.dart | 30 ++- lib/ui/widgets/document_validation_info.dart | 93 +++++++++ lib/widgetbook_app.directories.g.dart | 179 ++++++++++-------- 7 files changed, 326 insertions(+), 85 deletions(-) create mode 100644 lib/ui/fragment/document_validation_info_fragment.dart create mode 100644 lib/ui/widgets/document_validation_info.dart diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index e2599dc..08ed39e 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -475,6 +475,12 @@ abstract class AppLocalizations { /// **'{count, plural, one {Dokument obsahuje **1 podpis**} few {Dokument obsahuje **{count} podpisy**} many {Dokument obsahuje {count} podpisov**} other {Dokument obsahuje **{count} podpisov**}}'** String documentValidationHasValidSignaturesLabel(num count); + /// No description provided for @documentValidationSignaturesHeading. + /// + /// In sk, this message translates to: + /// **'Podpisy v dokumente'** + String get documentValidationSignaturesHeading; + /// No description provided for @selectCertificateTitle. /// /// In sk, this message translates to: diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index 99d4690..2c33498 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -256,6 +256,9 @@ class AppLocalizationsSk extends AppLocalizations { return '$_temp0'; } + @override + String get documentValidationSignaturesHeading => 'Podpisy v dokumente'; + @override String get selectCertificateTitle => 'Výber typu podpisu'; diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index d2e0a74..2eb2849 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -77,6 +77,7 @@ "documentValidationNoSignaturesLabel": "Dokument neobsahuje **žiadny podpis**", "documentValidationHasInvalidSignaturesLabel": "Dokument obsahuje **neplatné podpisy**", "documentValidationHasValidSignaturesLabel": "{count, plural, one {Dokument obsahuje **1 podpis**} few {Dokument obsahuje **{count} podpisy**} many {Dokument obsahuje {count} podpisov**} other {Dokument obsahuje **{count} podpisov**}}", + "documentValidationSignaturesHeading": "Podpisy v dokumente", "selectCertificateTitle": "Výber typu podpisu", "selectSigningCertificateTitle": "Nastavenie certifikátu", diff --git a/lib/ui/fragment/document_validation_info_fragment.dart b/lib/ui/fragment/document_validation_info_fragment.dart new file mode 100644 index 0000000..414ecc9 --- /dev/null +++ b/lib/ui/fragment/document_validation_info_fragment.dart @@ -0,0 +1,99 @@ +import 'package:autogram_sign/autogram_sign.dart'; +import 'package:flutter/material.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; + +import '../../strings_context.dart'; +import '../widgets/document_validation_info.dart'; + +/// Displays Document validation info based on [data] provided +/// as list of [DocumentValidationInfo] for each signature. +class DocumentValidationInfoFragment extends StatelessWidget { + final DocumentValidationResponseBody data; + + const DocumentValidationInfoFragment({ + super.key, + required this.data, + }); + + @override + Widget build(BuildContext context) { + final signatures = data.signatures ?? []; + + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + context.strings.documentValidationSignaturesHeading, + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + Expanded( + child: ListView.separated( + padding: const EdgeInsets.symmetric(vertical: 8), + itemCount: signatures.length, + itemBuilder: (context, index) { + final signature = signatures[index]; + + return DocumentValidationInfo(signature); + }, + separatorBuilder: (context, _) { + return const Divider(); + }, + ), + ), + ], + ); + } +} + +@widgetbook.UseCase( + path: '[Fragments]', + name: '', + type: DocumentValidationInfoFragment, +) +Widget previewDocumentValidationInfoFragment(BuildContext context) { + return const DocumentValidationInfoFragment( + data: DocumentValidationResponseBody( + containerType: DocumentValidationResponseBodyContainerType.asicE, + signatureForm: DocumentValidationResponseBodySignatureForm.xades, + signatures: [ + DocumentValidationResponseBody$Signatures$Item( + validationResult: + DocumentValidationResponseBody$Signatures$ItemValidationResult + .totalPassed, + level: DocumentValidationResponseBody$Signatures$ItemLevel + .xadesBaselineLta, + claimedSigningTime: "2023-08-01T12:37:47 +0200", + bestSigningTime: "2023-08-01T12:37:47 +0200", + signingCertificate: + DocumentValidationResponseBody$Signatures$Item$SigningCertificate( + qualification: + DocumentValidationResponseBody$Signatures$Item$SigningCertificateQualification + .qeseal, + issuerDN: + "OID.2.5.4.5=NTRCZ-26439395, O=\"První certifikační autorita, a.s.\", CN=I.CA Qualified CA/RSA 07/2015, C=CZ", + subjectDN: + "OID.2.5.4.5=ICA - 10432139, OID.2.5.4.97=NTRSK-00166073, CN=Ministerstvo spravodlivosti SR, O=Ministerstvo spravodlivosti SR, C=SK", + certificateDer: + "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", + ), + areQualifiedTimestamps: true, + timestamps: [ + DocumentValidationResponseBody$Signatures$Item$Timestamps$Item( + qualification: + DocumentValidationResponseBody$Signatures$Item$Timestamps$ItemQualification + .qtsa, + timestampType: + DocumentValidationResponseBody$Signatures$Item$Timestamps$ItemTimestampType + .signatureTimestamp, + subjectDN: + "CN=NASES Time Stamp Authority 2, O=Národná agentúra pre sieťové a elektronické služby, OID.2.5.4.97=NTRSK-42156424, OU=SNCA, C=SK", + certificateDer: + "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", + productionTime: "2024-08-02T13:01:24 +0200", + ), + ], + ), + ], + ), + ); +} diff --git a/lib/ui/screens/preview_document_screen.dart b/lib/ui/screens/preview_document_screen.dart index ddc1d71..c20400d 100644 --- a/lib/ui/screens/preview_document_screen.dart +++ b/lib/ui/screens/preview_document_screen.dart @@ -1,7 +1,7 @@ import 'dart:io' show File; import 'package:autogram_sign/autogram_sign.dart' - show DocumentVisualizationResponseBody; + show DocumentValidationResponseBody, DocumentVisualizationResponseBody; import 'package:dotted_border/dotted_border.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -15,6 +15,7 @@ import '../../file_system_entity_extensions.dart'; import '../../strings_context.dart'; import '../app_theme.dart'; import '../fragment/document_validation_fragment.dart'; +import '../fragment/document_validation_info_fragment.dart'; import '../widgets/document_visualization.dart'; import '../widgets/error_content.dart'; import '../widgets/loading_content.dart'; @@ -24,8 +25,9 @@ import 'select_certificate_screen.dart'; /// Screen for previewing single document from [file] and [documentId]. /// /// Uses [PreviewDocumentCubit]. -/// +/// TODO Also update docs - mention fragments /// Navigates next to [SelectCertificateScreen]. +/// Shows [DocumentValidationInfoFragment]. /// /// See also: /// - [OpenDocumentScreen] @@ -55,6 +57,9 @@ class PreviewDocumentScreen extends StatelessWidget { onSignRequested: () { _onSignRequested(context); }, + onShowDocumentValidationInfoRequested: (data) { + _onShowDocumentValidationInfoRequested(context, data); + }, ); }, ), @@ -102,6 +107,19 @@ class PreviewDocumentScreen extends StatelessWidget { return Navigator.of(context).push(route); } + + Future _onShowDocumentValidationInfoRequested( + BuildContext context, + DocumentValidationResponseBody data, + ) { + // TODO Call avm.showModalBottomSheet + return showModalBottomSheet( + context: context, + builder: (_) { + return DocumentValidationInfoFragment(data: data); + }, + ); + } } /// [PreviewDocumentScreen] body. @@ -109,19 +127,23 @@ class _Body extends StatelessWidget { final String documentId; final PreviewDocumentState state; final VoidCallback? onSignRequested; + final ValueSetter? + onShowDocumentValidationInfoRequested; const _Body({ this.documentId = '', required this.state, required this.onSignRequested, + this.onShowDocumentValidationInfoRequested, }); @override Widget build(BuildContext context) { + // TODO Fix margins according to Designs https://www.figma.com/design/9i8kwShc6o8Urp2lYoPg6M/Autogram-v-mobile-(WIP)?node-id=742-718&node-type=FRAME&t=tOi5uDxBr4g6AUTW-0 final child1 = DocumentValidationFragment( documentId: documentId, - onShowDocumentValidationInfoRequested: (response) { - // TODO Show popup with details + onShowDocumentValidationInfoRequested: (data) { + onShowDocumentValidationInfoRequested?.call(data); }, ); // TODO Extract whole child2 as Fragment, so it can have separate preview diff --git a/lib/ui/widgets/document_validation_info.dart b/lib/ui/widgets/document_validation_info.dart new file mode 100644 index 0000000..79ca01b --- /dev/null +++ b/lib/ui/widgets/document_validation_info.dart @@ -0,0 +1,93 @@ +import 'package:autogram_sign/autogram_sign.dart'; +import 'package:flutter/material.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; + +import '../../oids.dart'; +import '../../utils.dart'; + +/// Displays Document validation info based on [data] provided. +class DocumentValidationInfo extends StatelessWidget { + // TODO Add primitive params for preview + create factory ctor for DocumentValidationResponseBody$Signatures$Item + final DocumentValidationResponseBody$Signatures$Item data; + + const DocumentValidationInfo(this.data, {super.key}); + + @override + Widget build(BuildContext context) { + final cert = + x509CertificateDataFromDer(data.signingCertificate.certificateDer) + .tbsCertificate; + final label = [ + cert?.subject[X500Oids.cn], + cert?.subject[X500Oids.ln], + cert?.subject[X500Oids.c], + ].whereType().join(", "); + + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Text( + label, + maxLines: 2, + style: const TextStyle(fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + ), + ), + ElevatedButton( + onPressed: null, + // TODO Show various states - with color and custom text - extract as Widget + child: Text(data.validationResult.name), + ), + ], + ); + } +} + +@widgetbook.UseCase( + path: '[Core]', + name: '', + type: DocumentValidationInfo, +) +Widget previewDocumentValidationInfo(BuildContext context) { + return const DocumentValidationInfo( + DocumentValidationResponseBody$Signatures$Item( + validationResult: + DocumentValidationResponseBody$Signatures$ItemValidationResult + .totalPassed, + level: + DocumentValidationResponseBody$Signatures$ItemLevel.xadesBaselineLta, + claimedSigningTime: "2023-08-01T12:37:47 +0200", + bestSigningTime: "2023-08-01T12:37:47 +0200", + signingCertificate: + DocumentValidationResponseBody$Signatures$Item$SigningCertificate( + qualification: + DocumentValidationResponseBody$Signatures$Item$SigningCertificateQualification + .qeseal, + issuerDN: + "OID.2.5.4.5=NTRCZ-26439395, O=\"První certifikační autorita, a.s.\", CN=I.CA Qualified CA/RSA 07/2015, C=CZ", + subjectDN: + "OID.2.5.4.5=ICA - 10432139, OID.2.5.4.97=NTRSK-00166073, CN=Ministerstvo spravodlivosti SR, O=Ministerstvo spravodlivosti SR, C=SK", + certificateDer: + "MIIH5TCCBc2gAwIBAgIEALfsWjANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJDWjEmMCQGA1UEAwwdSS5DQSBRdWFsaWZpZWQgQ0EvUlNBIDA3LzIwMTUxLTArBgNVBAoMJFBydm7DrSBjZXJ0aWZpa2HEjW7DrSBhdXRvcml0YSwgYS5zLjEXMBUGA1UEBRMOTlRSQ1otMjY0MzkzOTUwHhcNMjIwOTIwMTExMTAxWhcNMjMwOTIwMTExMTAxWjCBkTELMAkGA1UEBhMCU0sxJzAlBgNVBAoMHk1pbmlzdGVyc3R2byBzcHJhdm9kbGl2b3N0aSBTUjEnMCUGA1UEAwweTWluaXN0ZXJzdHZvIHNwcmF2b2RsaXZvc3RpIFNSMRcwFQYDVQRhDA5OVFJTSy0wMDE2NjA3MzEXMBUGA1UEBRMOSUNBIC0gMTA0MzIxMzkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCWG6O21F/DSe4QCHnkElUAcqmNshPiW6d05gWUnbq8RwqRyMJJ5lZxNvAmcgB0ob8v34Z2TBLfV/vpx81wXJQd/xTvqp/tgTIAoBZrmpBYXJAQJLVXxWihWgHCJFCuPKowFpFcVwrQ6NbINvbXPyuIgWJ/gN4w35I9ipQCslgJWajJNtuF+hQWMvLm11NuY8rBIg4cHGGEgtu8SgqhNY8+NMaILTKpNb3jtP/ITVOCl6cp3wA5TOYPGyXb/pCHVHmnBGehUAs1+BDf1urfTcavZspXU/dTR1ErOiw+pjYQhb6qj+bNX0TqFgsaaXCB8/6GLL5lmVE6SziwZTkCdv6BAgMBAAGjggNWMIIDUjAjBgNVHREEHDAaoBgGCisGAQQBgbhIBAagCgwIMTA0MzIxMzkwDgYDVR0PAQH/BAQDAgbAMIIBLgYDVR0gBIIBJTCCASEwMAYNKwYBBAGBuEgKAVsBATAfMB0GCCsGAQUFBwIBFhFodHRwOi8vd3d3LmljYS5jejCB4QYNK4EekZmEBQAAAAECAjCBzzCBzAYIKwYBBQUHAgIwgb8MgbxFTjogVGhpcyBpcyBhIHF1YWxpZmllZCBjZXJ0aWZpY2F0ZSBmb3IgZWxlY3Ryb25pYyBzZWFsIGFjY29yZGluZyB0byBSZWd1bGF0aW9uIChFVSkgTm8gOTEwLzIwMTQuIFNLOiBLdmFsaWZpa292YW55IGNlcnRpZmlrYXQgcHJlIGVsZWt0cm9uaWNrdSBwZWNhdCB2IHN1bGFkZSBzIG5hcmlhZGVuaW0gKEVVKSBjLjkxMC8yMDE0LjAJBgcEAIvsQAEDMIGMBgNVHR8EgYQwgYEwKaAnoCWGI2h0dHA6Ly9xY3JsZHAxLmljYS5jei9xY2ExNV9yc2EuY3JsMCmgJ6AlhiNodHRwOi8vcWNybGRwMi5pY2EuY3ovcWNhMTVfcnNhLmNybDApoCegJYYjaHR0cDovL3FjcmxkcDMuaWNhLmN6L3FjYTE1X3JzYS5jcmwwgZIGCCsGAQUFBwEDBIGFMIGCMAgGBgQAjkYBATAIBgYEAI5GAQQwVwYGBACORgEFME0wLRYnaHR0cHM6Ly93d3cuaWNhLmN6L1pwcmF2eS1wcm8tdXppdmF0ZWxlEwJjczAcFhZodHRwczovL3d3dy5pY2EuY3ovUERTEwJlbjATBgYEAI5GAQYwCQYHBACORgEGAjBlBggrBgEFBQcBAQRZMFcwKwYIKwYBBQUHMAKGH2h0dHA6Ly9xLmljYS5jei9xY2ExNXNrX3JzYS5wN2MwKAYIKwYBBQUHMAGGHGh0dHA6Ly9vY3NwLmljYS5jei9xY2ExNV9yc2EwCQYDVR0TBAIwADAdBgNVHQ4EFgQUZnA9DYix8Eh4k/Q/zdAD88y3Y+MwHwYDVR0jBBgwFoAUbIEnWTPiopohGIspFLw4bdRzeT0wEwYDVR0lBAwwCgYIKwYBBQUHAwQwDQYJKoZIhvcNAQELBQADggIBANgEAV4KCWPyH+2NB8JAc9rUiE+zDHMZO31ovV8FHiDUthcoghwgPhC4ufM5pDpgB73GMuGLA1vv0VqEH6jRAWsU9l8qobGYuBcmHaHCY79zLXCMSpwlQu5nlbOPUr5FqgtIWal7m2uHRrVJrK96VWtLALeFn18PPBwK2ylhWjoKCtwehLmKwaYnefROR2R2DbaRL+Wp6SXu9lDY7itsRBtRzZ7bJooji05609wWlWsmAYLT7KNXCzpYCFBu8DOY6HGNUbM1f5JU+BfiI7ITIGQeipx8uQymko8vEhaEXLR1oNtWdjo5hPPYiUMrUMK3hiXd29k9npsr1BWJC+RGzJSu/la6TEOxK/MUtkVtXZzWib1IS1JugGsn8mdJoHgRXOPBuX84PybEuRy/INl8PAXPP6dYkN4niIh1iVV+NQoCpP2C13XApd7uzssCFbMAlVUyAlNShookOXZs2js7d0yrnM1HTuyrxtfZV7D8rSqsKxZK0feRlU/di4/Zv+9+pdLBZQWWB0Ej7gRdHmIDPIwW0EduCIeffLCGLhz8/yPdvlfIexDoL6RGjtC4ptFwrfI7QT6/er27Q1XOyu9WkASDQi04KNkHLZ/MPgOdwk1816bDW/NtY0k1pdJ/1HEDUvTC+HdWJt0HxAPwrBprnXFj2u/b1Cv9jxVxW1bub5R6", + ), + areQualifiedTimestamps: true, + timestamps: [ + DocumentValidationResponseBody$Signatures$Item$Timestamps$Item( + qualification: + DocumentValidationResponseBody$Signatures$Item$Timestamps$ItemQualification + .qtsa, + timestampType: + DocumentValidationResponseBody$Signatures$Item$Timestamps$ItemTimestampType + .signatureTimestamp, + subjectDN: + "CN=NASES Time Stamp Authority 2, O=Národná agentúra pre sieťové a elektronické služby, OID.2.5.4.97=NTRSK-42156424, OU=SNCA, C=SK", + certificateDer: + "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", + productionTime: "2024-08-02T13:01:24 +0200", + ), + ], + ), + ); +} diff --git a/lib/widgetbook_app.directories.g.dart b/lib/widgetbook_app.directories.g.dart index c12dc4e..53aeb0f 100644 --- a/lib/widgetbook_app.directories.g.dart +++ b/lib/widgetbook_app.directories.g.dart @@ -11,46 +11,49 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:autogram/ui/app_theme.dart' as _i3; import 'package:autogram/ui/assets.dart' as _i4; -import 'package:autogram/ui/fragment/show_web_page_fragment.dart' as _i23; -import 'package:autogram/ui/screens/about_screen.dart' as _i26; -import 'package:autogram/ui/screens/main_menu_screen.dart' as _i27; +import 'package:autogram/ui/fragment/document_validation_info_fragment.dart' + as _i24; +import 'package:autogram/ui/fragment/show_web_page_fragment.dart' as _i25; +import 'package:autogram/ui/screens/about_screen.dart' as _i28; +import 'package:autogram/ui/screens/main_menu_screen.dart' as _i29; import 'package:autogram/ui/screens/main_screen.dart' as _i2; import 'package:autogram/ui/screens/onboarding_accept_document_screen.dart' - as _i28; -import 'package:autogram/ui/screens/onboarding_finished_screen.dart' as _i29; -import 'package:autogram/ui/screens/onboarding_select_signing_certificate_screen.dart' as _i30; -import 'package:autogram/ui/screens/open_document_screen.dart' as _i31; -import 'package:autogram/ui/screens/paired_device_list_screen.dart' as _i32; +import 'package:autogram/ui/screens/onboarding_finished_screen.dart' as _i31; +import 'package:autogram/ui/screens/onboarding_select_signing_certificate_screen.dart' + as _i32; +import 'package:autogram/ui/screens/open_document_screen.dart' as _i33; +import 'package:autogram/ui/screens/paired_device_list_screen.dart' as _i34; import 'package:autogram/ui/screens/present_signed_document_screen.dart' - as _i33; -import 'package:autogram/ui/screens/preview_document_screen.dart' as _i34; -import 'package:autogram/ui/screens/qr_code_scanner_screen.dart' as _i21; -import 'package:autogram/ui/screens/select_certificate_screen.dart' as _i35; -import 'package:autogram/ui/screens/settings_screen.dart' as _i36; -import 'package:autogram/ui/screens/show_document_screen.dart' as _i37; -import 'package:autogram/ui/screens/sign_document_screen.dart' as _i38; + as _i35; +import 'package:autogram/ui/screens/preview_document_screen.dart' as _i36; +import 'package:autogram/ui/screens/qr_code_scanner_screen.dart' as _i22; +import 'package:autogram/ui/screens/select_certificate_screen.dart' as _i37; +import 'package:autogram/ui/screens/settings_screen.dart' as _i38; +import 'package:autogram/ui/screens/show_document_screen.dart' as _i39; +import 'package:autogram/ui/screens/sign_document_screen.dart' as _i40; import 'package:autogram/ui/screens/start_remote_document_signing_screen.dart' - as _i39; + as _i41; import 'package:autogram/ui/widgets/app_version_text.dart' as _i9; import 'package:autogram/ui/widgets/autogram_logo.dart' as _i5; import 'package:autogram/ui/widgets/buttons.dart' as _i6; -import 'package:autogram/ui/widgets/certificate_picker.dart' as _i24; +import 'package:autogram/ui/widgets/certificate_picker.dart' as _i26; import 'package:autogram/ui/widgets/close_button.dart' as _i7; -import 'package:autogram/ui/widgets/dialogs.dart' as _i22; -import 'package:autogram/ui/widgets/document_validation_strip.dart' as _i10; -import 'package:autogram/ui/widgets/document_visualization.dart' as _i11; -import 'package:autogram/ui/widgets/error_content.dart' as _i12; -import 'package:autogram/ui/widgets/html_preview.dart' as _i13; -import 'package:autogram/ui/widgets/loading_content.dart' as _i14; +import 'package:autogram/ui/widgets/dialogs.dart' as _i23; +import 'package:autogram/ui/widgets/document_validation_info.dart' as _i10; +import 'package:autogram/ui/widgets/document_validation_strip.dart' as _i11; +import 'package:autogram/ui/widgets/document_visualization.dart' as _i12; +import 'package:autogram/ui/widgets/error_content.dart' as _i13; +import 'package:autogram/ui/widgets/html_preview.dart' as _i14; +import 'package:autogram/ui/widgets/loading_content.dart' as _i15; import 'package:autogram/ui/widgets/loading_indicator.dart' as _i8; -import 'package:autogram/ui/widgets/markdown_text.dart' as _i15; -import 'package:autogram/ui/widgets/option_picker.dart' as _i16; -import 'package:autogram/ui/widgets/preference_tile.dart' as _i17; -import 'package:autogram/ui/widgets/result_view.dart' as _i18; -import 'package:autogram/ui/widgets/retry_view.dart' as _i19; -import 'package:autogram/ui/widgets/signature_type_picker.dart' as _i25; -import 'package:autogram/ui/widgets/step_indicator.dart' as _i20; +import 'package:autogram/ui/widgets/markdown_text.dart' as _i16; +import 'package:autogram/ui/widgets/option_picker.dart' as _i17; +import 'package:autogram/ui/widgets/preference_tile.dart' as _i18; +import 'package:autogram/ui/widgets/result_view.dart' as _i19; +import 'package:autogram/ui/widgets/retry_view.dart' as _i20; +import 'package:autogram/ui/widgets/signature_type_picker.dart' as _i27; +import 'package:autogram/ui/widgets/step_indicator.dart' as _i21; import 'package:widgetbook/widgetbook.dart' as _i1; final directories = <_i1.WidgetbookNode>[ @@ -157,16 +160,23 @@ final directories = <_i1.WidgetbookNode>[ builder: _i9.previewAppVersionText, ), ), + _i1.WidgetbookLeafComponent( + name: 'DocumentValidationInfo', + useCase: _i1.WidgetbookUseCase( + name: '', + builder: _i10.previewDocumentValidationInfo, + ), + ), _i1.WidgetbookComponent( name: 'DocumentValidationStrip', useCases: [ _i1.WidgetbookUseCase( name: 'loading', - builder: _i10.previewLoadingDocumentValidationStrip, + builder: _i11.previewLoadingDocumentValidationStrip, ), _i1.WidgetbookUseCase( name: 'signatures', - builder: _i10.previewOtherDocumentValidationStrip, + builder: _i11.previewOtherDocumentValidationStrip, ), ], ), @@ -174,49 +184,49 @@ final directories = <_i1.WidgetbookNode>[ name: 'DocumentVisualization', useCase: _i1.WidgetbookUseCase( name: 'DocumentVisualization', - builder: _i11.previewDocumentVisualization, + builder: _i12.previewDocumentVisualization, ), ), _i1.WidgetbookLeafComponent( name: 'ErrorContent', useCase: _i1.WidgetbookUseCase( name: 'ErrorContent', - builder: _i12.previewErrorContent, + builder: _i13.previewErrorContent, ), ), _i1.WidgetbookLeafComponent( name: 'HtmlPreview', useCase: _i1.WidgetbookUseCase( name: 'HtmlPreview', - builder: _i13.previewHtmlPreview, + builder: _i14.previewHtmlPreview, ), ), _i1.WidgetbookLeafComponent( name: 'LoadingContent', useCase: _i1.WidgetbookUseCase( name: 'LoadingContent', - builder: _i14.previewLoadingContent, + builder: _i15.previewLoadingContent, ), ), _i1.WidgetbookLeafComponent( name: 'MarkdownText', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i15.previewMarkdownText, + builder: _i16.previewMarkdownText, ), ), _i1.WidgetbookLeafComponent( name: 'OptionPicker', useCase: _i1.WidgetbookUseCase( name: 'OptionPicker', - builder: _i16.previewOptionPicker, + builder: _i17.previewOptionPicker, ), ), _i1.WidgetbookLeafComponent( name: 'PreferenceTile', useCase: _i1.WidgetbookUseCase( name: 'PreferenceTile', - builder: _i17.previewPreferenceTile, + builder: _i18.previewPreferenceTile, ), ), _i1.WidgetbookComponent( @@ -224,19 +234,19 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'custom', - builder: _i18.previewCustomResultView, + builder: _i19.previewCustomResultView, ), _i1.WidgetbookUseCase( name: 'error', - builder: _i18.previewErrorResultView, + builder: _i19.previewErrorResultView, ), _i1.WidgetbookUseCase( name: 'info', - builder: _i18.previewInfoResultView, + builder: _i19.previewInfoResultView, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i18.previewSuccessResultView, + builder: _i19.previewSuccessResultView, ), ], ), @@ -244,21 +254,21 @@ final directories = <_i1.WidgetbookNode>[ name: 'RetryView', useCase: _i1.WidgetbookUseCase( name: 'RetryView', - builder: _i19.previewRetryView, + builder: _i20.previewRetryView, ), ), _i1.WidgetbookLeafComponent( name: 'StepIndicator', useCase: _i1.WidgetbookUseCase( name: 'StepIndicator', - builder: _i20.previewStepIndicator, + builder: _i21.previewStepIndicator, ), ), _i1.WidgetbookLeafComponent( name: '_ViewFinder', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i21.previewViewFinder, + builder: _i22.previewViewFinder, ), ), ], @@ -270,7 +280,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'BottomSheet', useCase: _i1.WidgetbookUseCase( name: 'NotificationsPermissionRationale', - builder: _i22.previewNotificationsPermissionRationaleModal, + builder: _i23.previewNotificationsPermissionRationaleModal, ), ) ], @@ -278,13 +288,20 @@ final directories = <_i1.WidgetbookNode>[ _i1.WidgetbookCategory( name: 'Fragments', children: [ + _i1.WidgetbookLeafComponent( + name: 'DocumentValidationInfoFragment', + useCase: _i1.WidgetbookUseCase( + name: '', + builder: _i24.previewDocumentValidationInfoFragment, + ), + ), _i1.WidgetbookLeafComponent( name: 'ShowWebPageFragment', useCase: _i1.WidgetbookUseCase( name: 'ShowWebPageFragment', - builder: _i23.previewShowWebPageFragment, + builder: _i25.previewShowWebPageFragment, ), - ) + ), ], ), _i1.WidgetbookCategory( @@ -294,14 +311,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'CertificatePicker', useCase: _i1.WidgetbookUseCase( name: 'CertificatePicker', - builder: _i24.previewCertificatePicker, + builder: _i26.previewCertificatePicker, ), ), _i1.WidgetbookLeafComponent( name: 'SignatureTypePicker', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i25.previewSignatureTypePicker, + builder: _i27.previewSignatureTypePicker, ), ), ], @@ -313,14 +330,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'AboutScreen', useCase: _i1.WidgetbookUseCase( name: 'AboutScreen', - builder: _i26.previewAboutScreen, + builder: _i28.previewAboutScreen, ), ), _i1.WidgetbookLeafComponent( name: 'MainMenuScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i27.previewMainMenuScreen, + builder: _i29.previewMainMenuScreen, ), ), _i1.WidgetbookLeafComponent( @@ -334,14 +351,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'OnboardingAcceptDocumentScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i28.previewOnboardingAcceptDocumentScreen, + builder: _i30.previewOnboardingAcceptDocumentScreen, ), ), _i1.WidgetbookLeafComponent( name: 'OnboardingFinishedScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i29.previewOnboardingFinishedScreen, + builder: _i31.previewOnboardingFinishedScreen, ), ), _i1.WidgetbookComponent( @@ -349,20 +366,20 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'canceled', - builder: _i30.previewCanceledOnboardingSelectSigningCertificateBody, + builder: _i32.previewCanceledOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'initial', - builder: _i30.previewInitialOnboardingSelectSigningCertificateBody, + builder: _i32.previewInitialOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'no certificate', builder: - _i30.previewNoCertificateOnboardingSelectSigningCertificateBody, + _i32.previewNoCertificateOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i30.previewSuccessOnboardingSelectSigningCertificateBody, + builder: _i32.previewSuccessOnboardingSelectSigningCertificateBody, ), ], ), @@ -371,11 +388,11 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i31.previewErrorOpenDocumentScreen, + builder: _i33.previewErrorOpenDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i31.previewLoadingOpenDocumentScreen, + builder: _i33.previewLoadingOpenDocumentScreen, ), ], ), @@ -383,7 +400,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'PairedDeviceListScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i32.previewPairedDeviceListScreen, + builder: _i34.previewPairedDeviceListScreen, ), ), _i1.WidgetbookComponent( @@ -391,19 +408,19 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i33.previewErrorPresentSignedDocumentScreen, + builder: _i35.previewErrorPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'initial', - builder: _i33.previewInitialPresentSignedDocumentScreen, + builder: _i35.previewInitialPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i33.previewLoadingPresentSignedDocumentScreen, + builder: _i35.previewLoadingPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i33.previewSuccessPresentSignedDocumentScreen, + builder: _i35.previewSuccessPresentSignedDocumentScreen, ), ], ), @@ -412,15 +429,15 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i34.previewErrorPreviewDocumentScreen, + builder: _i36.previewErrorPreviewDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i34.previewLoadingPreviewDocumentScreen, + builder: _i36.previewLoadingPreviewDocumentScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i34.previewSuccessPreviewDocumentScreen, + builder: _i36.previewSuccessPreviewDocumentScreen, ), ], ), @@ -428,7 +445,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'QRCodeScannerScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i21.previewQRCodeScannerScreen, + builder: _i22.previewQRCodeScannerScreen, ), ), _i1.WidgetbookComponent( @@ -436,23 +453,23 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'canceled', - builder: _i35.previewCanceledSelectCertificateScreen, + builder: _i37.previewCanceledSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'error', - builder: _i35.previewErrorSelectCertificateScreen, + builder: _i37.previewErrorSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i35.previewLoadingSelectCertificateScreen, + builder: _i37.previewLoadingSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'no certificate', - builder: _i35.previewNoCertificateSelectCertificateScreen, + builder: _i37.previewNoCertificateSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i35.previewSuccessSelectCertificateScreen, + builder: _i37.previewSuccessSelectCertificateScreen, ), ], ), @@ -460,14 +477,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'SettingsScreen', useCase: _i1.WidgetbookUseCase( name: 'SettingsScreen', - builder: _i36.previewSettingsScreen, + builder: _i38.previewSettingsScreen, ), ), _i1.WidgetbookLeafComponent( name: 'ShowDocumentScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i37.previewShowDocumentScreen, + builder: _i39.previewShowDocumentScreen, ), ), _i1.WidgetbookComponent( @@ -475,15 +492,15 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i38.previewErrorSignDocumentScreen, + builder: _i40.previewErrorSignDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i38.previewLoadingSignDocumentScreen, + builder: _i40.previewLoadingSignDocumentScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i38.previewSuccessSignDocumentScreen, + builder: _i40.previewSuccessSignDocumentScreen, ), ], ), @@ -491,7 +508,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'StartRemoteDocumentSigningScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i39.previewStartRemoteDocumentSigningScreen, + builder: _i41.previewStartRemoteDocumentSigningScreen, ), ), ], From ed81217a775386855c048af248e8d89d8923c437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Mon, 9 Sep 2024 19:57:35 +0200 Subject: [PATCH 11/34] Impl. custom AVM Chip widget --- lib/ui/widgets/chip.dart | 85 +++++++++ lib/ui/widgets/document_validation_info.dart | 66 ++++++- lib/widgetbook_app.directories.g.dart | 188 ++++++++++--------- 3 files changed, 244 insertions(+), 95 deletions(-) create mode 100644 lib/ui/widgets/chip.dart diff --git a/lib/ui/widgets/chip.dart b/lib/ui/widgets/chip.dart new file mode 100644 index 0000000..b7d15ec --- /dev/null +++ b/lib/ui/widgets/chip.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart' as material; +import 'package:flutter/widgets.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; + +/// Chip widget with [label] text inside. +/// Has [foreground], [background] and [border] colors, and optional +/// [leading] widget. +class Chip extends StatelessWidget { + final Widget? leading; + final String label; + final Color foreground; + final Color background; + final Color border; + + const Chip({ + super.key, + this.leading, + required this.label, + required this.foreground, + required this.background, + required this.border, + }); + + @override + material.Widget build(material.BuildContext context) { + return Container( + decoration: BoxDecoration( + color: background, + border: Border.all(color: border, width: 1), + borderRadius: const BorderRadius.all(Radius.circular(4)), + ), + padding: const EdgeInsets.all(4), + child: Row( + children: [ + if (leading != null) leading!, + Text(label, style: TextStyle(color: foreground)), + ], + ), + ); + } +} + +@widgetbook.UseCase( + path: '[AVM]', + name: '', + type: Chip, +) +Widget previewChip(BuildContext context) { + var label = context.knobs.string( + label: 'Label', + initialValue: 'Chip!', + ); + var foreground = context.knobs.color( + label: 'Foreground', + initialValue: material.Colors.black, + ); + var background = context.knobs.color( + label: 'Background', + initialValue: material.Colors.white, + ); + var border = context.knobs.color( + label: 'Border', + initialValue: material.Colors.black, + ); + var icon = context.knobs.listOrNull( + label: 'Icon', + options: ['ok', 'warning', 'error'], + ); + + return Chip( + label: label, + foreground: foreground, + background: background, + border: border, + leading: (icon != null + ? Icon(switch (icon) { + 'ok' => material.Icons.check, + 'warning' => material.Icons.warning_amber_outlined, + 'error' => material.Icons.error_outline, + _ => material.Icons.question_mark, + }) + : null), + ); +} diff --git a/lib/ui/widgets/document_validation_info.dart b/lib/ui/widgets/document_validation_info.dart index 79ca01b..46cd70e 100644 --- a/lib/ui/widgets/document_validation_info.dart +++ b/lib/ui/widgets/document_validation_info.dart @@ -4,6 +4,7 @@ import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; import '../../oids.dart'; import '../../utils.dart'; +import '../widgets/chip.dart' as avm; /// Displays Document validation info based on [data] provided. class DocumentValidationInfo extends StatelessWidget { @@ -35,14 +36,69 @@ class DocumentValidationInfo extends StatelessWidget { overflow: TextOverflow.ellipsis, ), ), - ElevatedButton( - onPressed: null, - // TODO Show various states - with color and custom text - extract as Widget - child: Text(data.validationResult.name), - ), + _buildChip(context), ], ); } + + Widget _buildChip(BuildContext context) { + final icon = switch (data.validationResult) { + DocumentValidationResponseBody$Signatures$ItemValidationResult + .totalPassed => + Icons.check, + DocumentValidationResponseBody$Signatures$ItemValidationResult + .indeterminate => + Icons.warning_amber_outlined, + DocumentValidationResponseBody$Signatures$ItemValidationResult + .totalFailed => + Icons.error_outline, + _ => Icons.question_mark_outlined, + }; + final foreground = switch (data.validationResult) { + DocumentValidationResponseBody$Signatures$ItemValidationResult + .totalPassed => + const Color(0xFF033608), + DocumentValidationResponseBody$Signatures$ItemValidationResult + .indeterminate => + const Color(0xFF4E2A00), + DocumentValidationResponseBody$Signatures$ItemValidationResult + .totalFailed => + const Color(0xFF4E0711), + _ => Colors.black, + }; + final background = switch (data.validationResult) { + DocumentValidationResponseBody$Signatures$ItemValidationResult + .totalPassed => + const Color(0xFFEDF5F3), + DocumentValidationResponseBody$Signatures$ItemValidationResult + .indeterminate => + const Color(0xFFF4F4EC), + DocumentValidationResponseBody$Signatures$ItemValidationResult + .totalFailed => + const Color(0xFFFBEEF0), + _ => Colors.white, + }; + final border = switch (data.validationResult) { + DocumentValidationResponseBody$Signatures$ItemValidationResult + .totalPassed => + const Color(0xFFA9D9CD), + DocumentValidationResponseBody$Signatures$ItemValidationResult + .indeterminate => + const Color(0xFFD5D6A2), + DocumentValidationResponseBody$Signatures$ItemValidationResult + .totalFailed => + const Color(0xFFC3112B), + _ => Colors.grey, + }; + + return avm.Chip( + label: 'label', + foreground: foreground, + background: background, + border: border, + leading: Icon(icon, color: foreground), + ); + } } @widgetbook.UseCase( diff --git a/lib/widgetbook_app.directories.g.dart b/lib/widgetbook_app.directories.g.dart index 53aeb0f..919ba4d 100644 --- a/lib/widgetbook_app.directories.g.dart +++ b/lib/widgetbook_app.directories.g.dart @@ -12,48 +12,49 @@ import 'package:autogram/ui/app_theme.dart' as _i3; import 'package:autogram/ui/assets.dart' as _i4; import 'package:autogram/ui/fragment/document_validation_info_fragment.dart' - as _i24; -import 'package:autogram/ui/fragment/show_web_page_fragment.dart' as _i25; -import 'package:autogram/ui/screens/about_screen.dart' as _i28; -import 'package:autogram/ui/screens/main_menu_screen.dart' as _i29; + as _i25; +import 'package:autogram/ui/fragment/show_web_page_fragment.dart' as _i26; +import 'package:autogram/ui/screens/about_screen.dart' as _i29; +import 'package:autogram/ui/screens/main_menu_screen.dart' as _i30; import 'package:autogram/ui/screens/main_screen.dart' as _i2; import 'package:autogram/ui/screens/onboarding_accept_document_screen.dart' - as _i30; -import 'package:autogram/ui/screens/onboarding_finished_screen.dart' as _i31; + as _i31; +import 'package:autogram/ui/screens/onboarding_finished_screen.dart' as _i32; import 'package:autogram/ui/screens/onboarding_select_signing_certificate_screen.dart' - as _i32; -import 'package:autogram/ui/screens/open_document_screen.dart' as _i33; -import 'package:autogram/ui/screens/paired_device_list_screen.dart' as _i34; + as _i33; +import 'package:autogram/ui/screens/open_document_screen.dart' as _i34; +import 'package:autogram/ui/screens/paired_device_list_screen.dart' as _i35; import 'package:autogram/ui/screens/present_signed_document_screen.dart' - as _i35; -import 'package:autogram/ui/screens/preview_document_screen.dart' as _i36; -import 'package:autogram/ui/screens/qr_code_scanner_screen.dart' as _i22; -import 'package:autogram/ui/screens/select_certificate_screen.dart' as _i37; -import 'package:autogram/ui/screens/settings_screen.dart' as _i38; -import 'package:autogram/ui/screens/show_document_screen.dart' as _i39; -import 'package:autogram/ui/screens/sign_document_screen.dart' as _i40; + as _i36; +import 'package:autogram/ui/screens/preview_document_screen.dart' as _i37; +import 'package:autogram/ui/screens/qr_code_scanner_screen.dart' as _i23; +import 'package:autogram/ui/screens/select_certificate_screen.dart' as _i38; +import 'package:autogram/ui/screens/settings_screen.dart' as _i39; +import 'package:autogram/ui/screens/show_document_screen.dart' as _i40; +import 'package:autogram/ui/screens/sign_document_screen.dart' as _i41; import 'package:autogram/ui/screens/start_remote_document_signing_screen.dart' - as _i41; -import 'package:autogram/ui/widgets/app_version_text.dart' as _i9; + as _i42; +import 'package:autogram/ui/widgets/app_version_text.dart' as _i10; import 'package:autogram/ui/widgets/autogram_logo.dart' as _i5; import 'package:autogram/ui/widgets/buttons.dart' as _i6; -import 'package:autogram/ui/widgets/certificate_picker.dart' as _i26; -import 'package:autogram/ui/widgets/close_button.dart' as _i7; -import 'package:autogram/ui/widgets/dialogs.dart' as _i23; -import 'package:autogram/ui/widgets/document_validation_info.dart' as _i10; -import 'package:autogram/ui/widgets/document_validation_strip.dart' as _i11; -import 'package:autogram/ui/widgets/document_visualization.dart' as _i12; -import 'package:autogram/ui/widgets/error_content.dart' as _i13; -import 'package:autogram/ui/widgets/html_preview.dart' as _i14; -import 'package:autogram/ui/widgets/loading_content.dart' as _i15; -import 'package:autogram/ui/widgets/loading_indicator.dart' as _i8; -import 'package:autogram/ui/widgets/markdown_text.dart' as _i16; -import 'package:autogram/ui/widgets/option_picker.dart' as _i17; -import 'package:autogram/ui/widgets/preference_tile.dart' as _i18; -import 'package:autogram/ui/widgets/result_view.dart' as _i19; -import 'package:autogram/ui/widgets/retry_view.dart' as _i20; -import 'package:autogram/ui/widgets/signature_type_picker.dart' as _i27; -import 'package:autogram/ui/widgets/step_indicator.dart' as _i21; +import 'package:autogram/ui/widgets/certificate_picker.dart' as _i27; +import 'package:autogram/ui/widgets/chip.dart' as _i7; +import 'package:autogram/ui/widgets/close_button.dart' as _i8; +import 'package:autogram/ui/widgets/dialogs.dart' as _i24; +import 'package:autogram/ui/widgets/document_validation_info.dart' as _i11; +import 'package:autogram/ui/widgets/document_validation_strip.dart' as _i12; +import 'package:autogram/ui/widgets/document_visualization.dart' as _i13; +import 'package:autogram/ui/widgets/error_content.dart' as _i14; +import 'package:autogram/ui/widgets/html_preview.dart' as _i15; +import 'package:autogram/ui/widgets/loading_content.dart' as _i16; +import 'package:autogram/ui/widgets/loading_indicator.dart' as _i9; +import 'package:autogram/ui/widgets/markdown_text.dart' as _i17; +import 'package:autogram/ui/widgets/option_picker.dart' as _i18; +import 'package:autogram/ui/widgets/preference_tile.dart' as _i19; +import 'package:autogram/ui/widgets/result_view.dart' as _i20; +import 'package:autogram/ui/widgets/retry_view.dart' as _i21; +import 'package:autogram/ui/widgets/signature_type_picker.dart' as _i28; +import 'package:autogram/ui/widgets/step_indicator.dart' as _i22; import 'package:widgetbook/widgetbook.dart' as _i1; final directories = <_i1.WidgetbookNode>[ @@ -106,11 +107,18 @@ final directories = <_i1.WidgetbookNode>[ ), ], ), + _i1.WidgetbookLeafComponent( + name: 'Chip', + useCase: _i1.WidgetbookUseCase( + name: '', + builder: _i7.previewChip, + ), + ), _i1.WidgetbookLeafComponent( name: 'CloseButton', useCase: _i1.WidgetbookUseCase( name: 'CloseButton', - builder: _i7.previewCloseButton, + builder: _i8.previewCloseButton, ), ), _i1.WidgetbookLeafComponent( @@ -124,7 +132,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'LoadingIndicator', useCase: _i1.WidgetbookUseCase( name: 'LoadingIndicator', - builder: _i8.previewLoadingIndicator, + builder: _i9.previewLoadingIndicator, ), ), _i1.WidgetbookLeafComponent( @@ -157,14 +165,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'AppVersionText', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i9.previewAppVersionText, + builder: _i10.previewAppVersionText, ), ), _i1.WidgetbookLeafComponent( name: 'DocumentValidationInfo', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i10.previewDocumentValidationInfo, + builder: _i11.previewDocumentValidationInfo, ), ), _i1.WidgetbookComponent( @@ -172,11 +180,11 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'loading', - builder: _i11.previewLoadingDocumentValidationStrip, + builder: _i12.previewLoadingDocumentValidationStrip, ), _i1.WidgetbookUseCase( name: 'signatures', - builder: _i11.previewOtherDocumentValidationStrip, + builder: _i12.previewOtherDocumentValidationStrip, ), ], ), @@ -184,49 +192,49 @@ final directories = <_i1.WidgetbookNode>[ name: 'DocumentVisualization', useCase: _i1.WidgetbookUseCase( name: 'DocumentVisualization', - builder: _i12.previewDocumentVisualization, + builder: _i13.previewDocumentVisualization, ), ), _i1.WidgetbookLeafComponent( name: 'ErrorContent', useCase: _i1.WidgetbookUseCase( name: 'ErrorContent', - builder: _i13.previewErrorContent, + builder: _i14.previewErrorContent, ), ), _i1.WidgetbookLeafComponent( name: 'HtmlPreview', useCase: _i1.WidgetbookUseCase( name: 'HtmlPreview', - builder: _i14.previewHtmlPreview, + builder: _i15.previewHtmlPreview, ), ), _i1.WidgetbookLeafComponent( name: 'LoadingContent', useCase: _i1.WidgetbookUseCase( name: 'LoadingContent', - builder: _i15.previewLoadingContent, + builder: _i16.previewLoadingContent, ), ), _i1.WidgetbookLeafComponent( name: 'MarkdownText', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i16.previewMarkdownText, + builder: _i17.previewMarkdownText, ), ), _i1.WidgetbookLeafComponent( name: 'OptionPicker', useCase: _i1.WidgetbookUseCase( name: 'OptionPicker', - builder: _i17.previewOptionPicker, + builder: _i18.previewOptionPicker, ), ), _i1.WidgetbookLeafComponent( name: 'PreferenceTile', useCase: _i1.WidgetbookUseCase( name: 'PreferenceTile', - builder: _i18.previewPreferenceTile, + builder: _i19.previewPreferenceTile, ), ), _i1.WidgetbookComponent( @@ -234,19 +242,19 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'custom', - builder: _i19.previewCustomResultView, + builder: _i20.previewCustomResultView, ), _i1.WidgetbookUseCase( name: 'error', - builder: _i19.previewErrorResultView, + builder: _i20.previewErrorResultView, ), _i1.WidgetbookUseCase( name: 'info', - builder: _i19.previewInfoResultView, + builder: _i20.previewInfoResultView, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i19.previewSuccessResultView, + builder: _i20.previewSuccessResultView, ), ], ), @@ -254,21 +262,21 @@ final directories = <_i1.WidgetbookNode>[ name: 'RetryView', useCase: _i1.WidgetbookUseCase( name: 'RetryView', - builder: _i20.previewRetryView, + builder: _i21.previewRetryView, ), ), _i1.WidgetbookLeafComponent( name: 'StepIndicator', useCase: _i1.WidgetbookUseCase( name: 'StepIndicator', - builder: _i21.previewStepIndicator, + builder: _i22.previewStepIndicator, ), ), _i1.WidgetbookLeafComponent( name: '_ViewFinder', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i22.previewViewFinder, + builder: _i23.previewViewFinder, ), ), ], @@ -280,7 +288,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'BottomSheet', useCase: _i1.WidgetbookUseCase( name: 'NotificationsPermissionRationale', - builder: _i23.previewNotificationsPermissionRationaleModal, + builder: _i24.previewNotificationsPermissionRationaleModal, ), ) ], @@ -292,14 +300,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'DocumentValidationInfoFragment', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i24.previewDocumentValidationInfoFragment, + builder: _i25.previewDocumentValidationInfoFragment, ), ), _i1.WidgetbookLeafComponent( name: 'ShowWebPageFragment', useCase: _i1.WidgetbookUseCase( name: 'ShowWebPageFragment', - builder: _i25.previewShowWebPageFragment, + builder: _i26.previewShowWebPageFragment, ), ), ], @@ -311,14 +319,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'CertificatePicker', useCase: _i1.WidgetbookUseCase( name: 'CertificatePicker', - builder: _i26.previewCertificatePicker, + builder: _i27.previewCertificatePicker, ), ), _i1.WidgetbookLeafComponent( name: 'SignatureTypePicker', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i27.previewSignatureTypePicker, + builder: _i28.previewSignatureTypePicker, ), ), ], @@ -330,14 +338,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'AboutScreen', useCase: _i1.WidgetbookUseCase( name: 'AboutScreen', - builder: _i28.previewAboutScreen, + builder: _i29.previewAboutScreen, ), ), _i1.WidgetbookLeafComponent( name: 'MainMenuScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i29.previewMainMenuScreen, + builder: _i30.previewMainMenuScreen, ), ), _i1.WidgetbookLeafComponent( @@ -351,14 +359,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'OnboardingAcceptDocumentScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i30.previewOnboardingAcceptDocumentScreen, + builder: _i31.previewOnboardingAcceptDocumentScreen, ), ), _i1.WidgetbookLeafComponent( name: 'OnboardingFinishedScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i31.previewOnboardingFinishedScreen, + builder: _i32.previewOnboardingFinishedScreen, ), ), _i1.WidgetbookComponent( @@ -366,20 +374,20 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'canceled', - builder: _i32.previewCanceledOnboardingSelectSigningCertificateBody, + builder: _i33.previewCanceledOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'initial', - builder: _i32.previewInitialOnboardingSelectSigningCertificateBody, + builder: _i33.previewInitialOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'no certificate', builder: - _i32.previewNoCertificateOnboardingSelectSigningCertificateBody, + _i33.previewNoCertificateOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i32.previewSuccessOnboardingSelectSigningCertificateBody, + builder: _i33.previewSuccessOnboardingSelectSigningCertificateBody, ), ], ), @@ -388,11 +396,11 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i33.previewErrorOpenDocumentScreen, + builder: _i34.previewErrorOpenDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i33.previewLoadingOpenDocumentScreen, + builder: _i34.previewLoadingOpenDocumentScreen, ), ], ), @@ -400,7 +408,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'PairedDeviceListScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i34.previewPairedDeviceListScreen, + builder: _i35.previewPairedDeviceListScreen, ), ), _i1.WidgetbookComponent( @@ -408,19 +416,19 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i35.previewErrorPresentSignedDocumentScreen, + builder: _i36.previewErrorPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'initial', - builder: _i35.previewInitialPresentSignedDocumentScreen, + builder: _i36.previewInitialPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i35.previewLoadingPresentSignedDocumentScreen, + builder: _i36.previewLoadingPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i35.previewSuccessPresentSignedDocumentScreen, + builder: _i36.previewSuccessPresentSignedDocumentScreen, ), ], ), @@ -429,15 +437,15 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i36.previewErrorPreviewDocumentScreen, + builder: _i37.previewErrorPreviewDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i36.previewLoadingPreviewDocumentScreen, + builder: _i37.previewLoadingPreviewDocumentScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i36.previewSuccessPreviewDocumentScreen, + builder: _i37.previewSuccessPreviewDocumentScreen, ), ], ), @@ -445,7 +453,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'QRCodeScannerScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i22.previewQRCodeScannerScreen, + builder: _i23.previewQRCodeScannerScreen, ), ), _i1.WidgetbookComponent( @@ -453,23 +461,23 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'canceled', - builder: _i37.previewCanceledSelectCertificateScreen, + builder: _i38.previewCanceledSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'error', - builder: _i37.previewErrorSelectCertificateScreen, + builder: _i38.previewErrorSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i37.previewLoadingSelectCertificateScreen, + builder: _i38.previewLoadingSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'no certificate', - builder: _i37.previewNoCertificateSelectCertificateScreen, + builder: _i38.previewNoCertificateSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i37.previewSuccessSelectCertificateScreen, + builder: _i38.previewSuccessSelectCertificateScreen, ), ], ), @@ -477,14 +485,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'SettingsScreen', useCase: _i1.WidgetbookUseCase( name: 'SettingsScreen', - builder: _i38.previewSettingsScreen, + builder: _i39.previewSettingsScreen, ), ), _i1.WidgetbookLeafComponent( name: 'ShowDocumentScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i39.previewShowDocumentScreen, + builder: _i40.previewShowDocumentScreen, ), ), _i1.WidgetbookComponent( @@ -492,15 +500,15 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i40.previewErrorSignDocumentScreen, + builder: _i41.previewErrorSignDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i40.previewLoadingSignDocumentScreen, + builder: _i41.previewLoadingSignDocumentScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i40.previewSuccessSignDocumentScreen, + builder: _i41.previewSuccessSignDocumentScreen, ), ], ), @@ -508,7 +516,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'StartRemoteDocumentSigningScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i41.previewStartRemoteDocumentSigningScreen, + builder: _i42.previewStartRemoteDocumentSigningScreen, ), ), ], From 10d797ad1a174c803065b571efc86e907dc61f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Mon, 7 Oct 2024 18:01:15 +0200 Subject: [PATCH 12/34] Simplify code to use type alias --- lib/ui/widgets/dialogs.dart | 9 +- lib/ui/widgets/document_validation_info.dart | 93 +++++++++----------- 2 files changed, 47 insertions(+), 55 deletions(-) diff --git a/lib/ui/widgets/dialogs.dart b/lib/ui/widgets/dialogs.dart index 7cc025a..d0fb832 100644 --- a/lib/ui/widgets/dialogs.dart +++ b/lib/ui/widgets/dialogs.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart' as material; import 'package:flutter/material.dart'; import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; @@ -43,15 +44,15 @@ Future showNotificationsPermissionRationaleModal(BuildContext context) { ], ); - return _showModalBottomSheet( + return showModalBottomSheet( context: context, child: child, ); } -/// Calls Material [showModalBottomSheet] with predefined [child] wrapper +/// Calls Material `showModalBottomSheet` with predefined [child] wrapper /// and [avm.CloseButton]. -Future _showModalBottomSheet({ +Future showModalBottomSheet({ required BuildContext context, required Widget child, }) { @@ -67,7 +68,7 @@ Future _showModalBottomSheet({ ), ); - return showModalBottomSheet( + return material.showModalBottomSheet( context: context, isScrollControlled: true, constraints: const BoxConstraints(minWidth: double.infinity), diff --git a/lib/ui/widgets/document_validation_info.dart b/lib/ui/widgets/document_validation_info.dart index 46cd70e..1aec4b8 100644 --- a/lib/ui/widgets/document_validation_info.dart +++ b/lib/ui/widgets/document_validation_info.dart @@ -6,31 +6,46 @@ import '../../oids.dart'; import '../../utils.dart'; import '../widgets/chip.dart' as avm; +typedef _ValidationResult + = DocumentValidationResponseBody$Signatures$ItemValidationResult; + /// Displays Document validation info based on [data] provided. class DocumentValidationInfo extends StatelessWidget { - // TODO Add primitive params for preview + create factory ctor for DocumentValidationResponseBody$Signatures$Item - final DocumentValidationResponseBody$Signatures$Item data; + final String subject; + final DocumentValidationResponseBody$Signatures$ItemValidationResult + validationResult; - const DocumentValidationInfo(this.data, {super.key}); + const DocumentValidationInfo._({ + required this.subject, + required this.validationResult, + }); - @override - Widget build(BuildContext context) { - final cert = - x509CertificateDataFromDer(data.signingCertificate.certificateDer) - .tbsCertificate; + factory DocumentValidationInfo( + DocumentValidationResponseBody$Signatures$Item data, + ) { + final certDer = data.signingCertificate.certificateDer; + final cert = x509CertificateDataFromDer(certDer).tbsCertificate; final label = [ cert?.subject[X500Oids.cn], cert?.subject[X500Oids.ln], cert?.subject[X500Oids.c], ].whereType().join(", "); + return DocumentValidationInfo._( + subject: label, + validationResult: data.validationResult, + ); + } + + @override + Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Text( - label, + subject, maxLines: 2, style: const TextStyle(fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, @@ -42,52 +57,28 @@ class DocumentValidationInfo extends StatelessWidget { } Widget _buildChip(BuildContext context) { - final icon = switch (data.validationResult) { - DocumentValidationResponseBody$Signatures$ItemValidationResult - .totalPassed => - Icons.check, - DocumentValidationResponseBody$Signatures$ItemValidationResult - .indeterminate => - Icons.warning_amber_outlined, - DocumentValidationResponseBody$Signatures$ItemValidationResult - .totalFailed => - Icons.error_outline, + final icon = switch (validationResult) { + _ValidationResult.totalPassed => Icons.check, + _ValidationResult.indeterminate => Icons.warning_amber_outlined, + _ValidationResult.totalFailed => Icons.error_outline, _ => Icons.question_mark_outlined, }; - final foreground = switch (data.validationResult) { - DocumentValidationResponseBody$Signatures$ItemValidationResult - .totalPassed => - const Color(0xFF033608), - DocumentValidationResponseBody$Signatures$ItemValidationResult - .indeterminate => - const Color(0xFF4E2A00), - DocumentValidationResponseBody$Signatures$ItemValidationResult - .totalFailed => - const Color(0xFF4E0711), + final foreground = switch (validationResult) { + _ValidationResult.totalPassed => const Color(0xFF033608), + _ValidationResult.indeterminate => const Color(0xFF4E2A00), + _ValidationResult.totalFailed => const Color(0xFF4E0711), _ => Colors.black, }; - final background = switch (data.validationResult) { - DocumentValidationResponseBody$Signatures$ItemValidationResult - .totalPassed => - const Color(0xFFEDF5F3), - DocumentValidationResponseBody$Signatures$ItemValidationResult - .indeterminate => - const Color(0xFFF4F4EC), - DocumentValidationResponseBody$Signatures$ItemValidationResult - .totalFailed => - const Color(0xFFFBEEF0), + final background = switch (validationResult) { + _ValidationResult.totalPassed => const Color(0xFFEDF5F3), + _ValidationResult.indeterminate => const Color(0xFFF4F4EC), + _ValidationResult.totalFailed => const Color(0xFFFBEEF0), _ => Colors.white, }; - final border = switch (data.validationResult) { - DocumentValidationResponseBody$Signatures$ItemValidationResult - .totalPassed => - const Color(0xFFA9D9CD), - DocumentValidationResponseBody$Signatures$ItemValidationResult - .indeterminate => - const Color(0xFFD5D6A2), - DocumentValidationResponseBody$Signatures$ItemValidationResult - .totalFailed => - const Color(0xFFC3112B), + final border = switch (validationResult) { + _ValidationResult.totalPassed => const Color(0xFFA9D9CD), + _ValidationResult.indeterminate => const Color(0xFFD5D6A2), + _ValidationResult.totalFailed => const Color(0xFFC3112B), _ => Colors.grey, }; @@ -107,8 +98,8 @@ class DocumentValidationInfo extends StatelessWidget { type: DocumentValidationInfo, ) Widget previewDocumentValidationInfo(BuildContext context) { - return const DocumentValidationInfo( - DocumentValidationResponseBody$Signatures$Item( + return DocumentValidationInfo( + const DocumentValidationResponseBody$Signatures$Item( validationResult: DocumentValidationResponseBody$Signatures$ItemValidationResult .totalPassed, From d2e6b6c62c31860804d82045ea6a11dcde3da12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Tue, 15 Oct 2024 22:09:57 +0200 Subject: [PATCH 13/34] Update AGP for latest Android Studio support --- CHANGELOG.md | 1 + android/app/build.gradle | 12 +++--------- android/gradle/wrapper/gradle-wrapper.properties | 2 +- android/settings.gradle | 2 +- pubspec.lock | 2 +- 5 files changed, 7 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca48e06..00bcf51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - #35 - #39 +- Update AGP and Gradle version for latest Android Studio Ladybug (2024.2.1 Patch 1) ## 2024-07-16 - v1.0.4(35) diff --git a/android/app/build.gradle b/android/app/build.gradle index be42fcb..053ad8a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -32,12 +32,12 @@ android { ndkVersion flutter.ndkVersion compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } sourceSets { @@ -76,14 +76,8 @@ android { repositories { google() mavenCentral() - maven { - // Local distribution - url = uri("../../../eidmsdk_flutter/android/libs") - } } flutter { source '../..' } - -dependencies {} diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index ec915a8..5e6b542 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index b9082bf..d31a1c9 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -23,7 +23,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.4.2" apply false + id "com.android.application" version "8.3.2" apply false // START: FlutterFire Configuration id "com.google.gms.google-services" version "4.3.15" apply false id "com.google.firebase.crashlytics" version "2.8.1" apply false diff --git a/pubspec.lock b/pubspec.lock index 6d829ae..2f29686 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -63,7 +63,7 @@ packages: path: "../autogram_sign" relative: true source: path - version: "0.4.3" + version: "0.4.4" barcode: dependency: transitive description: From b4bca05120ac237c1b46d4434890968375b05692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Tue, 15 Oct 2024 22:43:57 +0200 Subject: [PATCH 14/34] Update existing code --- .../document_validation_fragment.dart | 16 ++++---- lib/ui/widgets/document_validation_info.dart | 2 +- lib/ui/widgets/document_validation_strip.dart | 37 +++++++++++-------- 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/lib/ui/fragment/document_validation_fragment.dart b/lib/ui/fragment/document_validation_fragment.dart index 2bef582..7e8adfb 100644 --- a/lib/ui/fragment/document_validation_fragment.dart +++ b/lib/ui/fragment/document_validation_fragment.dart @@ -9,11 +9,14 @@ import '../../bloc/document_validation_cubit.dart'; import '../../di.dart'; import '../widgets/document_validation_strip.dart'; +typedef _ValidationResult + = DocumentValidationResponseBody$Signatures$ItemValidationResult; + /// Executes Document validation and displays output in [DocumentValidationStrip]. /// /// Uses [DocumentValidationCubit]. class DocumentValidationFragment extends StatelessWidget { - // TODO Consider migrating to to stateful widget becasue of Cubit + // TODO Consider migrating to to stateful widget because of Cubit final String documentId; final ValueSetter onShowDocumentValidationInfoRequested; @@ -63,18 +66,13 @@ class DocumentValidationFragment extends StatelessWidget { } } +/// A set of extensions on [DocumentValidationResponseBody]. extension _DocumentValidationResponseBodyExtensions on DocumentValidationResponseBody { int? get validSignaturesCount => signatures - ?.where((s) => - s.validationResult == - DocumentValidationResponseBody$Signatures$ItemValidationResult - .totalPassed) + ?.where((s) => s.validationResult == _ValidationResult.totalPassed) .length; int? get invalidSignaturesCount => signatures - ?.where((s) => - s.validationResult != - DocumentValidationResponseBody$Signatures$ItemValidationResult - .totalPassed) + ?.where((s) => s.validationResult != _ValidationResult.totalPassed) .length; } diff --git a/lib/ui/widgets/document_validation_info.dart b/lib/ui/widgets/document_validation_info.dart index 1aec4b8..1f13604 100644 --- a/lib/ui/widgets/document_validation_info.dart +++ b/lib/ui/widgets/document_validation_info.dart @@ -9,7 +9,7 @@ import '../widgets/chip.dart' as avm; typedef _ValidationResult = DocumentValidationResponseBody$Signatures$ItemValidationResult; -/// Displays Document validation info based on [data] provided. +/// Displays Document validation info based on [validationResult] provided. class DocumentValidationInfo extends StatelessWidget { final String subject; final DocumentValidationResponseBody$Signatures$ItemValidationResult diff --git a/lib/ui/widgets/document_validation_strip.dart b/lib/ui/widgets/document_validation_strip.dart index 05a947e..2f83e9b 100644 --- a/lib/ui/widgets/document_validation_strip.dart +++ b/lib/ui/widgets/document_validation_strip.dart @@ -6,8 +6,12 @@ import '../../strings_context.dart'; import 'loading_indicator.dart'; import 'markdown_text.dart'; -/// Presents state of document validation - displays text and potentionally -/// loading indicator on the left or arrow on the right when has any signatures. +/// Presents state of document validation - displays text and potentially +/// loading indicator on the left and on the right one of the: +/// - arrow on the right when has any signatures +/// - × when has no signatures +/// +/// [onTap] is invoked when taped on whole widget. class DocumentValidationStrip extends StatelessWidget { final DocumentValidationStripValue value; final VoidCallback? onTap; @@ -43,26 +47,27 @@ class DocumentValidationStrip extends StatelessWidget { }; const foregroundColor = Colors.white; + final children = [ + if (isLoading) const LoadingIndicator(size: 16), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 6), + child: MarkdownText(text, textColor: foregroundColor), + ), + ), + Icon( + (hasSignatures ? Icons.arrow_right_alt_outlined : Icons.close), + color: foregroundColor, + ) + ]; + return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 4), color: backgroundColor, child: Row( - children: [ - if (isLoading) const LoadingIndicator(size: 16), - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 6), - child: MarkdownText(text, textColor: foregroundColor), - ), - ), - if (hasSignatures) - const Icon( - Icons.arrow_right_alt_outlined, - color: foregroundColor, - ) - ], + children: children, ), ), ); From 2a2efa02c59e1dc7901c52d0e93a17a640101f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 11:40:59 +0100 Subject: [PATCH 15/34] Update paddings --- lib/ui/screens/preview_document_screen.dart | 25 +++++++++++---------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/ui/screens/preview_document_screen.dart b/lib/ui/screens/preview_document_screen.dart index c20400d..0122011 100644 --- a/lib/ui/screens/preview_document_screen.dart +++ b/lib/ui/screens/preview_document_screen.dart @@ -139,7 +139,6 @@ class _Body extends StatelessWidget { @override Widget build(BuildContext context) { - // TODO Fix margins according to Designs https://www.figma.com/design/9i8kwShc6o8Urp2lYoPg6M/Autogram-v-mobile-(WIP)?node-id=742-718&node-type=FRAME&t=tOi5uDxBr4g6AUTW-0 final child1 = DocumentValidationFragment( documentId: documentId, onShowDocumentValidationInfoRequested: (data) { @@ -166,13 +165,12 @@ class _Body extends StatelessWidget { ], ); - return Padding( - padding: kScreenMargin, - child: child, - ); + return child; } } +/// Displays [visualization] inside [DocumentVisualization] with +/// primary button below. class _SuccessContent extends StatelessWidget { final DocumentVisualizationResponseBody visualization; final VoidCallback? onSignRequested; @@ -191,12 +189,12 @@ class _SuccessContent extends StatelessWidget { // Document preview Expanded( child: Padding( - padding: const EdgeInsets.only(bottom: 16), + padding: const EdgeInsets.all(2), child: DottedBorder( color: dashColor, strokeWidth: 4, dashPattern: const [16, 16], - padding: const EdgeInsets.all(2), + padding: const EdgeInsets.all(0), child: DocumentVisualization( visualization: visualization, ), @@ -205,12 +203,15 @@ class _SuccessContent extends StatelessWidget { ), // Primary button - FilledButton( - style: FilledButton.styleFrom( - minimumSize: kPrimaryButtonMinimumSize, + Padding( + padding: kScreenMargin, + child: FilledButton( + style: FilledButton.styleFrom( + minimumSize: kPrimaryButtonMinimumSize, + ), + onPressed: onSignRequested, + child: Text(context.strings.buttonSignLabel), ), - onPressed: onSignRequested, - child: Text(context.strings.buttonSignLabel), ), ], ); From b89517309293f23455ee0489425222b57521255c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 12:08:35 +0100 Subject: [PATCH 16/34] Extract PreviewDocumentFragment --- .../fragment/preview_document_fragment.dart | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 lib/ui/fragment/preview_document_fragment.dart diff --git a/lib/ui/fragment/preview_document_fragment.dart b/lib/ui/fragment/preview_document_fragment.dart new file mode 100644 index 0000000..a3a52ee --- /dev/null +++ b/lib/ui/fragment/preview_document_fragment.dart @@ -0,0 +1,102 @@ +import 'package:autogram_sign/autogram_sign.dart' + show DocumentVisualizationResponseBody; +import 'package:dotted_border/dotted_border.dart'; +import 'package:flutter/material.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; + +import '../../bloc/preview_document_cubit.dart'; +import '../../strings_context.dart'; +import '../widgets/document_visualization.dart'; +import '../widgets/error_content.dart'; +import '../widgets/loading_content.dart'; + +/// Displays Document preview based on the [state]. +/// +/// See [PreviewDocumentCubit]. +class PreviewDocumentFragment extends StatelessWidget { + final PreviewDocumentState state; + + const PreviewDocumentFragment({ + super.key, + required this.state, + }); + + @override + Widget build(BuildContext context) { + return switch (state) { + PreviewDocumentInitialState _ => const LoadingContent(), + PreviewDocumentLoadingState _ => const LoadingContent(), + PreviewDocumentSuccessState state => _SuccessContent( + visualization: state.visualization, + ), + PreviewDocumentErrorState state => ErrorContent( + title: context.strings.previewDocumentErrorHeading, + error: state.error, + ), + }; + } +} + +/// Displays [visualization] using [DocumentVisualization] with dotted border. +class _SuccessContent extends StatelessWidget { + final DocumentVisualizationResponseBody visualization; + + const _SuccessContent({required this.visualization}); + + @override + Widget build(BuildContext context) { + final dashColor = Theme.of(context).colorScheme.primary; + + return Padding( + padding: const EdgeInsets.all(2), + child: DottedBorder( + color: dashColor, + strokeWidth: 4, + dashPattern: const [16, 16], + padding: EdgeInsets.zero, + child: DocumentVisualization( + visualization: visualization, + ), + ), + ); + } +} + +@widgetbook.UseCase( + path: '[Fragments]', + name: 'loading', + type: PreviewDocumentFragment, +) +Widget previewLoadingPreviewDocumentFragment(BuildContext context) { + return const PreviewDocumentFragment( + state: PreviewDocumentLoadingState(), + ); +} + +@widgetbook.UseCase( + path: '[Fragments]', + name: 'error', + type: PreviewDocumentFragment, +) +Widget previewErrorPreviewDocumentScreen(BuildContext context) { + return const PreviewDocumentFragment( + state: PreviewDocumentErrorState("Error message!"), + ); +} + +@widgetbook.UseCase( + path: '[Fragments]', + name: 'success', + type: PreviewDocumentFragment, +) +Widget previewSuccessPreviewDocumentScreen(BuildContext context) { + return const PreviewDocumentFragment( + state: PreviewDocumentSuccessState( + DocumentVisualizationResponseBody( + mimeType: "text/plain;base64", + filename: "sample.txt", + content: "", + ), + ), + ); +} From 7e32ed70436bcac43be4df55bc3f8097bf3f3d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 13:01:49 +0100 Subject: [PATCH 17/34] Screen uses two separate fragments with its onw lifecycle --- lib/bloc/document_validation_cubit.dart | 9 +- lib/bloc/preview_document_cubit.dart | 2 +- lib/ui/screens/preview_document_screen.dart | 193 +++++------------- lib/ui/widgets/document_validation_strip.dart | 7 +- lib/widgetbook_app.directories.g.dart | 94 ++++----- 5 files changed, 107 insertions(+), 198 deletions(-) diff --git a/lib/bloc/document_validation_cubit.dart b/lib/bloc/document_validation_cubit.dart index da78104..bec65fb 100644 --- a/lib/bloc/document_validation_cubit.dart +++ b/lib/bloc/document_validation_cubit.dart @@ -33,16 +33,19 @@ class DocumentValidationCubit extends Cubit { try { final response = await _service.getDocumentValidation(documentId); - _log.info("Got Document validation: $response."); + _log.info( + "Got Document validation: (signatureForm: ${response.signatureForm?.value}, signatures: ${response.signatures?.map((e) => e.validationResult.value)})."); emit(DocumentValidationSuccessState(response)); } catch (error, stackTrace) { - _log.severe("Error validating Document.", error, stackTrace); - if (error is ServiceException && error.errorCode == "DOCUMENT_NOT_SIGNED") { + _log.info("Cannot validate unsigned Document."); + emit(const DocumentValidationNotSignedState()); } else { + _log.severe("Error validating Document.", error, stackTrace); + emit(DocumentValidationErrorState(error)); } } diff --git a/lib/bloc/preview_document_cubit.dart b/lib/bloc/preview_document_cubit.dart index fe01598..51dacf1 100644 --- a/lib/bloc/preview_document_cubit.dart +++ b/lib/bloc/preview_document_cubit.dart @@ -31,7 +31,7 @@ class PreviewDocumentCubit extends Cubit { emit(state.toLoading()); try { - _log.info("Getting Document Visualisation for DocumentId: $documentId"); + _log.info("Getting Document Visualisation for Document Id: $documentId"); final visualization = await _service.getDocumentVisualization(documentId); diff --git a/lib/ui/screens/preview_document_screen.dart b/lib/ui/screens/preview_document_screen.dart index 0122011..8328e34 100644 --- a/lib/ui/screens/preview_document_screen.dart +++ b/lib/ui/screens/preview_document_screen.dart @@ -1,13 +1,11 @@ import 'dart:io' show File; import 'package:autogram_sign/autogram_sign.dart' - show DocumentValidationResponseBody, DocumentVisualizationResponseBody; -import 'package:dotted_border/dotted_border.dart'; + show DocumentValidationResponseBody; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get_it/get_it.dart'; import 'package:share_plus/share_plus.dart'; -import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; import '../../bloc/preview_document_cubit.dart'; import '../../data/document_signing_type.dart'; @@ -16,16 +14,18 @@ import '../../strings_context.dart'; import '../app_theme.dart'; import '../fragment/document_validation_fragment.dart'; import '../fragment/document_validation_info_fragment.dart'; -import '../widgets/document_visualization.dart'; -import '../widgets/error_content.dart'; -import '../widgets/loading_content.dart'; +import '../fragment/preview_document_fragment.dart'; import 'open_document_screen.dart'; import 'select_certificate_screen.dart'; /// Screen for previewing single document from [file] and [documentId]. /// +/// Contains two fragments: +/// - [DocumentValidationFragment] +/// - [PreviewDocumentFragment] +/// /// Uses [PreviewDocumentCubit]. -/// TODO Also update docs - mention fragments +/// /// Navigates next to [SelectCertificateScreen]. /// Shows [DocumentValidationInfoFragment]. /// @@ -43,7 +43,13 @@ class PreviewDocumentScreen extends StatelessWidget { @override Widget build(BuildContext context) { - final body = BlocProvider( + final child1 = DocumentValidationFragment( + documentId: documentId, + onShowDocumentValidationInfoRequested: (data) { + _onShowDocumentValidationInfoRequested(context, data); + }, + ); + final child2 = BlocProvider( create: (context) { return GetIt.instance.get( param1: documentId, @@ -51,20 +57,21 @@ class PreviewDocumentScreen extends StatelessWidget { }, child: BlocBuilder( builder: (context, state) { - return _Body( - documentId: documentId, + return _Content( state: state, - onSignRequested: () { - _onSignRequested(context); - }, - onShowDocumentValidationInfoRequested: (data) { - _onShowDocumentValidationInfoRequested(context, data); - }, + onSignRequested: () => _onSignRequested(context), ); }, ), ); + final body = Column( + children: [ + child1, + Expanded(child: child2), + ], + ); + return Scaffold( appBar: AppBar( title: Text(context.strings.previewDocumentTitle), @@ -83,6 +90,19 @@ class PreviewDocumentScreen extends StatelessWidget { ); } + Future _onShowDocumentValidationInfoRequested( + BuildContext context, + DocumentValidationResponseBody data, + ) { + // TODO Call avm.showModalBottomSheet or full screen popup + return showModalBottomSheet( + context: context, + builder: (_) { + return DocumentValidationInfoFragment(data: data); + }, + ); + } + Future _onShareRequested(BuildContext context) async { final file = this.file; @@ -107,155 +127,40 @@ class PreviewDocumentScreen extends StatelessWidget { return Navigator.of(context).push(route); } - - Future _onShowDocumentValidationInfoRequested( - BuildContext context, - DocumentValidationResponseBody data, - ) { - // TODO Call avm.showModalBottomSheet - return showModalBottomSheet( - context: context, - builder: (_) { - return DocumentValidationInfoFragment(data: data); - }, - ); - } } -/// [PreviewDocumentScreen] body. -class _Body extends StatelessWidget { - final String documentId; +/// [PreviewDocumentScreen] content. +class _Content extends StatelessWidget { final PreviewDocumentState state; final VoidCallback? onSignRequested; - final ValueSetter? - onShowDocumentValidationInfoRequested; - const _Body({ - this.documentId = '', + const _Content({ required this.state, required this.onSignRequested, - this.onShowDocumentValidationInfoRequested, }); @override Widget build(BuildContext context) { - final child1 = DocumentValidationFragment( - documentId: documentId, - onShowDocumentValidationInfoRequested: (data) { - onShowDocumentValidationInfoRequested?.call(data); - }, - ); - // TODO Extract whole child2 as Fragment, so it can have separate preview - final child2 = switch (state) { - PreviewDocumentInitialState _ => const LoadingContent(), - PreviewDocumentLoadingState _ => const LoadingContent(), - PreviewDocumentSuccessState state => _SuccessContent( - visualization: state.visualization, - onSignRequested: onSignRequested, - ), - PreviewDocumentErrorState state => ErrorContent( - title: context.strings.previewDocumentErrorHeading, - error: state.error, - ), - }; - final child = Column( - children: [ - child1, - Expanded(child: child2), - ], - ); - - return child; - } -} - -/// Displays [visualization] inside [DocumentVisualization] with -/// primary button below. -class _SuccessContent extends StatelessWidget { - final DocumentVisualizationResponseBody visualization; - final VoidCallback? onSignRequested; - - const _SuccessContent({ - required this.visualization, - required this.onSignRequested, - }); - - @override - Widget build(BuildContext context) { - final dashColor = Theme.of(context).colorScheme.primary; - return Column( children: [ // Document preview Expanded( - child: Padding( - padding: const EdgeInsets.all(2), - child: DottedBorder( - color: dashColor, - strokeWidth: 4, - dashPattern: const [16, 16], - padding: const EdgeInsets.all(0), - child: DocumentVisualization( - visualization: visualization, - ), - ), - ), + child: PreviewDocumentFragment(state: state), ), // Primary button - Padding( - padding: kScreenMargin, - child: FilledButton( - style: FilledButton.styleFrom( - minimumSize: kPrimaryButtonMinimumSize, + if (state is PreviewDocumentSuccessState) + Padding( + padding: kScreenMargin, + child: FilledButton( + style: FilledButton.styleFrom( + minimumSize: kPrimaryButtonMinimumSize, + ), + onPressed: () => onSignRequested?.call(), + child: Text(context.strings.buttonSignLabel), ), - onPressed: onSignRequested, - child: Text(context.strings.buttonSignLabel), ), - ), ], ); } } - -@widgetbook.UseCase( - path: '[Screens]', - name: 'loading', - type: PreviewDocumentScreen, -) -Widget previewLoadingPreviewDocumentScreen(BuildContext context) { - return const _Body( - state: PreviewDocumentLoadingState(), - onSignRequested: null, - ); -} - -@widgetbook.UseCase( - path: '[Screens]', - name: 'error', - type: PreviewDocumentScreen, -) -Widget previewErrorPreviewDocumentScreen(BuildContext context) { - return const _Body( - state: PreviewDocumentErrorState("Error message!"), - onSignRequested: null, - ); -} - -@widgetbook.UseCase( - path: '[Screens]', - name: 'success', - type: PreviewDocumentScreen, -) -Widget previewSuccessPreviewDocumentScreen(BuildContext context) { - return const _Body( - state: PreviewDocumentSuccessState( - DocumentVisualizationResponseBody( - mimeType: "text/plain;base64", - filename: "sample.txt", - content: "", - ), - ), - onSignRequested: null, - ); -} diff --git a/lib/ui/widgets/document_validation_strip.dart b/lib/ui/widgets/document_validation_strip.dart index 2f83e9b..4f409bb 100644 --- a/lib/ui/widgets/document_validation_strip.dart +++ b/lib/ui/widgets/document_validation_strip.dart @@ -6,12 +6,12 @@ import '../../strings_context.dart'; import 'loading_indicator.dart'; import 'markdown_text.dart'; -/// Presents state of document validation - displays text and potentially +/// Presents state of Document validation - displays text and potentially /// loading indicator on the left and on the right one of the: /// - arrow on the right when has any signatures /// - × when has no signatures /// -/// [onTap] is invoked when taped on whole widget. +/// [onTap] is called when taped on whole widget. class DocumentValidationStrip extends StatelessWidget { final DocumentValidationStripValue value; final VoidCallback? onTap; @@ -46,6 +46,7 @@ class DocumentValidationStrip extends StatelessWidget { (_, _, _) => Colors.transparent, }; const foregroundColor = Colors.white; + final icon = (hasSignatures ? Icons.arrow_right_alt_outlined : Icons.close); final children = [ if (isLoading) const LoadingIndicator(size: 16), @@ -56,7 +57,7 @@ class DocumentValidationStrip extends StatelessWidget { ), ), Icon( - (hasSignatures ? Icons.arrow_right_alt_outlined : Icons.close), + icon, color: foregroundColor, ) ]; diff --git a/lib/widgetbook_app.directories.g.dart b/lib/widgetbook_app.directories.g.dart index 919ba4d..3ab8f90 100644 --- a/lib/widgetbook_app.directories.g.dart +++ b/lib/widgetbook_app.directories.g.dart @@ -13,20 +13,20 @@ import 'package:autogram/ui/app_theme.dart' as _i3; import 'package:autogram/ui/assets.dart' as _i4; import 'package:autogram/ui/fragment/document_validation_info_fragment.dart' as _i25; -import 'package:autogram/ui/fragment/show_web_page_fragment.dart' as _i26; -import 'package:autogram/ui/screens/about_screen.dart' as _i29; -import 'package:autogram/ui/screens/main_menu_screen.dart' as _i30; +import 'package:autogram/ui/fragment/preview_document_fragment.dart' as _i26; +import 'package:autogram/ui/fragment/show_web_page_fragment.dart' as _i27; +import 'package:autogram/ui/screens/about_screen.dart' as _i30; +import 'package:autogram/ui/screens/main_menu_screen.dart' as _i31; import 'package:autogram/ui/screens/main_screen.dart' as _i2; import 'package:autogram/ui/screens/onboarding_accept_document_screen.dart' - as _i31; -import 'package:autogram/ui/screens/onboarding_finished_screen.dart' as _i32; + as _i32; +import 'package:autogram/ui/screens/onboarding_finished_screen.dart' as _i33; import 'package:autogram/ui/screens/onboarding_select_signing_certificate_screen.dart' - as _i33; -import 'package:autogram/ui/screens/open_document_screen.dart' as _i34; -import 'package:autogram/ui/screens/paired_device_list_screen.dart' as _i35; + as _i34; +import 'package:autogram/ui/screens/open_document_screen.dart' as _i35; +import 'package:autogram/ui/screens/paired_device_list_screen.dart' as _i36; import 'package:autogram/ui/screens/present_signed_document_screen.dart' - as _i36; -import 'package:autogram/ui/screens/preview_document_screen.dart' as _i37; + as _i37; import 'package:autogram/ui/screens/qr_code_scanner_screen.dart' as _i23; import 'package:autogram/ui/screens/select_certificate_screen.dart' as _i38; import 'package:autogram/ui/screens/settings_screen.dart' as _i39; @@ -37,7 +37,7 @@ import 'package:autogram/ui/screens/start_remote_document_signing_screen.dart' import 'package:autogram/ui/widgets/app_version_text.dart' as _i10; import 'package:autogram/ui/widgets/autogram_logo.dart' as _i5; import 'package:autogram/ui/widgets/buttons.dart' as _i6; -import 'package:autogram/ui/widgets/certificate_picker.dart' as _i27; +import 'package:autogram/ui/widgets/certificate_picker.dart' as _i28; import 'package:autogram/ui/widgets/chip.dart' as _i7; import 'package:autogram/ui/widgets/close_button.dart' as _i8; import 'package:autogram/ui/widgets/dialogs.dart' as _i24; @@ -53,7 +53,7 @@ import 'package:autogram/ui/widgets/option_picker.dart' as _i18; import 'package:autogram/ui/widgets/preference_tile.dart' as _i19; import 'package:autogram/ui/widgets/result_view.dart' as _i20; import 'package:autogram/ui/widgets/retry_view.dart' as _i21; -import 'package:autogram/ui/widgets/signature_type_picker.dart' as _i28; +import 'package:autogram/ui/widgets/signature_type_picker.dart' as _i29; import 'package:autogram/ui/widgets/step_indicator.dart' as _i22; import 'package:widgetbook/widgetbook.dart' as _i1; @@ -303,11 +303,28 @@ final directories = <_i1.WidgetbookNode>[ builder: _i25.previewDocumentValidationInfoFragment, ), ), + _i1.WidgetbookComponent( + name: 'PreviewDocumentFragment', + useCases: [ + _i1.WidgetbookUseCase( + name: 'error', + builder: _i26.previewErrorPreviewDocumentScreen, + ), + _i1.WidgetbookUseCase( + name: 'loading', + builder: _i26.previewLoadingPreviewDocumentFragment, + ), + _i1.WidgetbookUseCase( + name: 'success', + builder: _i26.previewSuccessPreviewDocumentScreen, + ), + ], + ), _i1.WidgetbookLeafComponent( name: 'ShowWebPageFragment', useCase: _i1.WidgetbookUseCase( name: 'ShowWebPageFragment', - builder: _i26.previewShowWebPageFragment, + builder: _i27.previewShowWebPageFragment, ), ), ], @@ -319,14 +336,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'CertificatePicker', useCase: _i1.WidgetbookUseCase( name: 'CertificatePicker', - builder: _i27.previewCertificatePicker, + builder: _i28.previewCertificatePicker, ), ), _i1.WidgetbookLeafComponent( name: 'SignatureTypePicker', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i28.previewSignatureTypePicker, + builder: _i29.previewSignatureTypePicker, ), ), ], @@ -338,14 +355,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'AboutScreen', useCase: _i1.WidgetbookUseCase( name: 'AboutScreen', - builder: _i29.previewAboutScreen, + builder: _i30.previewAboutScreen, ), ), _i1.WidgetbookLeafComponent( name: 'MainMenuScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i30.previewMainMenuScreen, + builder: _i31.previewMainMenuScreen, ), ), _i1.WidgetbookLeafComponent( @@ -359,14 +376,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'OnboardingAcceptDocumentScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i31.previewOnboardingAcceptDocumentScreen, + builder: _i32.previewOnboardingAcceptDocumentScreen, ), ), _i1.WidgetbookLeafComponent( name: 'OnboardingFinishedScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i32.previewOnboardingFinishedScreen, + builder: _i33.previewOnboardingFinishedScreen, ), ), _i1.WidgetbookComponent( @@ -374,20 +391,20 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'canceled', - builder: _i33.previewCanceledOnboardingSelectSigningCertificateBody, + builder: _i34.previewCanceledOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'initial', - builder: _i33.previewInitialOnboardingSelectSigningCertificateBody, + builder: _i34.previewInitialOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'no certificate', builder: - _i33.previewNoCertificateOnboardingSelectSigningCertificateBody, + _i34.previewNoCertificateOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i33.previewSuccessOnboardingSelectSigningCertificateBody, + builder: _i34.previewSuccessOnboardingSelectSigningCertificateBody, ), ], ), @@ -396,11 +413,11 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i34.previewErrorOpenDocumentScreen, + builder: _i35.previewErrorOpenDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i34.previewLoadingOpenDocumentScreen, + builder: _i35.previewLoadingOpenDocumentScreen, ), ], ), @@ -408,7 +425,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'PairedDeviceListScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i35.previewPairedDeviceListScreen, + builder: _i36.previewPairedDeviceListScreen, ), ), _i1.WidgetbookComponent( @@ -416,36 +433,19 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i36.previewErrorPresentSignedDocumentScreen, + builder: _i37.previewErrorPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'initial', - builder: _i36.previewInitialPresentSignedDocumentScreen, - ), - _i1.WidgetbookUseCase( - name: 'loading', - builder: _i36.previewLoadingPresentSignedDocumentScreen, - ), - _i1.WidgetbookUseCase( - name: 'success', - builder: _i36.previewSuccessPresentSignedDocumentScreen, - ), - ], - ), - _i1.WidgetbookComponent( - name: 'PreviewDocumentScreen', - useCases: [ - _i1.WidgetbookUseCase( - name: 'error', - builder: _i37.previewErrorPreviewDocumentScreen, + builder: _i37.previewInitialPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i37.previewLoadingPreviewDocumentScreen, + builder: _i37.previewLoadingPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i37.previewSuccessPreviewDocumentScreen, + builder: _i37.previewSuccessPresentSignedDocumentScreen, ), ], ), From 553587dfd2716a3beaf83a70037b67647d750ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 14:21:37 +0100 Subject: [PATCH 18/34] Include new feature in main screen body --- lib/l10n/app_localizations.dart | 2 +- lib/l10n/app_localizations_sk.dart | 2 +- lib/l10n/app_sk.arb | 2 +- lib/ui/screens/main_screen.dart | 5 ++++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 08ed39e..3b7332e 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -376,7 +376,7 @@ abstract class AppLocalizations { /// No description provided for @introBody. /// /// In sk, this message translates to: - /// **'Začnite výberom dokumentu na:\n ✅ Jednoduché podpisovanie'** + /// **'Začnite výberom dokumentu na:\n ✅ Jednoduché podpisovanie\n ✅ Rýchle overenie podpisov'** String get introBody; /// No description provided for @onboardingFinishedHeading. diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index 2c33498..f2d31df 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -194,7 +194,7 @@ class AppLocalizationsSk extends AppLocalizations { String get introHeading => 'Nový, lepší a krajší podpisovač v mobile'; @override - String get introBody => 'Začnite výberom dokumentu na:\n ✅ Jednoduché podpisovanie'; + String get introBody => 'Začnite výberom dokumentu na:\n ✅ Jednoduché podpisovanie\n ✅ Rýchle overenie podpisov'; @override String get onboardingFinishedHeading => 'Autogram je pripravený'; diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index 2eb2849..e182715 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -53,7 +53,7 @@ "thirdPartyLicensesLabel": "Licencie knižníc tretích strán", "introHeading": "Nový, lepší a krajší podpisovač v mobile", - "introBody": "Začnite výberom dokumentu na:\n ✅ Jednoduché podpisovanie", + "introBody": "Začnite výberom dokumentu na:\n ✅ Jednoduché podpisovanie\n ✅ Rýchle overenie podpisov", "onboardingFinishedHeading": "Autogram je pripravený", "onboardingFinishedBody": "Začnite výberom dokumentu na:\n ✅ Jednoduché podpisovanie", diff --git a/lib/ui/screens/main_screen.dart b/lib/ui/screens/main_screen.dart index 1c85709..91da749 100644 --- a/lib/ui/screens/main_screen.dart +++ b/lib/ui/screens/main_screen.dart @@ -292,7 +292,10 @@ class _Body extends StatelessWidget { style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), const SizedBox(height: 24), - Text(strings.introBody), + Text( + strings.introBody, + style: const TextStyle(height: 1.75), + ), const Spacer(), // Primary button From 603a119832391782d2667ea17d9cee1ff67dd403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 15:02:14 +0100 Subject: [PATCH 19/34] Impl strip hiding on tap --- .../document_validation_fragment.dart | 30 +++++++++++++++---- lib/ui/widgets/document_validation_strip.dart | 12 ++++---- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/lib/ui/fragment/document_validation_fragment.dart b/lib/ui/fragment/document_validation_fragment.dart index 7e8adfb..5a80193 100644 --- a/lib/ui/fragment/document_validation_fragment.dart +++ b/lib/ui/fragment/document_validation_fragment.dart @@ -15,8 +15,7 @@ typedef _ValidationResult /// Executes Document validation and displays output in [DocumentValidationStrip]. /// /// Uses [DocumentValidationCubit]. -class DocumentValidationFragment extends StatelessWidget { - // TODO Consider migrating to to stateful widget because of Cubit +class DocumentValidationFragment extends StatefulWidget { final String documentId; final ValueSetter onShowDocumentValidationInfoRequested; @@ -27,12 +26,21 @@ class DocumentValidationFragment extends StatelessWidget { required this.onShowDocumentValidationInfoRequested, }); + @override + State createState() => + _DocumentValidationFragmentState(); +} + +class _DocumentValidationFragmentState + extends State { + bool _hidden = false; + @override Widget build(BuildContext context) { return BlocProvider( create: (context) { return getIt.get() - ..validateDocument(documentId); + ..validateDocument(widget.documentId); }, child: BlocBuilder( builder: _buildContent, @@ -41,6 +49,11 @@ class DocumentValidationFragment extends StatelessWidget { } Widget _buildContent(BuildContext context, DocumentValidationState state) { + if (_hidden) { + // TODO Animate height from 100% to 0 + return const SizedBox.shrink(); + } + return switch (state) { DocumentValidationInitialState _ => const DocumentValidationStrip( value: DocumentValidationStripValue.loading(), @@ -48,8 +61,13 @@ class DocumentValidationFragment extends StatelessWidget { DocumentValidationLoadingState _ => const DocumentValidationStrip( value: DocumentValidationStripValue.loading(), ), - DocumentValidationNotSignedState _ => const DocumentValidationStrip( - value: DocumentValidationStripValue.none(), + DocumentValidationNotSignedState _ => DocumentValidationStrip( + value: const DocumentValidationStripValue.none(), + onTap: () { + setState(() { + _hidden = true; + }); + }, ), DocumentValidationSuccessState state => DocumentValidationStrip( value: DocumentValidationStripValue.value( @@ -57,7 +75,7 @@ class DocumentValidationFragment extends StatelessWidget { invalidCount: state.response.invalidSignaturesCount ?? 0, ), onTap: () { - onShowDocumentValidationInfoRequested.call(state.response); + widget.onShowDocumentValidationInfoRequested.call(state.response); }, ), DocumentValidationErrorState _ => const SizedBox.shrink(), diff --git a/lib/ui/widgets/document_validation_strip.dart b/lib/ui/widgets/document_validation_strip.dart index 4f409bb..cf9b172 100644 --- a/lib/ui/widgets/document_validation_strip.dart +++ b/lib/ui/widgets/document_validation_strip.dart @@ -59,6 +59,7 @@ class DocumentValidationStrip extends StatelessWidget { Icon( icon, color: foregroundColor, + semanticLabel: null, ) ]; @@ -80,21 +81,22 @@ class DocumentValidationStripValue { final bool isLoading; final int validCount; final int invalidCount; + final Object? error; const DocumentValidationStripValue.loading() : isLoading = true, validCount = 0, - invalidCount = 0; + invalidCount = 0, + error = null; const DocumentValidationStripValue.value({ required this.validCount, required this.invalidCount, - }) : isLoading = false; + }) : isLoading = false, + error = null; const DocumentValidationStripValue.none() - : validCount = 0, - invalidCount = 0, - isLoading = false; + : this.value(validCount: 0, invalidCount: 0); } @widgetbook.UseCase( From 22f848c8b1b62018f7c80cb0ecc1de43aecb077a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 15:30:17 +0100 Subject: [PATCH 20/34] Update DocumentValidationInfo label --- .../document_validation_info_fragment.dart | 32 +++--- lib/ui/widgets/document_validation_info.dart | 106 +++++++++++++----- 2 files changed, 95 insertions(+), 43 deletions(-) diff --git a/lib/ui/fragment/document_validation_info_fragment.dart b/lib/ui/fragment/document_validation_info_fragment.dart index 414ecc9..daba790 100644 --- a/lib/ui/fragment/document_validation_info_fragment.dart +++ b/lib/ui/fragment/document_validation_info_fragment.dart @@ -17,8 +17,6 @@ class DocumentValidationInfoFragment extends StatelessWidget { @override Widget build(BuildContext context) { - final signatures = data.signatures ?? []; - return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -27,22 +25,28 @@ class DocumentValidationInfoFragment extends StatelessWidget { style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), Expanded( - child: ListView.separated( - padding: const EdgeInsets.symmetric(vertical: 8), - itemCount: signatures.length, - itemBuilder: (context, index) { - final signature = signatures[index]; - - return DocumentValidationInfo(signature); - }, - separatorBuilder: (context, _) { - return const Divider(); - }, - ), + child: _buildList(context), ), ], ); } + + Widget _buildList(BuildContext context) { + final signatures = data.signatures ?? []; + + return ListView.separated( + padding: const EdgeInsets.symmetric(vertical: 8), + itemCount: signatures.length, + itemBuilder: (context, index) { + final signature = signatures[index]; + + return DocumentValidationInfo(signature); + }, + separatorBuilder: (context, _) { + return const Divider(); + }, + ); + } } @widgetbook.UseCase( diff --git a/lib/ui/widgets/document_validation_info.dart b/lib/ui/widgets/document_validation_info.dart index 1f13604..2498d4c 100644 --- a/lib/ui/widgets/document_validation_info.dart +++ b/lib/ui/widgets/document_validation_info.dart @@ -1,24 +1,36 @@ import 'package:autogram_sign/autogram_sign.dart'; import 'package:flutter/material.dart'; +import 'package:widgetbook/widgetbook.dart'; import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; import '../../oids.dart'; import '../../utils.dart'; import '../widgets/chip.dart' as avm; +typedef SigningCertificateQualification + = DocumentValidationResponseBody$Signatures$Item$SigningCertificateQualification; typedef _ValidationResult = DocumentValidationResponseBody$Signatures$ItemValidationResult; -/// Displays Document validation info based on [validationResult] provided. +/// Displays Document validation info based on provided [data]. +/// +/// On left side, there is subject; validation result with qualification are +/// on right side. class DocumentValidationInfo extends StatelessWidget { - final String subject; - final DocumentValidationResponseBody$Signatures$ItemValidationResult - validationResult; + final String _subject; + final _ValidationResult _validationResult; + final SigningCertificateQualification _qualification; + final bool _areQualifiedTimestamps; const DocumentValidationInfo._({ - required this.subject, - required this.validationResult, - }); + required String subject, + required _ValidationResult validationResult, + required SigningCertificateQualification qualification, + required bool areQualifiedTimestamps, + }) : _subject = subject, + _validationResult = validationResult, + _qualification = qualification, + _areQualifiedTimestamps = areQualifiedTimestamps; factory DocumentValidationInfo( DocumentValidationResponseBody$Signatures$Item data, @@ -34,48 +46,51 @@ class DocumentValidationInfo extends StatelessWidget { return DocumentValidationInfo._( subject: label, validationResult: data.validationResult, + qualification: data.signingCertificate.qualification, + areQualifiedTimestamps: data.areQualifiedTimestamps, ); } @override Widget build(BuildContext context) { + final label = Text( + _subject, + maxLines: 2, + style: const TextStyle(fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + ); + return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Expanded( - child: Text( - subject, - maxLines: 2, - style: const TextStyle(fontWeight: FontWeight.bold), - overflow: TextOverflow.ellipsis, - ), - ), + Expanded(child: label), + const SizedBox(width: 8), _buildChip(context), ], ); } Widget _buildChip(BuildContext context) { - final icon = switch (validationResult) { + final icon = switch (_validationResult) { _ValidationResult.totalPassed => Icons.check, _ValidationResult.indeterminate => Icons.warning_amber_outlined, _ValidationResult.totalFailed => Icons.error_outline, _ => Icons.question_mark_outlined, }; - final foreground = switch (validationResult) { + final foreground = switch (_validationResult) { _ValidationResult.totalPassed => const Color(0xFF033608), _ValidationResult.indeterminate => const Color(0xFF4E2A00), _ValidationResult.totalFailed => const Color(0xFF4E0711), _ => Colors.black, }; - final background = switch (validationResult) { + final background = switch (_validationResult) { _ValidationResult.totalPassed => const Color(0xFFEDF5F3), _ValidationResult.indeterminate => const Color(0xFFF4F4EC), _ValidationResult.totalFailed => const Color(0xFFFBEEF0), _ => Colors.white, }; - final border = switch (validationResult) { + final border = switch (_validationResult) { _ValidationResult.totalPassed => const Color(0xFFA9D9CD), _ValidationResult.indeterminate => const Color(0xFFD5D6A2), _ValidationResult.totalFailed => const Color(0xFFC3112B), @@ -83,13 +98,39 @@ class DocumentValidationInfo extends StatelessWidget { }; return avm.Chip( - label: 'label', + label: _getLabel(), foreground: foreground, background: background, border: border, leading: Icon(icon, color: foreground), ); } + + String _getLabel() { + // TODO Extract labels into ARB + switch (_validationResult) { + case _ValidationResult.totalFailed: + return "Neplatný"; + + case _ValidationResult.indeterminate: + case _ValidationResult.swaggerGeneratedUnknown: + return "Neznámy"; + + case _ValidationResult.totalPassed: + return switch (_qualification) { + SigningCertificateQualification.qesig => (!_areQualifiedTimestamps + ? "Vlastnoručný podpis" + : "Osvedčený podpis"), + SigningCertificateQualification.qeseal => + (_areQualifiedTimestamps ? "Elektronická pečať" : ""), + SigningCertificateQualification.adesigQcQc => + (_areQualifiedTimestamps ? "Uznaný spôsob autorizácie" : ""), + _ => "" + // TODO handle other cases properly + // https://github.com/slovensko-digital/autogram/blob/748d17c4c8d1b14516dba76bbb8f7beaadbc1bf6/src/main/java/digital/slovensko/autogram/ui/gui/SignatureBadgeFactory.java#L116 + }; + } + } } @widgetbook.UseCase( @@ -98,20 +139,27 @@ class DocumentValidationInfo extends StatelessWidget { type: DocumentValidationInfo, ) Widget previewDocumentValidationInfo(BuildContext context) { + final validationResult = _ValidationResult.values.byName(context.knobs.list( + label: "Validation result", + options: _ValidationResult.values.map((e) => e.name).toList(), + )); + final qualification = + SigningCertificateQualification.values.byName(context.knobs.list( + label: "Qualification", + options: SigningCertificateQualification.values.map((e) => e.name).toList(), + )); + final areQualifiedTimestamps = context.knobs.boolean(label: "TS qualified"); + return DocumentValidationInfo( - const DocumentValidationResponseBody$Signatures$Item( - validationResult: - DocumentValidationResponseBody$Signatures$ItemValidationResult - .totalPassed, + DocumentValidationResponseBody$Signatures$Item( + validationResult: validationResult, level: DocumentValidationResponseBody$Signatures$ItemLevel.xadesBaselineLta, claimedSigningTime: "2023-08-01T12:37:47 +0200", bestSigningTime: "2023-08-01T12:37:47 +0200", signingCertificate: DocumentValidationResponseBody$Signatures$Item$SigningCertificate( - qualification: - DocumentValidationResponseBody$Signatures$Item$SigningCertificateQualification - .qeseal, + qualification: qualification, issuerDN: "OID.2.5.4.5=NTRCZ-26439395, O=\"První certifikační autorita, a.s.\", CN=I.CA Qualified CA/RSA 07/2015, C=CZ", subjectDN: @@ -119,8 +167,8 @@ Widget previewDocumentValidationInfo(BuildContext context) { certificateDer: "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", ), - areQualifiedTimestamps: true, - timestamps: [ + areQualifiedTimestamps: areQualifiedTimestamps, + timestamps: const [ DocumentValidationResponseBody$Signatures$Item$Timestamps$Item( qualification: DocumentValidationResponseBody$Signatures$Item$Timestamps$ItemQualification From 3d17d09ce74076e694c57ecbbc0a891d28ac339b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 16:52:22 +0100 Subject: [PATCH 21/34] Update padding --- lib/ui/fragment/document_validation_info_fragment.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/ui/fragment/document_validation_info_fragment.dart b/lib/ui/fragment/document_validation_info_fragment.dart index daba790..cd37c0c 100644 --- a/lib/ui/fragment/document_validation_info_fragment.dart +++ b/lib/ui/fragment/document_validation_info_fragment.dart @@ -32,15 +32,17 @@ class DocumentValidationInfoFragment extends StatelessWidget { } Widget _buildList(BuildContext context) { - final signatures = data.signatures ?? []; + final signatures = (data.signatures ?? []); return ListView.separated( - padding: const EdgeInsets.symmetric(vertical: 8), itemCount: signatures.length, itemBuilder: (context, index) { final signature = signatures[index]; - return DocumentValidationInfo(signature); + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: DocumentValidationInfo(signature), + ); }, separatorBuilder: (context, _) { return const Divider(); From e875cef43d2ed055760a36bc31e7769f299632bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 17:06:05 +0100 Subject: [PATCH 22/34] Using AVM modal bottom sheet for list --- .../fragment/document_validation_info_fragment.dart | 1 + lib/ui/screens/preview_document_screen.dart | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/ui/fragment/document_validation_info_fragment.dart b/lib/ui/fragment/document_validation_info_fragment.dart index cd37c0c..0246eb9 100644 --- a/lib/ui/fragment/document_validation_info_fragment.dart +++ b/lib/ui/fragment/document_validation_info_fragment.dart @@ -24,6 +24,7 @@ class DocumentValidationInfoFragment extends StatelessWidget { context.strings.documentValidationSignaturesHeading, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), + const SizedBox(height: 16), Expanded( child: _buildList(context), ), diff --git a/lib/ui/screens/preview_document_screen.dart b/lib/ui/screens/preview_document_screen.dart index 8328e34..583cd47 100644 --- a/lib/ui/screens/preview_document_screen.dart +++ b/lib/ui/screens/preview_document_screen.dart @@ -15,6 +15,7 @@ import '../app_theme.dart'; import '../fragment/document_validation_fragment.dart'; import '../fragment/document_validation_info_fragment.dart'; import '../fragment/preview_document_fragment.dart'; +import '../widgets/dialogs.dart' as avm; import 'open_document_screen.dart'; import 'select_certificate_screen.dart'; @@ -94,12 +95,12 @@ class PreviewDocumentScreen extends StatelessWidget { BuildContext context, DocumentValidationResponseBody data, ) { - // TODO Call avm.showModalBottomSheet or full screen popup - return showModalBottomSheet( + return avm.showModalBottomSheet( context: context, - builder: (_) { - return DocumentValidationInfoFragment(data: data); - }, + child: SizedBox( + height: 240, + child: DocumentValidationInfoFragment(data: data), + ), ); } From b3f6050e57f3bdc34a3457de052f74023f4f6209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 17:50:20 +0100 Subject: [PATCH 23/34] Prepare CertificateDetails --- lib/l10n/app_localizations.dart | 30 +++ lib/l10n/app_localizations_sk.dart | 15 ++ lib/l10n/app_sk.arb | 5 + lib/oids.dart | 3 + .../screens/certificate_details_dialog.dart | 46 +++++ lib/ui/widgets/certificate_details.dart | 95 ++++++++++ lib/ui/widgets/document_validation_info.dart | 38 ++-- lib/widgetbook_app.directories.g.dart | 176 +++++++++--------- 8 files changed, 309 insertions(+), 99 deletions(-) create mode 100644 lib/ui/screens/certificate_details_dialog.dart create mode 100644 lib/ui/widgets/certificate_details.dart diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 3b7332e..a8cfd11 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -535,6 +535,36 @@ abstract class AppLocalizations { /// **'Vydavateľ: {text}'** String certificateIssuer(Object text); + /// No description provided for @certificateIssuerLabel. + /// + /// In sk, this message translates to: + /// **'Vydavateľ'** + String get certificateIssuerLabel; + + /// No description provided for @certificateSubjectLabel. + /// + /// In sk, this message translates to: + /// **'Vydaný pre'** + String get certificateSubjectLabel; + + /// No description provided for @certificateValidityLabel. + /// + /// In sk, this message translates to: + /// **'Platnosť'** + String get certificateValidityLabel; + + /// No description provided for @certificateValidityNotBeforeLabel. + /// + /// In sk, this message translates to: + /// **'Platný od'** + String get certificateValidityNotBeforeLabel; + + /// No description provided for @certificateValidityNotAfterLabel. + /// + /// In sk, this message translates to: + /// **'Platný do'** + String get certificateValidityNotAfterLabel; + /// No description provided for @certificateNotAfter. /// /// In sk, this message translates to: diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index f2d31df..1383fb4 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -288,6 +288,21 @@ class AppLocalizationsSk extends AppLocalizations { return 'Vydavateľ: $text'; } + @override + String get certificateIssuerLabel => 'Vydavateľ'; + + @override + String get certificateSubjectLabel => 'Vydaný pre'; + + @override + String get certificateValidityLabel => 'Platnosť'; + + @override + String get certificateValidityNotBeforeLabel => 'Platný od'; + + @override + String get certificateValidityNotAfterLabel => 'Platný do'; + @override String certificateNotAfter(Object text) { return 'Platný do: $text'; diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index e182715..bc2f421 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -90,6 +90,11 @@ "selectSigningCertificateErrorHeading": "Chyba pri načítavaní certifikátov z občianskeho preukazu.", "certificateIssuer": "Vydavateľ: {text}", + "certificateIssuerLabel": "Vydavateľ", + "certificateSubjectLabel": "Vydaný pre", + "certificateValidityLabel": "Platnosť", + "certificateValidityNotBeforeLabel": "Platný od", + "certificateValidityNotAfterLabel": "Platný do", "certificateNotAfter": "Platný do: {text}", "signDocumentTitle": "Podpisovanie dokumentu", diff --git a/lib/oids.dart b/lib/oids.dart index 2d4641b..495d15f 100644 --- a/lib/oids.dart +++ b/lib/oids.dart @@ -8,6 +8,9 @@ class X500Oids { /// Organization name (ON). static const on = '2.5.4.10'; + /// Organization unit name (OU). + static const ou = '2.5.4.11'; + /// Serial number (SN). static const sn = '2.5.4.5'; diff --git a/lib/ui/screens/certificate_details_dialog.dart b/lib/ui/screens/certificate_details_dialog.dart new file mode 100644 index 0000000..3b55a8f --- /dev/null +++ b/lib/ui/screens/certificate_details_dialog.dart @@ -0,0 +1,46 @@ +import 'package:basic_utils/basic_utils.dart' show TbsCertificate; +import 'package:flutter/material.dart'; + +import '../app_theme.dart'; +import '../widgets/certificate_details.dart'; + +/// Dialog that contains [CertificateDetails] widget. +class CertificateDetailsDialog extends StatelessWidget { + final TbsCertificate certificate; + + const CertificateDetailsDialog(this.certificate, {super.key}); + + @override + Widget build(BuildContext context) { + final body = SingleChildScrollView( + child: Padding( + padding: kScreenMargin, + child: CertificateDetails(certificate: certificate), + ), + ); + + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + title: null, + actions: const [CloseButton()], + ), + body: SafeArea( + child: body, + ), + ); + } + + static Future show( + BuildContext context, + TbsCertificate certificate, + ) { + return showGeneralDialog( + context: context, + barrierDismissible: false, + pageBuilder: (context, __, ___) { + return CertificateDetailsDialog(certificate); + }, + ); + } +} diff --git a/lib/ui/widgets/certificate_details.dart b/lib/ui/widgets/certificate_details.dart new file mode 100644 index 0000000..554623b --- /dev/null +++ b/lib/ui/widgets/certificate_details.dart @@ -0,0 +1,95 @@ +import 'package:basic_utils/basic_utils.dart' show TbsCertificate; +import 'package:flutter/material.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; + +import '../../oids.dart'; +import '../../strings_context.dart'; +import '../../utils.dart'; + +/// Displays X509 Certificate details: +/// - Issued to (Subject) +/// - Issued by +/// - Validity +class CertificateDetails extends StatelessWidget { + final TbsCertificate certificate; + + const CertificateDetails({super.key, required this.certificate}); + + @override + Widget build(BuildContext context) { + final strings = context.strings; + + final children = [ + _buildHeadline(strings.certificateSubjectLabel), + ..._buildSubjectInfo(certificate.subject), + _buildHeadline(strings.certificateIssuerLabel), + ..._buildSubjectInfo(certificate.issuer), + _buildHeadline(strings.certificateValidityLabel), + _Info(strings.certificateValidityNotBeforeLabel, + certificate.validity.notBefore.toString()), + _Info(strings.certificateValidityNotAfterLabel, + certificate.validity.notAfter.toString()), + ]; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: children, + ); + } + + Widget _buildHeadline(String text) { + return Padding( + padding: const EdgeInsets.only(top: 8, bottom: 2), + child: Text( + text, + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14), + ), + ); + } + + List _buildSubjectInfo(Map data) { + return [ + _Info("Common name (CN)", data[X500Oids.cn]), + _Info("Organization (O)", data[X500Oids.on]), + _Info("Locality name (LN)", data[X500Oids.ln]), + _Info("Country (C)", data[X500Oids.c]), + _Info("Serial number (SN)", data[X500Oids.sn]), + ]; + } +} + +/// Wrapped row with [label] on the left and [value] on the right. +class _Info extends StatelessWidget { + final String label; + final String? value; + + const _Info(this.label, this.value); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 24), + child: Wrap( + spacing: 8, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + SizedBox(width: 160, child: Text(label)), + Text(value ?? ''), + ], + ), + ); + } +} + +@widgetbook.UseCase( + path: '[Core]', + name: 'CertificateDetails', + type: CertificateDetails, +) +Widget previewCertificateDetails(BuildContext context) { + const certDer = + "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"; + final cert = x509CertificateDataFromDer(certDer).tbsCertificate!; + + return CertificateDetails(certificate: cert); +} diff --git a/lib/ui/widgets/document_validation_info.dart b/lib/ui/widgets/document_validation_info.dart index 2498d4c..a95798a 100644 --- a/lib/ui/widgets/document_validation_info.dart +++ b/lib/ui/widgets/document_validation_info.dart @@ -1,10 +1,12 @@ import 'package:autogram_sign/autogram_sign.dart'; +import 'package:basic_utils/basic_utils.dart'; import 'package:flutter/material.dart'; import 'package:widgetbook/widgetbook.dart'; import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; import '../../oids.dart'; import '../../utils.dart'; +import '../screens/certificate_details_dialog.dart'; import '../widgets/chip.dart' as avm; typedef SigningCertificateQualification @@ -17,17 +19,17 @@ typedef _ValidationResult /// On left side, there is subject; validation result with qualification are /// on right side. class DocumentValidationInfo extends StatelessWidget { - final String _subject; + final TbsCertificate _certificate; final _ValidationResult _validationResult; final SigningCertificateQualification _qualification; final bool _areQualifiedTimestamps; const DocumentValidationInfo._({ - required String subject, + required TbsCertificate certificate, required _ValidationResult validationResult, required SigningCertificateQualification qualification, required bool areQualifiedTimestamps, - }) : _subject = subject, + }) : _certificate = certificate, _validationResult = validationResult, _qualification = qualification, _areQualifiedTimestamps = areQualifiedTimestamps; @@ -36,15 +38,10 @@ class DocumentValidationInfo extends StatelessWidget { DocumentValidationResponseBody$Signatures$Item data, ) { final certDer = data.signingCertificate.certificateDer; - final cert = x509CertificateDataFromDer(certDer).tbsCertificate; - final label = [ - cert?.subject[X500Oids.cn], - cert?.subject[X500Oids.ln], - cert?.subject[X500Oids.c], - ].whereType().join(", "); + final certificate = x509CertificateDataFromDer(certDer).tbsCertificate!; return DocumentValidationInfo._( - subject: label, + certificate: certificate, validationResult: data.validationResult, qualification: data.signingCertificate.qualification, areQualifiedTimestamps: data.areQualifiedTimestamps, @@ -53,11 +50,22 @@ class DocumentValidationInfo extends StatelessWidget { @override Widget build(BuildContext context) { - final label = Text( - _subject, - maxLines: 2, - style: const TextStyle(fontWeight: FontWeight.bold), - overflow: TextOverflow.ellipsis, + final subject = [ + _certificate.subject[X500Oids.cn], + _certificate.subject[X500Oids.ln], + _certificate.subject[X500Oids.c], + ].whereType().join(", "); + + final label = InkWell( + onTap: () { + CertificateDetailsDialog.show(context, _certificate); + }, + child: Text( + subject, + style: const TextStyle(fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), ); return Row( diff --git a/lib/widgetbook_app.directories.g.dart b/lib/widgetbook_app.directories.g.dart index 3ab8f90..ecae5bb 100644 --- a/lib/widgetbook_app.directories.g.dart +++ b/lib/widgetbook_app.directories.g.dart @@ -12,49 +12,50 @@ import 'package:autogram/ui/app_theme.dart' as _i3; import 'package:autogram/ui/assets.dart' as _i4; import 'package:autogram/ui/fragment/document_validation_info_fragment.dart' - as _i25; -import 'package:autogram/ui/fragment/preview_document_fragment.dart' as _i26; -import 'package:autogram/ui/fragment/show_web_page_fragment.dart' as _i27; -import 'package:autogram/ui/screens/about_screen.dart' as _i30; -import 'package:autogram/ui/screens/main_menu_screen.dart' as _i31; + as _i26; +import 'package:autogram/ui/fragment/preview_document_fragment.dart' as _i27; +import 'package:autogram/ui/fragment/show_web_page_fragment.dart' as _i28; +import 'package:autogram/ui/screens/about_screen.dart' as _i31; +import 'package:autogram/ui/screens/main_menu_screen.dart' as _i32; import 'package:autogram/ui/screens/main_screen.dart' as _i2; import 'package:autogram/ui/screens/onboarding_accept_document_screen.dart' - as _i32; -import 'package:autogram/ui/screens/onboarding_finished_screen.dart' as _i33; + as _i33; +import 'package:autogram/ui/screens/onboarding_finished_screen.dart' as _i34; import 'package:autogram/ui/screens/onboarding_select_signing_certificate_screen.dart' - as _i34; -import 'package:autogram/ui/screens/open_document_screen.dart' as _i35; -import 'package:autogram/ui/screens/paired_device_list_screen.dart' as _i36; + as _i35; +import 'package:autogram/ui/screens/open_document_screen.dart' as _i36; +import 'package:autogram/ui/screens/paired_device_list_screen.dart' as _i37; import 'package:autogram/ui/screens/present_signed_document_screen.dart' - as _i37; -import 'package:autogram/ui/screens/qr_code_scanner_screen.dart' as _i23; -import 'package:autogram/ui/screens/select_certificate_screen.dart' as _i38; -import 'package:autogram/ui/screens/settings_screen.dart' as _i39; -import 'package:autogram/ui/screens/show_document_screen.dart' as _i40; -import 'package:autogram/ui/screens/sign_document_screen.dart' as _i41; + as _i38; +import 'package:autogram/ui/screens/qr_code_scanner_screen.dart' as _i24; +import 'package:autogram/ui/screens/select_certificate_screen.dart' as _i39; +import 'package:autogram/ui/screens/settings_screen.dart' as _i40; +import 'package:autogram/ui/screens/show_document_screen.dart' as _i41; +import 'package:autogram/ui/screens/sign_document_screen.dart' as _i42; import 'package:autogram/ui/screens/start_remote_document_signing_screen.dart' - as _i42; + as _i43; import 'package:autogram/ui/widgets/app_version_text.dart' as _i10; import 'package:autogram/ui/widgets/autogram_logo.dart' as _i5; import 'package:autogram/ui/widgets/buttons.dart' as _i6; -import 'package:autogram/ui/widgets/certificate_picker.dart' as _i28; +import 'package:autogram/ui/widgets/certificate_details.dart' as _i11; +import 'package:autogram/ui/widgets/certificate_picker.dart' as _i29; import 'package:autogram/ui/widgets/chip.dart' as _i7; import 'package:autogram/ui/widgets/close_button.dart' as _i8; -import 'package:autogram/ui/widgets/dialogs.dart' as _i24; -import 'package:autogram/ui/widgets/document_validation_info.dart' as _i11; -import 'package:autogram/ui/widgets/document_validation_strip.dart' as _i12; -import 'package:autogram/ui/widgets/document_visualization.dart' as _i13; -import 'package:autogram/ui/widgets/error_content.dart' as _i14; -import 'package:autogram/ui/widgets/html_preview.dart' as _i15; -import 'package:autogram/ui/widgets/loading_content.dart' as _i16; +import 'package:autogram/ui/widgets/dialogs.dart' as _i25; +import 'package:autogram/ui/widgets/document_validation_info.dart' as _i12; +import 'package:autogram/ui/widgets/document_validation_strip.dart' as _i13; +import 'package:autogram/ui/widgets/document_visualization.dart' as _i14; +import 'package:autogram/ui/widgets/error_content.dart' as _i15; +import 'package:autogram/ui/widgets/html_preview.dart' as _i16; +import 'package:autogram/ui/widgets/loading_content.dart' as _i17; import 'package:autogram/ui/widgets/loading_indicator.dart' as _i9; -import 'package:autogram/ui/widgets/markdown_text.dart' as _i17; -import 'package:autogram/ui/widgets/option_picker.dart' as _i18; -import 'package:autogram/ui/widgets/preference_tile.dart' as _i19; -import 'package:autogram/ui/widgets/result_view.dart' as _i20; -import 'package:autogram/ui/widgets/retry_view.dart' as _i21; -import 'package:autogram/ui/widgets/signature_type_picker.dart' as _i29; -import 'package:autogram/ui/widgets/step_indicator.dart' as _i22; +import 'package:autogram/ui/widgets/markdown_text.dart' as _i18; +import 'package:autogram/ui/widgets/option_picker.dart' as _i19; +import 'package:autogram/ui/widgets/preference_tile.dart' as _i20; +import 'package:autogram/ui/widgets/result_view.dart' as _i21; +import 'package:autogram/ui/widgets/retry_view.dart' as _i22; +import 'package:autogram/ui/widgets/signature_type_picker.dart' as _i30; +import 'package:autogram/ui/widgets/step_indicator.dart' as _i23; import 'package:widgetbook/widgetbook.dart' as _i1; final directories = <_i1.WidgetbookNode>[ @@ -168,11 +169,18 @@ final directories = <_i1.WidgetbookNode>[ builder: _i10.previewAppVersionText, ), ), + _i1.WidgetbookLeafComponent( + name: 'CertificateDetails', + useCase: _i1.WidgetbookUseCase( + name: 'CertificateDetails', + builder: _i11.previewCertificateDetails, + ), + ), _i1.WidgetbookLeafComponent( name: 'DocumentValidationInfo', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i11.previewDocumentValidationInfo, + builder: _i12.previewDocumentValidationInfo, ), ), _i1.WidgetbookComponent( @@ -180,11 +188,11 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'loading', - builder: _i12.previewLoadingDocumentValidationStrip, + builder: _i13.previewLoadingDocumentValidationStrip, ), _i1.WidgetbookUseCase( name: 'signatures', - builder: _i12.previewOtherDocumentValidationStrip, + builder: _i13.previewOtherDocumentValidationStrip, ), ], ), @@ -192,49 +200,49 @@ final directories = <_i1.WidgetbookNode>[ name: 'DocumentVisualization', useCase: _i1.WidgetbookUseCase( name: 'DocumentVisualization', - builder: _i13.previewDocumentVisualization, + builder: _i14.previewDocumentVisualization, ), ), _i1.WidgetbookLeafComponent( name: 'ErrorContent', useCase: _i1.WidgetbookUseCase( name: 'ErrorContent', - builder: _i14.previewErrorContent, + builder: _i15.previewErrorContent, ), ), _i1.WidgetbookLeafComponent( name: 'HtmlPreview', useCase: _i1.WidgetbookUseCase( name: 'HtmlPreview', - builder: _i15.previewHtmlPreview, + builder: _i16.previewHtmlPreview, ), ), _i1.WidgetbookLeafComponent( name: 'LoadingContent', useCase: _i1.WidgetbookUseCase( name: 'LoadingContent', - builder: _i16.previewLoadingContent, + builder: _i17.previewLoadingContent, ), ), _i1.WidgetbookLeafComponent( name: 'MarkdownText', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i17.previewMarkdownText, + builder: _i18.previewMarkdownText, ), ), _i1.WidgetbookLeafComponent( name: 'OptionPicker', useCase: _i1.WidgetbookUseCase( name: 'OptionPicker', - builder: _i18.previewOptionPicker, + builder: _i19.previewOptionPicker, ), ), _i1.WidgetbookLeafComponent( name: 'PreferenceTile', useCase: _i1.WidgetbookUseCase( name: 'PreferenceTile', - builder: _i19.previewPreferenceTile, + builder: _i20.previewPreferenceTile, ), ), _i1.WidgetbookComponent( @@ -242,19 +250,19 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'custom', - builder: _i20.previewCustomResultView, + builder: _i21.previewCustomResultView, ), _i1.WidgetbookUseCase( name: 'error', - builder: _i20.previewErrorResultView, + builder: _i21.previewErrorResultView, ), _i1.WidgetbookUseCase( name: 'info', - builder: _i20.previewInfoResultView, + builder: _i21.previewInfoResultView, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i20.previewSuccessResultView, + builder: _i21.previewSuccessResultView, ), ], ), @@ -262,21 +270,21 @@ final directories = <_i1.WidgetbookNode>[ name: 'RetryView', useCase: _i1.WidgetbookUseCase( name: 'RetryView', - builder: _i21.previewRetryView, + builder: _i22.previewRetryView, ), ), _i1.WidgetbookLeafComponent( name: 'StepIndicator', useCase: _i1.WidgetbookUseCase( name: 'StepIndicator', - builder: _i22.previewStepIndicator, + builder: _i23.previewStepIndicator, ), ), _i1.WidgetbookLeafComponent( name: '_ViewFinder', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i23.previewViewFinder, + builder: _i24.previewViewFinder, ), ), ], @@ -288,7 +296,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'BottomSheet', useCase: _i1.WidgetbookUseCase( name: 'NotificationsPermissionRationale', - builder: _i24.previewNotificationsPermissionRationaleModal, + builder: _i25.previewNotificationsPermissionRationaleModal, ), ) ], @@ -300,7 +308,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'DocumentValidationInfoFragment', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i25.previewDocumentValidationInfoFragment, + builder: _i26.previewDocumentValidationInfoFragment, ), ), _i1.WidgetbookComponent( @@ -308,15 +316,15 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i26.previewErrorPreviewDocumentScreen, + builder: _i27.previewErrorPreviewDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i26.previewLoadingPreviewDocumentFragment, + builder: _i27.previewLoadingPreviewDocumentFragment, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i26.previewSuccessPreviewDocumentScreen, + builder: _i27.previewSuccessPreviewDocumentScreen, ), ], ), @@ -324,7 +332,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'ShowWebPageFragment', useCase: _i1.WidgetbookUseCase( name: 'ShowWebPageFragment', - builder: _i27.previewShowWebPageFragment, + builder: _i28.previewShowWebPageFragment, ), ), ], @@ -336,14 +344,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'CertificatePicker', useCase: _i1.WidgetbookUseCase( name: 'CertificatePicker', - builder: _i28.previewCertificatePicker, + builder: _i29.previewCertificatePicker, ), ), _i1.WidgetbookLeafComponent( name: 'SignatureTypePicker', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i29.previewSignatureTypePicker, + builder: _i30.previewSignatureTypePicker, ), ), ], @@ -355,14 +363,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'AboutScreen', useCase: _i1.WidgetbookUseCase( name: 'AboutScreen', - builder: _i30.previewAboutScreen, + builder: _i31.previewAboutScreen, ), ), _i1.WidgetbookLeafComponent( name: 'MainMenuScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i31.previewMainMenuScreen, + builder: _i32.previewMainMenuScreen, ), ), _i1.WidgetbookLeafComponent( @@ -376,14 +384,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'OnboardingAcceptDocumentScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i32.previewOnboardingAcceptDocumentScreen, + builder: _i33.previewOnboardingAcceptDocumentScreen, ), ), _i1.WidgetbookLeafComponent( name: 'OnboardingFinishedScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i33.previewOnboardingFinishedScreen, + builder: _i34.previewOnboardingFinishedScreen, ), ), _i1.WidgetbookComponent( @@ -391,20 +399,20 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'canceled', - builder: _i34.previewCanceledOnboardingSelectSigningCertificateBody, + builder: _i35.previewCanceledOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'initial', - builder: _i34.previewInitialOnboardingSelectSigningCertificateBody, + builder: _i35.previewInitialOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'no certificate', builder: - _i34.previewNoCertificateOnboardingSelectSigningCertificateBody, + _i35.previewNoCertificateOnboardingSelectSigningCertificateBody, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i34.previewSuccessOnboardingSelectSigningCertificateBody, + builder: _i35.previewSuccessOnboardingSelectSigningCertificateBody, ), ], ), @@ -413,11 +421,11 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i35.previewErrorOpenDocumentScreen, + builder: _i36.previewErrorOpenDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i35.previewLoadingOpenDocumentScreen, + builder: _i36.previewLoadingOpenDocumentScreen, ), ], ), @@ -425,7 +433,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'PairedDeviceListScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i36.previewPairedDeviceListScreen, + builder: _i37.previewPairedDeviceListScreen, ), ), _i1.WidgetbookComponent( @@ -433,19 +441,19 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i37.previewErrorPresentSignedDocumentScreen, + builder: _i38.previewErrorPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'initial', - builder: _i37.previewInitialPresentSignedDocumentScreen, + builder: _i38.previewInitialPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i37.previewLoadingPresentSignedDocumentScreen, + builder: _i38.previewLoadingPresentSignedDocumentScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i37.previewSuccessPresentSignedDocumentScreen, + builder: _i38.previewSuccessPresentSignedDocumentScreen, ), ], ), @@ -453,7 +461,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'QRCodeScannerScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i23.previewQRCodeScannerScreen, + builder: _i24.previewQRCodeScannerScreen, ), ), _i1.WidgetbookComponent( @@ -461,23 +469,23 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'canceled', - builder: _i38.previewCanceledSelectCertificateScreen, + builder: _i39.previewCanceledSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'error', - builder: _i38.previewErrorSelectCertificateScreen, + builder: _i39.previewErrorSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i38.previewLoadingSelectCertificateScreen, + builder: _i39.previewLoadingSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'no certificate', - builder: _i38.previewNoCertificateSelectCertificateScreen, + builder: _i39.previewNoCertificateSelectCertificateScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i38.previewSuccessSelectCertificateScreen, + builder: _i39.previewSuccessSelectCertificateScreen, ), ], ), @@ -485,14 +493,14 @@ final directories = <_i1.WidgetbookNode>[ name: 'SettingsScreen', useCase: _i1.WidgetbookUseCase( name: 'SettingsScreen', - builder: _i39.previewSettingsScreen, + builder: _i40.previewSettingsScreen, ), ), _i1.WidgetbookLeafComponent( name: 'ShowDocumentScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i40.previewShowDocumentScreen, + builder: _i41.previewShowDocumentScreen, ), ), _i1.WidgetbookComponent( @@ -500,15 +508,15 @@ final directories = <_i1.WidgetbookNode>[ useCases: [ _i1.WidgetbookUseCase( name: 'error', - builder: _i41.previewErrorSignDocumentScreen, + builder: _i42.previewErrorSignDocumentScreen, ), _i1.WidgetbookUseCase( name: 'loading', - builder: _i41.previewLoadingSignDocumentScreen, + builder: _i42.previewLoadingSignDocumentScreen, ), _i1.WidgetbookUseCase( name: 'success', - builder: _i41.previewSuccessSignDocumentScreen, + builder: _i42.previewSuccessSignDocumentScreen, ), ], ), @@ -516,7 +524,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'StartRemoteDocumentSigningScreen', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i42.previewStartRemoteDocumentSigningScreen, + builder: _i43.previewStartRemoteDocumentSigningScreen, ), ), ], From d74892249229b01f286fa624b5926b42cd6b33a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 18:45:57 +0100 Subject: [PATCH 24/34] Displaying only certificate CN in labels --- lib/ui/screens/settings_screen.dart | 6 +----- lib/ui/widgets/certificate_picker_item.dart | 11 ++++------- lib/ui/widgets/document_validation_info.dart | 7 +------ 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/lib/ui/screens/settings_screen.dart b/lib/ui/screens/settings_screen.dart index a1922c8..aaa5033 100644 --- a/lib/ui/screens/settings_screen.dart +++ b/lib/ui/screens/settings_screen.dart @@ -105,11 +105,7 @@ class _Body extends StatelessWidget { final cert = value.tbsCertificate; - return [ - cert.subject[X500Oids.cn], - cert.subject[X500Oids.ln], - cert.subject[X500Oids.c], - ].whereType().join(", "); + return cert.subject[X500Oids.cn]; }(), onPressed: null, ); diff --git a/lib/ui/widgets/certificate_picker_item.dart b/lib/ui/widgets/certificate_picker_item.dart index a6e5c98..8dab274 100644 --- a/lib/ui/widgets/certificate_picker_item.dart +++ b/lib/ui/widgets/certificate_picker_item.dart @@ -31,19 +31,16 @@ class CertificatePickerItem extends StatelessWidget { Widget build(BuildContext context) { final cert = certificate.tbsCertificate; - // NOT using RadioListTile because need to scale-up and style Radio - - final title = [ - cert.subject[X500Oids.cn], - cert.subject[X500Oids.ln], - cert.subject[X500Oids.c], - ].whereType().join(", "); final strings = context.strings; + + final title = cert.subject[X500Oids.cn] ?? ''; final identity = "${cert.subject[X500Oids.sn]}"; final issuer = strings.certificateIssuer("${cert.issuer[X500Oids.cn]}"); final validTo = strings.certificateNotAfter(_dateFormat.format(cert.validity.notAfter)); + // NOT using RadioListTile because need to scale-up and style Radio + return ListTile( onTap: () { onCertificateChanged(certificate); diff --git a/lib/ui/widgets/document_validation_info.dart b/lib/ui/widgets/document_validation_info.dart index a95798a..4c5aa4b 100644 --- a/lib/ui/widgets/document_validation_info.dart +++ b/lib/ui/widgets/document_validation_info.dart @@ -50,12 +50,7 @@ class DocumentValidationInfo extends StatelessWidget { @override Widget build(BuildContext context) { - final subject = [ - _certificate.subject[X500Oids.cn], - _certificate.subject[X500Oids.ln], - _certificate.subject[X500Oids.c], - ].whereType().join(", "); - + final subject = _certificate.subject[X500Oids.cn] ?? ''; final label = InkWell( onTap: () { CertificateDetailsDialog.show(context, _certificate); From 27d3d95ff88cdd58932789a98cb0fb5d22a3ea6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 19:08:40 +0100 Subject: [PATCH 25/34] Rename one widget --- .../document_validation_info_fragment.dart | 6 ++-- ...info.dart => document_signature_info.dart} | 32 ++++++++----------- lib/widgetbook_app.directories.g.dart | 4 +-- 3 files changed, 18 insertions(+), 24 deletions(-) rename lib/ui/widgets/{document_validation_info.dart => document_signature_info.dart} (94%) diff --git a/lib/ui/fragment/document_validation_info_fragment.dart b/lib/ui/fragment/document_validation_info_fragment.dart index 0246eb9..e2c91cc 100644 --- a/lib/ui/fragment/document_validation_info_fragment.dart +++ b/lib/ui/fragment/document_validation_info_fragment.dart @@ -3,10 +3,10 @@ import 'package:flutter/material.dart'; import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; import '../../strings_context.dart'; -import '../widgets/document_validation_info.dart'; +import '../widgets/document_signature_info.dart'; /// Displays Document validation info based on [data] provided -/// as list of [DocumentValidationInfo] for each signature. +/// as list of [DocumentSignatureInfo] for each signature. class DocumentValidationInfoFragment extends StatelessWidget { final DocumentValidationResponseBody data; @@ -42,7 +42,7 @@ class DocumentValidationInfoFragment extends StatelessWidget { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), - child: DocumentValidationInfo(signature), + child: DocumentSignatureInfo(signature), ); }, separatorBuilder: (context, _) { diff --git a/lib/ui/widgets/document_validation_info.dart b/lib/ui/widgets/document_signature_info.dart similarity index 94% rename from lib/ui/widgets/document_validation_info.dart rename to lib/ui/widgets/document_signature_info.dart index 4c5aa4b..4da5fd6 100644 --- a/lib/ui/widgets/document_validation_info.dart +++ b/lib/ui/widgets/document_signature_info.dart @@ -6,7 +6,6 @@ import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; import '../../oids.dart'; import '../../utils.dart'; -import '../screens/certificate_details_dialog.dart'; import '../widgets/chip.dart' as avm; typedef SigningCertificateQualification @@ -14,17 +13,17 @@ typedef SigningCertificateQualification typedef _ValidationResult = DocumentValidationResponseBody$Signatures$ItemValidationResult; -/// Displays Document validation info based on provided [data]. +/// Displays Document validation info based on provided [DocumentValidationResponseBody$Signatures$Item]. /// /// On left side, there is subject; validation result with qualification are /// on right side. -class DocumentValidationInfo extends StatelessWidget { +class DocumentSignatureInfo extends StatelessWidget { final TbsCertificate _certificate; final _ValidationResult _validationResult; final SigningCertificateQualification _qualification; final bool _areQualifiedTimestamps; - const DocumentValidationInfo._({ + const DocumentSignatureInfo._({ required TbsCertificate certificate, required _ValidationResult validationResult, required SigningCertificateQualification qualification, @@ -34,13 +33,13 @@ class DocumentValidationInfo extends StatelessWidget { _qualification = qualification, _areQualifiedTimestamps = areQualifiedTimestamps; - factory DocumentValidationInfo( + factory DocumentSignatureInfo( DocumentValidationResponseBody$Signatures$Item data, ) { final certDer = data.signingCertificate.certificateDer; final certificate = x509CertificateDataFromDer(certDer).tbsCertificate!; - return DocumentValidationInfo._( + return DocumentSignatureInfo._( certificate: certificate, validationResult: data.validationResult, qualification: data.signingCertificate.qualification, @@ -51,16 +50,11 @@ class DocumentValidationInfo extends StatelessWidget { @override Widget build(BuildContext context) { final subject = _certificate.subject[X500Oids.cn] ?? ''; - final label = InkWell( - onTap: () { - CertificateDetailsDialog.show(context, _certificate); - }, - child: Text( - subject, - style: const TextStyle(fontWeight: FontWeight.bold), - overflow: TextOverflow.ellipsis, - maxLines: 2, - ), + final label = Text( + subject, + style: const TextStyle(fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + maxLines: 2, ); return Row( @@ -139,9 +133,9 @@ class DocumentValidationInfo extends StatelessWidget { @widgetbook.UseCase( path: '[Core]', name: '', - type: DocumentValidationInfo, + type: DocumentSignatureInfo, ) -Widget previewDocumentValidationInfo(BuildContext context) { +Widget previewDocumentSignatureInfo(BuildContext context) { final validationResult = _ValidationResult.values.byName(context.knobs.list( label: "Validation result", options: _ValidationResult.values.map((e) => e.name).toList(), @@ -153,7 +147,7 @@ Widget previewDocumentValidationInfo(BuildContext context) { )); final areQualifiedTimestamps = context.knobs.boolean(label: "TS qualified"); - return DocumentValidationInfo( + return DocumentSignatureInfo( DocumentValidationResponseBody$Signatures$Item( validationResult: validationResult, level: diff --git a/lib/widgetbook_app.directories.g.dart b/lib/widgetbook_app.directories.g.dart index ecae5bb..9fa7e0e 100644 --- a/lib/widgetbook_app.directories.g.dart +++ b/lib/widgetbook_app.directories.g.dart @@ -42,7 +42,7 @@ import 'package:autogram/ui/widgets/certificate_picker.dart' as _i29; import 'package:autogram/ui/widgets/chip.dart' as _i7; import 'package:autogram/ui/widgets/close_button.dart' as _i8; import 'package:autogram/ui/widgets/dialogs.dart' as _i25; -import 'package:autogram/ui/widgets/document_validation_info.dart' as _i12; +import 'package:autogram/ui/widgets/document_signature_info.dart' as _i12; import 'package:autogram/ui/widgets/document_validation_strip.dart' as _i13; import 'package:autogram/ui/widgets/document_visualization.dart' as _i14; import 'package:autogram/ui/widgets/error_content.dart' as _i15; @@ -180,7 +180,7 @@ final directories = <_i1.WidgetbookNode>[ name: 'DocumentValidationInfo', useCase: _i1.WidgetbookUseCase( name: '', - builder: _i12.previewDocumentValidationInfo, + builder: _i12.previewDocumentSignatureInfo, ), ), _i1.WidgetbookComponent( From 3a7446094979fcde4ec9ab8f7305729f8e9f0c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 19:24:01 +0100 Subject: [PATCH 26/34] Extract strings --- lib/l10n/app_localizations.dart | 48 +++++++++++++++++++++ lib/l10n/app_localizations_sk.dart | 24 +++++++++++ lib/l10n/app_sk.arb | 10 +++++ lib/ui/widgets/document_signature_info.dart | 41 +++++++++--------- 4 files changed, 103 insertions(+), 20 deletions(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index a8cfd11..acd68f9 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -571,6 +571,54 @@ abstract class AppLocalizations { /// **'Platný do: {text}'** String certificateNotAfter(Object text); + /// No description provided for @validationResultPassedLabel. + /// + /// In sk, this message translates to: + /// **'Platný'** + String get validationResultPassedLabel; + + /// No description provided for @validationResultFailedLabel. + /// + /// In sk, this message translates to: + /// **'Neplatný'** + String get validationResultFailedLabel; + + /// No description provided for @validationResultIndeterminateLabel. + /// + /// In sk, this message translates to: + /// **'Neznámy'** + String get validationResultIndeterminateLabel; + + /// No description provided for @validationResultUnknownLabel. + /// + /// In sk, this message translates to: + /// **'Neznámy'** + String get validationResultUnknownLabel; + + /// No description provided for @signatureQualificationQesigLabel. + /// + /// In sk, this message translates to: + /// **'Vlastnoručný podpis'** + String get signatureQualificationQesigLabel; + + /// No description provided for @signatureQualificationQesigWithQTLabel. + /// + /// In sk, this message translates to: + /// **'Osvedčený podpis'** + String get signatureQualificationQesigWithQTLabel; + + /// No description provided for @signatureQualificationQesealWithQTLabel. + /// + /// In sk, this message translates to: + /// **'Elektronická pečať'** + String get signatureQualificationQesealWithQTLabel; + + /// No description provided for @signatureQualificationAdesigWithQTLabel. + /// + /// In sk, this message translates to: + /// **'Uznaný spôsob autorizácie'** + String get signatureQualificationAdesigWithQTLabel; + /// No description provided for @signDocumentTitle. /// /// In sk, this message translates to: diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index 1383fb4..9ab732e 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -308,6 +308,30 @@ class AppLocalizationsSk extends AppLocalizations { return 'Platný do: $text'; } + @override + String get validationResultPassedLabel => 'Platný'; + + @override + String get validationResultFailedLabel => 'Neplatný'; + + @override + String get validationResultIndeterminateLabel => 'Neznámy'; + + @override + String get validationResultUnknownLabel => 'Neznámy'; + + @override + String get signatureQualificationQesigLabel => 'Vlastnoručný podpis'; + + @override + String get signatureQualificationQesigWithQTLabel => 'Osvedčený podpis'; + + @override + String get signatureQualificationQesealWithQTLabel => 'Elektronická pečať'; + + @override + String get signatureQualificationAdesigWithQTLabel => 'Uznaný spôsob autorizácie'; + @override String get signDocumentTitle => 'Podpisovanie dokumentu'; diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index bc2f421..3dfb839 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -97,6 +97,16 @@ "certificateValidityNotAfterLabel": "Platný do", "certificateNotAfter": "Platný do: {text}", + "validationResultPassedLabel": "Platný", + "validationResultFailedLabel": "Neplatný", + "validationResultIndeterminateLabel": "Neznámy", + "validationResultUnknownLabel": "Neznámy", + + "signatureQualificationQesigLabel": "Vlastnoručný podpis", + "signatureQualificationQesigWithQTLabel": "Osvedčený podpis", + "signatureQualificationQesealWithQTLabel": "Elektronická pečať", + "signatureQualificationAdesigWithQTLabel": "Uznaný spôsob autorizácie", + "signDocumentTitle": "Podpisovanie dokumentu", "signDocumentCanceledHeading": "Podpisovanie pomocou občianskeho preukazu bolo prerušené", "signDocumentErrorHeading": "Pri podpisovaní sa vyskytla chyba", diff --git a/lib/ui/widgets/document_signature_info.dart b/lib/ui/widgets/document_signature_info.dart index 4da5fd6..7f0bfd7 100644 --- a/lib/ui/widgets/document_signature_info.dart +++ b/lib/ui/widgets/document_signature_info.dart @@ -5,6 +5,7 @@ import 'package:widgetbook/widgetbook.dart'; import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; import '../../oids.dart'; +import '../../strings_context.dart'; import '../../utils.dart'; import '../widgets/chip.dart' as avm; @@ -95,7 +96,7 @@ class DocumentSignatureInfo extends StatelessWidget { }; return avm.Chip( - label: _getLabel(), + label: _getLabel(context), foreground: foreground, background: background, border: border, @@ -103,30 +104,30 @@ class DocumentSignatureInfo extends StatelessWidget { ); } - String _getLabel() { - // TODO Extract labels into ARB - switch (_validationResult) { - case _ValidationResult.totalFailed: - return "Neplatný"; + String _getLabel(BuildContext context) { + final strings = context.strings; - case _ValidationResult.indeterminate: - case _ValidationResult.swaggerGeneratedUnknown: - return "Neznámy"; - - case _ValidationResult.totalPassed: - return switch (_qualification) { + return switch (_validationResult) { + _ValidationResult.totalFailed => strings.validationResultFailedLabel, + _ValidationResult.indeterminate => + strings.validationResultIndeterminateLabel, + _ValidationResult.swaggerGeneratedUnknown => + strings.validationResultUnknownLabel, + _ValidationResult.totalPassed => switch (_qualification) { SigningCertificateQualification.qesig => (!_areQualifiedTimestamps - ? "Vlastnoručný podpis" - : "Osvedčený podpis"), - SigningCertificateQualification.qeseal => - (_areQualifiedTimestamps ? "Elektronická pečať" : ""), - SigningCertificateQualification.adesigQcQc => - (_areQualifiedTimestamps ? "Uznaný spôsob autorizácie" : ""), + ? strings.signatureQualificationQesigLabel + : strings.signatureQualificationQesigWithQTLabel), + SigningCertificateQualification.qeseal => (_areQualifiedTimestamps + ? strings.signatureQualificationQesealWithQTLabel + : ""), + SigningCertificateQualification.adesigQcQc => (_areQualifiedTimestamps + ? strings.signatureQualificationAdesigWithQTLabel + : ""), _ => "" // TODO handle other cases properly // https://github.com/slovensko-digital/autogram/blob/748d17c4c8d1b14516dba76bbb8f7beaadbc1bf6/src/main/java/digital/slovensko/autogram/ui/gui/SignatureBadgeFactory.java#L116 - }; - } + } + }; } } From 7c19a855d92d43406084627fea98aca3e2b59980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 19:34:48 +0100 Subject: [PATCH 27/34] Cleanup ctor --- lib/ui/widgets/document_signature_info.dart | 41 ++++++++++----------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/lib/ui/widgets/document_signature_info.dart b/lib/ui/widgets/document_signature_info.dart index 7f0bfd7..cbd6f88 100644 --- a/lib/ui/widgets/document_signature_info.dart +++ b/lib/ui/widgets/document_signature_info.dart @@ -19,20 +19,17 @@ typedef _ValidationResult /// On left side, there is subject; validation result with qualification are /// on right side. class DocumentSignatureInfo extends StatelessWidget { - final TbsCertificate _certificate; - final _ValidationResult _validationResult; - final SigningCertificateQualification _qualification; - final bool _areQualifiedTimestamps; + final TbsCertificate certificate; + final _ValidationResult validationResult; + final SigningCertificateQualification qualification; + final bool areQualifiedTimestamps; const DocumentSignatureInfo._({ - required TbsCertificate certificate, - required _ValidationResult validationResult, - required SigningCertificateQualification qualification, - required bool areQualifiedTimestamps, - }) : _certificate = certificate, - _validationResult = validationResult, - _qualification = qualification, - _areQualifiedTimestamps = areQualifiedTimestamps; + required this.certificate, + required this.validationResult, + required this.qualification, + required this.areQualifiedTimestamps, + }); factory DocumentSignatureInfo( DocumentValidationResponseBody$Signatures$Item data, @@ -50,7 +47,7 @@ class DocumentSignatureInfo extends StatelessWidget { @override Widget build(BuildContext context) { - final subject = _certificate.subject[X500Oids.cn] ?? ''; + final subject = certificate.subject[X500Oids.cn] ?? ''; final label = Text( subject, style: const TextStyle(fontWeight: FontWeight.bold), @@ -70,25 +67,25 @@ class DocumentSignatureInfo extends StatelessWidget { } Widget _buildChip(BuildContext context) { - final icon = switch (_validationResult) { + final icon = switch (validationResult) { _ValidationResult.totalPassed => Icons.check, _ValidationResult.indeterminate => Icons.warning_amber_outlined, _ValidationResult.totalFailed => Icons.error_outline, _ => Icons.question_mark_outlined, }; - final foreground = switch (_validationResult) { + final foreground = switch (validationResult) { _ValidationResult.totalPassed => const Color(0xFF033608), _ValidationResult.indeterminate => const Color(0xFF4E2A00), _ValidationResult.totalFailed => const Color(0xFF4E0711), _ => Colors.black, }; - final background = switch (_validationResult) { + final background = switch (validationResult) { _ValidationResult.totalPassed => const Color(0xFFEDF5F3), _ValidationResult.indeterminate => const Color(0xFFF4F4EC), _ValidationResult.totalFailed => const Color(0xFFFBEEF0), _ => Colors.white, }; - final border = switch (_validationResult) { + final border = switch (validationResult) { _ValidationResult.totalPassed => const Color(0xFFA9D9CD), _ValidationResult.indeterminate => const Color(0xFFD5D6A2), _ValidationResult.totalFailed => const Color(0xFFC3112B), @@ -107,20 +104,20 @@ class DocumentSignatureInfo extends StatelessWidget { String _getLabel(BuildContext context) { final strings = context.strings; - return switch (_validationResult) { + return switch (validationResult) { _ValidationResult.totalFailed => strings.validationResultFailedLabel, _ValidationResult.indeterminate => strings.validationResultIndeterminateLabel, _ValidationResult.swaggerGeneratedUnknown => strings.validationResultUnknownLabel, - _ValidationResult.totalPassed => switch (_qualification) { - SigningCertificateQualification.qesig => (!_areQualifiedTimestamps + _ValidationResult.totalPassed => switch (qualification) { + SigningCertificateQualification.qesig => (!areQualifiedTimestamps ? strings.signatureQualificationQesigLabel : strings.signatureQualificationQesigWithQTLabel), - SigningCertificateQualification.qeseal => (_areQualifiedTimestamps + SigningCertificateQualification.qeseal => (areQualifiedTimestamps ? strings.signatureQualificationQesealWithQTLabel : ""), - SigningCertificateQualification.adesigQcQc => (_areQualifiedTimestamps + SigningCertificateQualification.adesigQcQc => (areQualifiedTimestamps ? strings.signatureQualificationAdesigWithQTLabel : ""), _ => "" From c63cab9203c65e1987c309bdacceb8628b12652e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 19:41:15 +0100 Subject: [PATCH 28/34] Update change log --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00bcf51..66fd15a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,10 @@ ## NEXT - 1.1.0(36) -- #35 -- #39 - Update AGP and Gradle version for latest Android Studio Ladybug (2024.2.1 Patch 1) +- #35 | Android - opening only specific file types +- #39 | When sharing document, set file name +- #36 | Implement first version of Document validation ## 2024-07-16 - v1.0.4(35) From 1cc2d13615e6e6cedc429a3696955e59c8379d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 20:21:33 +0100 Subject: [PATCH 29/34] Tune Chip UX --- lib/ui/widgets/chip.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ui/widgets/chip.dart b/lib/ui/widgets/chip.dart index b7d15ec..9181386 100644 --- a/lib/ui/widgets/chip.dart +++ b/lib/ui/widgets/chip.dart @@ -30,10 +30,11 @@ class Chip extends StatelessWidget { border: Border.all(color: border, width: 1), borderRadius: const BorderRadius.all(Radius.circular(4)), ), - padding: const EdgeInsets.all(4), + padding: const EdgeInsets.only(left: 8, top: 6, bottom: 6, right: 12), child: Row( children: [ if (leading != null) leading!, + if (leading != null) const SizedBox(width: 8), Text(label, style: TextStyle(color: foreground)), ], ), From c69cc9e3863907369311ff1f383994695291dd8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 21:01:39 +0100 Subject: [PATCH 30/34] Tune validation info strip UI --- lib/l10n/app_localizations.dart | 8 +- lib/l10n/app_localizations_sk.dart | 5 +- lib/l10n/app_sk.arb | 3 +- .../document_validation_fragment.dart | 22 +++-- lib/ui/widgets/document_validation_strip.dart | 87 ++++++++++++------- 5 files changed, 85 insertions(+), 40 deletions(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index acd68f9..e128ff6 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -460,7 +460,7 @@ abstract class AppLocalizations { /// No description provided for @documentValidationNoSignaturesLabel. /// /// In sk, this message translates to: - /// **'Dokument neobsahuje **žiadny podpis**'** + /// **'Dokument zatiaľ neobsahuje **žiadny podpis**'** String get documentValidationNoSignaturesLabel; /// No description provided for @documentValidationHasInvalidSignaturesLabel. @@ -469,6 +469,12 @@ abstract class AppLocalizations { /// **'Dokument obsahuje **neplatné podpisy**'** String get documentValidationHasInvalidSignaturesLabel; + /// No description provided for @documentValidationHasIndeterminateSignatureLabel. + /// + /// In sk, this message translates to: + /// **'Dokument obsahuje **neznámy podpis**'** + String get documentValidationHasIndeterminateSignatureLabel; + /// No description provided for @documentValidationHasValidSignaturesLabel. /// /// In sk, this message translates to: diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index 9ab732e..75f425a 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -238,11 +238,14 @@ class AppLocalizationsSk extends AppLocalizations { String get documentValidationLoadingLabel => 'Prebieha overovanie podpisov'; @override - String get documentValidationNoSignaturesLabel => 'Dokument neobsahuje **žiadny podpis**'; + String get documentValidationNoSignaturesLabel => 'Dokument zatiaľ neobsahuje **žiadny podpis**'; @override String get documentValidationHasInvalidSignaturesLabel => 'Dokument obsahuje **neplatné podpisy**'; + @override + String get documentValidationHasIndeterminateSignatureLabel => 'Dokument obsahuje **neznámy podpis**'; + @override String documentValidationHasValidSignaturesLabel(num count) { String _temp0 = intl.Intl.pluralLogic( diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index 3dfb839..9c60a98 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -74,8 +74,9 @@ "documentVisualizationCannotVisualizeTypeError": "Neviem vizualizovať {type} typ.", "documentValidationLoadingLabel": "Prebieha overovanie podpisov", - "documentValidationNoSignaturesLabel": "Dokument neobsahuje **žiadny podpis**", + "documentValidationNoSignaturesLabel": "Dokument zatiaľ neobsahuje **žiadny podpis**", "documentValidationHasInvalidSignaturesLabel": "Dokument obsahuje **neplatné podpisy**", + "documentValidationHasIndeterminateSignatureLabel": "Dokument obsahuje **neznámy podpis**", "documentValidationHasValidSignaturesLabel": "{count, plural, one {Dokument obsahuje **1 podpis**} few {Dokument obsahuje **{count} podpisy**} many {Dokument obsahuje {count} podpisov**} other {Dokument obsahuje **{count} podpisov**}}", "documentValidationSignaturesHeading": "Podpisy v dokumente", diff --git a/lib/ui/fragment/document_validation_fragment.dart b/lib/ui/fragment/document_validation_fragment.dart index 5a80193..64d18f8 100644 --- a/lib/ui/fragment/document_validation_fragment.dart +++ b/lib/ui/fragment/document_validation_fragment.dart @@ -1,6 +1,7 @@ import 'package:autogram_sign/autogram_sign.dart' show DocumentValidationResponseBody, + DocumentValidationResponseBody$Signatures$Item, DocumentValidationResponseBody$Signatures$ItemValidationResult; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -71,8 +72,9 @@ class _DocumentValidationFragmentState ), DocumentValidationSuccessState state => DocumentValidationStrip( value: DocumentValidationStripValue.value( - validCount: state.response.validSignaturesCount ?? 0, - invalidCount: state.response.invalidSignaturesCount ?? 0, + failedCount: state.response.failedCount, + indeterminateCount: state.response.indeterminateCount, + passedCount: state.response.passedCount, ), onTap: () { widget.onShowDocumentValidationInfoRequested.call(state.response); @@ -87,10 +89,18 @@ class _DocumentValidationFragmentState /// A set of extensions on [DocumentValidationResponseBody]. extension _DocumentValidationResponseBodyExtensions on DocumentValidationResponseBody { - int? get validSignaturesCount => signatures - ?.where((s) => s.validationResult == _ValidationResult.totalPassed) + List get signaturesOrEmpty => + (signatures ?? []); + + int get failedCount => signaturesOrEmpty + .where((s) => s.validationResult == _ValidationResult.totalFailed) + .length; + + int get indeterminateCount => signaturesOrEmpty + .where((s) => s.validationResult == _ValidationResult.indeterminate) .length; - int? get invalidSignaturesCount => signatures - ?.where((s) => s.validationResult != _ValidationResult.totalPassed) + + int get passedCount => signaturesOrEmpty + .where((s) => s.validationResult == _ValidationResult.totalPassed) .length; } diff --git a/lib/ui/widgets/document_validation_strip.dart b/lib/ui/widgets/document_validation_strip.dart index cf9b172..75c8988 100644 --- a/lib/ui/widgets/document_validation_strip.dart +++ b/lib/ui/widgets/document_validation_strip.dart @@ -24,26 +24,38 @@ class DocumentValidationStrip extends StatelessWidget { @override Widget build(BuildContext context) { - final data = (value.isLoading, value.validCount, value.invalidCount); + final data = ( + value.isLoading, + value.failedCount, + value.indeterminateCount, + value.passedCount + ); final isLoading = value.isLoading; - final hasSignatures = (value.validCount > 0 || value.invalidCount > 0); - + final hasSignatures = + (value.failedCount + value.indeterminateCount + value.passedCount) > 0; final strings = context.strings; - - final String text = switch (data) { - (true, _, _) => strings.documentValidationLoadingLabel, - (false, _, > 0) => strings.documentValidationHasInvalidSignaturesLabel, - (false, > 0, _) => - strings.documentValidationHasValidSignaturesLabel(value.validCount), - (false, 0, 0) => strings.documentValidationNoSignaturesLabel, - (_, _, _) => "" // technically invalid case - }; - final Color backgroundColor = switch (data) { - (true, _, _) => const Color(0xFF126DFF), - (false, _, > 0) => const Color(0xFFC3112B), - (false, > 0, _) => const Color(0xFF078814), - (false, 0, 0) => const Color(0xFF126DFF), - (_, _, _) => Colors.transparent, + final (String text, Color backgroundColor) = switch (data) { + (true, _, _, _) => ( + strings.documentValidationLoadingLabel, + const Color(0xFF126DFF) + ), + (false, 0, 0, 0) => ( + strings.documentValidationNoSignaturesLabel, + const Color(0xFF126DFF), + ), + (false, > 0, _, _) => ( + strings.documentValidationHasInvalidSignaturesLabel, + const Color(0xFFC3112B), + ), + (false, _, > 0, _) => ( + strings.documentValidationHasIndeterminateSignatureLabel, + const Color(0xFFbd730c), + ), + (false, 0, 0, > 0) => ( + strings.documentValidationHasValidSignaturesLabel(value.passedCount), + const Color(0xFF078814), + ), + (_, _, _, _) => ("", Colors.transparent), // technically invalid case }; const foregroundColor = Colors.white; final icon = (hasSignatures ? Icons.arrow_right_alt_outlined : Icons.close); @@ -79,24 +91,31 @@ class DocumentValidationStrip extends StatelessWidget { /// Value for [DocumentValidationStrip] widget. class DocumentValidationStripValue { final bool isLoading; - final int validCount; - final int invalidCount; + final int failedCount; + final int indeterminateCount; + final int passedCount; final Object? error; const DocumentValidationStripValue.loading() : isLoading = true, - validCount = 0, - invalidCount = 0, + failedCount = 0, + indeterminateCount = 0, + passedCount = 0, error = null; const DocumentValidationStripValue.value({ - required this.validCount, - required this.invalidCount, + required this.failedCount, + required this.indeterminateCount, + required this.passedCount, }) : isLoading = false, error = null; const DocumentValidationStripValue.none() - : this.value(validCount: 0, invalidCount: 0); + : this.value( + failedCount: 0, + indeterminateCount: 0, + passedCount: 0, + ); } @widgetbook.UseCase( @@ -116,21 +135,27 @@ Widget previewLoadingDocumentValidationStrip(BuildContext context) { type: DocumentValidationStrip, ) Widget previewOtherDocumentValidationStrip(BuildContext context) { - final validCount = context.knobs.list( - label: 'Valid count', + final passedCount = context.knobs.list( + label: 'Passed count', + options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + initialOption: 0, + ); + final indeterminateCount = context.knobs.list( + label: 'Indeterminate count', options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], initialOption: 0, ); - final invalidCount = context.knobs.list( - label: 'Invalid count', + final failedCount = context.knobs.list( + label: 'Failed count', options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], initialOption: 0, ); return DocumentValidationStrip( value: DocumentValidationStripValue.value( - validCount: validCount, - invalidCount: invalidCount, + failedCount: failedCount, + indeterminateCount: indeterminateCount, + passedCount: passedCount, ), ); } From defed68b747a603553d74741684e8f2edd6ecb5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sat, 16 Nov 2024 21:18:29 +0100 Subject: [PATCH 31/34] Explicitly specify compatible NDK version --- android/app/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/build.gradle b/android/app/build.gradle index 35b03ba..46ccb8f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -71,6 +71,7 @@ android { signingConfig signingConfigs.release } } + ndkVersion "25.1.8937393" } repositories { From 505a834092c31a713be0c1de2edece680674fa37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sun, 17 Nov 2024 12:48:06 +0100 Subject: [PATCH 32/34] Setup ProGuard to fix Android build --- android/app/build.gradle | 1 + android/app/proguard-rules.pro | 38 ++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 android/app/proguard-rules.pro diff --git a/android/app/build.gradle b/android/app/build.gradle index 46ccb8f..9b0e7d7 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -69,6 +69,7 @@ android { buildTypes { release { signingConfig signingConfigs.release + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } ndkVersion "25.1.8937393" diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000..07b3062 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,38 @@ +-dontwarn com.google.android.play.core.splitcompat.SplitCompatApplication +-dontwarn com.google.android.play.core.splitinstall.SplitInstallException +-dontwarn com.google.android.play.core.splitinstall.SplitInstallManager +-dontwarn com.google.android.play.core.splitinstall.SplitInstallManagerFactory +-dontwarn com.google.android.play.core.splitinstall.SplitInstallRequest$Builder +-dontwarn com.google.android.play.core.splitinstall.SplitInstallRequest +-dontwarn com.google.android.play.core.splitinstall.SplitInstallSessionState +-dontwarn com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener +-dontwarn com.google.android.play.core.tasks.OnFailureListener +-dontwarn com.google.android.play.core.tasks.OnSuccessListener +-dontwarn com.google.android.play.core.tasks.Task +-dontwarn com.google.api.client.http.GenericUrl +-dontwarn com.google.api.client.http.HttpHeaders +-dontwarn com.google.api.client.http.HttpRequest +-dontwarn com.google.api.client.http.HttpRequestFactory +-dontwarn com.google.api.client.http.HttpResponse +-dontwarn com.google.api.client.http.HttpTransport +-dontwarn com.google.api.client.http.javanet.NetHttpTransport$Builder +-dontwarn com.google.api.client.http.javanet.NetHttpTransport +-dontwarn javax.naming.Binding +-dontwarn javax.naming.NamingEnumeration +-dontwarn javax.naming.NamingException +-dontwarn javax.naming.directory.Attribute +-dontwarn javax.naming.directory.Attributes +-dontwarn javax.naming.directory.DirContext +-dontwarn javax.naming.directory.InitialDirContext +-dontwarn javax.naming.directory.SearchControls +-dontwarn javax.naming.directory.SearchResult +-dontwarn org.bouncycastle.jsse.BCSSLParameters +-dontwarn org.bouncycastle.jsse.BCSSLSocket +-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider +-dontwarn org.conscrypt.Conscrypt$Version +-dontwarn org.conscrypt.Conscrypt +-dontwarn org.conscrypt.ConscryptHostnameVerifier +-dontwarn org.joda.time.Instant +-dontwarn org.openjsse.javax.net.ssl.SSLParameters +-dontwarn org.openjsse.javax.net.ssl.SSLSocket +-dontwarn org.openjsse.net.ssl.OpenJSSE From caa7224bf3aeca6449511d513de193e9a0f679d8 Mon Sep 17 00:00:00 2001 From: mkepes Date: Sun, 17 Nov 2024 18:39:21 +0100 Subject: [PATCH 33/34] Cherry pick e6e69cef99c8880a0628cf25122dfe5505701291 Fix infinite loading state for remote signing --- lib/bloc/present_signed_document_cubit.dart | 19 ++++-- lib/bloc/present_signed_document_state.dart | 19 ++++-- lib/di.config.dart | 68 ++++++++++--------- .../present_signed_document_screen.dart | 10 ++- lib/widgetbook_app.directories.g.dart | 2 +- 5 files changed, 72 insertions(+), 46 deletions(-) diff --git a/lib/bloc/present_signed_document_cubit.dart b/lib/bloc/present_signed_document_cubit.dart index 7e8ee3c..06d4160 100644 --- a/lib/bloc/present_signed_document_cubit.dart +++ b/lib/bloc/present_signed_document_cubit.dart @@ -11,6 +11,7 @@ import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; import '../app_service.dart'; +import '../data/document_signing_type.dart'; import '../file_extensions.dart'; import '../file_system_entity_extensions.dart'; import '../ui/screens/present_signed_document_screen.dart'; @@ -31,11 +32,17 @@ class PresentSignedDocumentCubit extends Cubit { final SignDocumentResponseBody signedDocument; - PresentSignedDocumentCubit({ - required AppService appService, - @factoryParam required this.signedDocument, - }) : _appService = appService, - super(const PresentSignedDocumentInitialState()); + PresentSignedDocumentCubit( + {required AppService appService, + @factoryParam required this.signedDocument, + @factoryParam required DocumentSigningType signingType}) + : _appService = appService, + super( + signingType == DocumentSigningType.local + ? const PresentSignedDocumentInitialState() + // Remote documents are not saved locally, so we go directly to success state + : const PresentSignedRemoteDocumentSuccessState(), + ); /// Saves [signedDocument] into public directory. Future saveDocument() async { @@ -67,7 +74,7 @@ class PresentSignedDocumentCubit extends Cubit { Future getShareableFile() async { final state = this.state; - if (state is PresentSignedDocumentSuccessState) { + if (state is PresentSignedLocalDocumentSuccessState) { final file = state.file; if (await file.exists()) { diff --git a/lib/bloc/present_signed_document_state.dart b/lib/bloc/present_signed_document_state.dart index cbe5684..b3cdfc7 100644 --- a/lib/bloc/present_signed_document_state.dart +++ b/lib/bloc/present_signed_document_state.dart @@ -15,8 +15,8 @@ sealed class PresentSignedDocumentState { return PresentSignedDocumentErrorState(error); } - PresentSignedDocumentSuccessState toSuccess(File file) { - return PresentSignedDocumentSuccessState(file); + PresentSignedLocalDocumentSuccessState toSuccess(File file) { + return PresentSignedLocalDocumentSuccessState(file); } @override @@ -44,13 +44,24 @@ class PresentSignedDocumentErrorState extends PresentSignedDocumentState { } } -class PresentSignedDocumentSuccessState extends PresentSignedDocumentState { +class PresentSignedLocalDocumentSuccessState + extends PresentSignedDocumentState { final File file; - const PresentSignedDocumentSuccessState(this.file); + const PresentSignedLocalDocumentSuccessState(this.file); @override String toString() { return "$runtimeType(file: $file)"; } } + +class PresentSignedRemoteDocumentSuccessState + extends PresentSignedDocumentState { + const PresentSignedRemoteDocumentSuccessState(); + + @override + String toString() { + return "$runtimeType()"; + } +} diff --git a/lib/di.config.dart b/lib/di.config.dart index ad074a0..8c76c1e 100644 --- a/lib/di.config.dart +++ b/lib/di.config.dart @@ -8,29 +8,30 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i16; -import 'dart:io' as _i17; +import 'dart:async' as _i17; +import 'dart:io' as _i18; import 'package:autogram_sign/autogram_sign.dart' as _i7; import 'package:eidmsdk/eidmsdk.dart' as _i4; -import 'package:eidmsdk/types.dart' as _i13; -import 'package:flutter/foundation.dart' as _i12; +import 'package:eidmsdk/types.dart' as _i14; +import 'package:flutter/foundation.dart' as _i13; import 'package:get_it/get_it.dart' as _i1; import 'package:injectable/injectable.dart' as _i2; import 'app_service.dart' as _i3; -import 'bloc/create_document_cubit.dart' as _i15; -import 'bloc/document_validation_cubit.dart' as _i19; -import 'bloc/get_document_signature_type_cubit.dart' as _i21; +import 'bloc/create_document_cubit.dart' as _i16; +import 'bloc/document_validation_cubit.dart' as _i20; +import 'bloc/get_document_signature_type_cubit.dart' as _i22; import 'bloc/paired_device_list_cubit.dart' as _i8; import 'bloc/present_signed_document_cubit.dart' as _i9; -import 'bloc/preview_document_cubit.dart' as _i10; -import 'bloc/select_signing_certificate_cubit.dart' as _i11; -import 'bloc/sign_document_cubit.dart' as _i14; -import 'data/pdf_signing_option.dart' as _i18; -import 'di.dart' as _i22; +import 'bloc/preview_document_cubit.dart' as _i11; +import 'bloc/select_signing_certificate_cubit.dart' as _i12; +import 'bloc/sign_document_cubit.dart' as _i15; +import 'data/document_signing_type.dart' as _i10; +import 'data/pdf_signing_option.dart' as _i19; +import 'di.dart' as _i23; import 'services/encryption_key_registry.dart' as _i5; -import 'use_case/get_document_signature_type_use_case.dart' as _i20; +import 'use_case/get_document_signature_type_use_case.dart' as _i21; import 'use_case/get_document_version_use_case.dart' as _i6; extension GetItInjectableX on _i1.GetIt { @@ -55,64 +56,65 @@ extension GetItInjectableX on _i1.GetIt { gh.factory<_i8.PairedDeviceListCubit>( () => _i8.PairedDeviceListCubit(service: gh<_i7.IAutogramService>())); gh.factoryParam<_i9.PresentSignedDocumentCubit, - _i7.SignDocumentResponseBody, dynamic>(( + _i7.SignDocumentResponseBody, _i10.DocumentSigningType>(( signedDocument, - _, + signingType, ) => _i9.PresentSignedDocumentCubit( appService: gh<_i3.AppService>(), signedDocument: signedDocument, + signingType: signingType, )); - gh.factoryParam<_i10.PreviewDocumentCubit, String, dynamic>(( + gh.factoryParam<_i11.PreviewDocumentCubit, String, dynamic>(( documentId, _, ) => - _i10.PreviewDocumentCubit( + _i11.PreviewDocumentCubit( service: gh<_i7.IAutogramService>(), documentId: documentId, )); - gh.factoryParam<_i11.SelectSigningCertificateCubit, - _i12.ValueNotifier<_i13.Certificate?>, dynamic>(( + gh.factoryParam<_i12.SelectSigningCertificateCubit, + _i13.ValueNotifier<_i14.Certificate?>, dynamic>(( signingCertificate, _, ) => - _i11.SelectSigningCertificateCubit( + _i12.SelectSigningCertificateCubit( eidmsdk: gh<_i4.Eidmsdk>(), signingCertificate: signingCertificate, )); - gh.factoryParam<_i14.SignDocumentCubit, String, _i13.Certificate>(( + gh.factoryParam<_i15.SignDocumentCubit, String, _i14.Certificate>(( documentId, certificate, ) => - _i14.SignDocumentCubit( + _i15.SignDocumentCubit( service: gh<_i7.IAutogramService>(), eidmsdk: gh<_i4.Eidmsdk>(), documentId: documentId, certificate: certificate, )); - gh.factoryParam<_i15.CreateDocumentCubit, _i16.FutureOr<_i17.File>, - _i18.PdfSigningOption>(( + gh.factoryParam<_i16.CreateDocumentCubit, _i17.FutureOr<_i18.File>, + _i19.PdfSigningOption>(( file, pdfSigningOption, ) => - _i15.CreateDocumentCubit( + _i16.CreateDocumentCubit( service: gh<_i7.IAutogramService>(), file: file, pdfSigningOption: pdfSigningOption, )); - gh.factory<_i19.DocumentValidationCubit>(() => - _i19.DocumentValidationCubit(service: gh<_i7.IAutogramService>())); - gh.lazySingleton<_i20.GetDocumentSignatureTypeUseCase>( - () => _i20.GetDocumentSignatureTypeUseCase(gh<_i7.IAutogramService>())); - gh.factory<_i21.GetDocumentSignatureTypeCubit>(() => - _i21.GetDocumentSignatureTypeCubit( + gh.factory<_i20.DocumentValidationCubit>(() => + _i20.DocumentValidationCubit(service: gh<_i7.IAutogramService>())); + gh.lazySingleton<_i21.GetDocumentSignatureTypeUseCase>( + () => _i21.GetDocumentSignatureTypeUseCase(gh<_i7.IAutogramService>())); + gh.factory<_i22.GetDocumentSignatureTypeCubit>(() => + _i22.GetDocumentSignatureTypeCubit( getDocumentSignatureType: - gh<_i20.GetDocumentSignatureTypeUseCase>())); + gh<_i21.GetDocumentSignatureTypeUseCase>())); return this; } } -class _$ExtrernalModule extends _i22.ExtrernalModule { +class _$ExtrernalModule extends _i23.ExtrernalModule { @override _i4.Eidmsdk get eidmsdk => _i4.Eidmsdk(); } diff --git a/lib/ui/screens/present_signed_document_screen.dart b/lib/ui/screens/present_signed_document_screen.dart index 9aae052..ceb6dc7 100644 --- a/lib/ui/screens/present_signed_document_screen.dart +++ b/lib/ui/screens/present_signed_document_screen.dart @@ -42,6 +42,7 @@ class PresentSignedDocumentScreen extends StatelessWidget { create: (context) { final cubit = getIt.get( param1: signedDocument, + param2: signingType, ); if (signingType == DocumentSigningType.local) { @@ -163,11 +164,16 @@ class _Body extends StatelessWidget { onShareFileRequested: onShareFileRequested, onCloseRequested: onCloseRequested, ), - PresentSignedDocumentSuccessState state => _SuccessContent( + PresentSignedLocalDocumentSuccessState state => _SuccessContent( file: state.file, onShareFileRequested: onShareFileRequested, onCloseRequested: onCloseRequested, ), + PresentSignedRemoteDocumentSuccessState() => _SuccessContent( + file: null, + onShareFileRequested: null, + onCloseRequested: onCloseRequested, + ), }; } } @@ -347,7 +353,7 @@ Widget previewSuccessPresentSignedDocumentScreen(BuildContext context) { final file = File(path); return _Body( - state: PresentSignedDocumentSuccessState(file), + state: PresentSignedLocalDocumentSuccessState(file), signingType: signingType, onShareFileRequested: () { developer.log('onShareFileRequested'); diff --git a/lib/widgetbook_app.directories.g.dart b/lib/widgetbook_app.directories.g.dart index 9fa7e0e..59389d6 100644 --- a/lib/widgetbook_app.directories.g.dart +++ b/lib/widgetbook_app.directories.g.dart @@ -177,7 +177,7 @@ final directories = <_i1.WidgetbookNode>[ ), ), _i1.WidgetbookLeafComponent( - name: 'DocumentValidationInfo', + name: 'DocumentSignatureInfo', useCase: _i1.WidgetbookUseCase( name: '', builder: _i12.previewDocumentSignatureInfo, From 3fe32a40d714d52a7f74556e25e4e0ef37126c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Hlatk=C3=BD?= Date: Sun, 17 Nov 2024 21:43:58 +0100 Subject: [PATCH 34/34] Cleanup --- lib/bloc/present_signed_document_cubit.dart | 10 +++++----- lib/bloc/present_signed_document_state.dart | 7 ++----- lib/ui/fragment/document_validation_fragment.dart | 1 - 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/bloc/present_signed_document_cubit.dart b/lib/bloc/present_signed_document_cubit.dart index 06d4160..a394a9a 100644 --- a/lib/bloc/present_signed_document_cubit.dart +++ b/lib/bloc/present_signed_document_cubit.dart @@ -32,11 +32,11 @@ class PresentSignedDocumentCubit extends Cubit { final SignDocumentResponseBody signedDocument; - PresentSignedDocumentCubit( - {required AppService appService, - @factoryParam required this.signedDocument, - @factoryParam required DocumentSigningType signingType}) - : _appService = appService, + PresentSignedDocumentCubit({ + required AppService appService, + @factoryParam required this.signedDocument, + @factoryParam required DocumentSigningType signingType, + }) : _appService = appService, super( signingType == DocumentSigningType.local ? const PresentSignedDocumentInitialState() diff --git a/lib/bloc/present_signed_document_state.dart b/lib/bloc/present_signed_document_state.dart index b3cdfc7..3a7b12c 100644 --- a/lib/bloc/present_signed_document_state.dart +++ b/lib/bloc/present_signed_document_state.dart @@ -2,6 +2,8 @@ import 'dart:io' show File; import 'package:flutter/foundation.dart'; +import 'present_signed_document_cubit.dart'; + /// State for [PresentSignedDocumentCubit]. @immutable sealed class PresentSignedDocumentState { @@ -59,9 +61,4 @@ class PresentSignedLocalDocumentSuccessState class PresentSignedRemoteDocumentSuccessState extends PresentSignedDocumentState { const PresentSignedRemoteDocumentSuccessState(); - - @override - String toString() { - return "$runtimeType()"; - } } diff --git a/lib/ui/fragment/document_validation_fragment.dart b/lib/ui/fragment/document_validation_fragment.dart index 64d18f8..f5ffd55 100644 --- a/lib/ui/fragment/document_validation_fragment.dart +++ b/lib/ui/fragment/document_validation_fragment.dart @@ -51,7 +51,6 @@ class _DocumentValidationFragmentState Widget _buildContent(BuildContext context, DocumentValidationState state) { if (_hidden) { - // TODO Animate height from 100% to 0 return const SizedBox.shrink(); }