diff --git a/README.md b/README.md index 685ff8bb..208c748f 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ If you want to manually install dependencies and build generated files, you can #### Inside the project's root folder 4. Install the dependencies for the app ```sh - flutter pub get + flutter packages pub get ``` 5. Build generated files for the app ```sh @@ -108,7 +108,7 @@ If you want to manually install dependencies and build generated files, you can ``` 6. Generate the localization files for the app ```sh - flutter pub run intl_utils:generate + flutter gen-l10n ``` ### Build release version diff --git a/lib/features/document_search/cubit/document_search_cubit.dart b/lib/features/document_search/cubit/document_search_cubit.dart index c07fb052..7fb1e71d 100644 --- a/lib/features/document_search/cubit/document_search_cubit.dart +++ b/lib/features/document_search/cubit/document_search_cubit.dart @@ -5,6 +5,7 @@ import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; +import 'package:paperless_mobile/features/settings/model/view_type.dart'; part 'document_search_state.dart'; @@ -33,7 +34,7 @@ class DocumentSearchCubit extends HydratedCubit view: SearchView.results, )); final searchFilter = DocumentFilter( - query: TextQuery.titleAndContent(query), + query: TextQuery.extended(query), ); await updateFilter(filter: searchFilter); @@ -48,6 +49,10 @@ class DocumentSearchCubit extends HydratedCubit ); } + void updateViewType(ViewType viewType) { + emit(state.copyWith(viewType: viewType)); + } + void removeHistoryEntry(String entry) { emit( state.copyWith( diff --git a/lib/features/document_search/cubit/document_search_state.dart b/lib/features/document_search/cubit/document_search_state.dart index 7b6cda50..f2cae8cc 100644 --- a/lib/features/document_search/cubit/document_search_state.dart +++ b/lib/features/document_search/cubit/document_search_state.dart @@ -11,10 +11,13 @@ class DocumentSearchState extends DocumentPagingState { final List searchHistory; final SearchView view; final List suggestions; + @JsonKey() + final ViewType viewType; const DocumentSearchState({ this.view = SearchView.suggestions, this.searchHistory = const [], this.suggestions = const [], + this.viewType = ViewType.detailed, super.filter, super.hasLoaded, super.isLoading, @@ -27,6 +30,7 @@ class DocumentSearchState extends DocumentPagingState { searchHistory, suggestions, view, + viewType, ]; @override @@ -52,6 +56,7 @@ class DocumentSearchState extends DocumentPagingState { DocumentFilter? filter, List? suggestions, SearchView? view, + ViewType? viewType, }) { return DocumentSearchState( value: value ?? this.value, @@ -61,6 +66,7 @@ class DocumentSearchState extends DocumentPagingState { searchHistory: searchHistory ?? this.searchHistory, view: view ?? this.view, suggestions: suggestions ?? this.suggestions, + viewType: viewType ?? this.viewType, ); } diff --git a/lib/features/document_search/view/document_search_page.dart b/lib/features/document_search/view/document_search_page.dart index 444a1f86..8f1d2a75 100644 --- a/lib/features/document_search/view/document_search_page.dart +++ b/lib/features/document_search/view/document_search_page.dart @@ -7,6 +7,8 @@ import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart'; import 'package:paperless_mobile/features/document_search/view/remove_history_entry_dialog.dart'; import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart'; +import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart'; +import 'package:paperless_mobile/features/settings/model/view_type.dart'; import 'package:paperless_mobile/generated/l10n/app_localizations.dart'; import 'package:paperless_mobile/routes/document_details_route.dart'; @@ -168,7 +170,7 @@ class _DocumentSearchPageState extends State { alignment: Alignment.center, transform: Matrix4.rotationY(math.pi), child: IconButton( - icon: Icon(Icons.arrow_outward), + icon: const Icon(Icons.arrow_outward), onPressed: () { _queryController.text = '$suggestion '; _queryController.selection = TextSelection.fromPosition( @@ -181,9 +183,23 @@ class _DocumentSearchPageState extends State { } Widget _buildResultsView(DocumentSearchState state) { - final header = Text( - S.of(context)!.results, - style: Theme.of(context).textTheme.labelSmall, + final header = Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + S.of(context)!.results, + style: Theme.of(context).textTheme.bodySmall, + ), + BlocBuilder( + builder: (context, state) { + return ViewTypeSelectionWidget( + viewType: state.viewType, + onChanged: (type) => + context.read().updateViewType(type), + ); + }, + ) + ], ).padded(); return CustomScrollView( slivers: [ @@ -196,6 +212,7 @@ class _DocumentSearchPageState extends State { ) else SliverAdaptiveDocumentsView( + viewType: state.viewType, documents: state.documents, hasInternetConnection: true, isLabelClickable: false, diff --git a/lib/features/documents/view/widgets/adaptive_documents_view.dart b/lib/features/documents/view/widgets/adaptive_documents_view.dart index 9c36e6c5..149c8e1a 100644 --- a/lib/features/documents/view/widgets/adaptive_documents_view.dart +++ b/lib/features/documents/view/widgets/adaptive_documents_view.dart @@ -146,6 +146,7 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView { onDocumentTypeSelected: onDocumentTypeSelected, onStoragePathSelected: onStoragePathSelected, enableHeroAnimation: enableHeroAnimation, + highlights: document.searchHit?.highlights, ), ); }, diff --git a/lib/features/documents/view/widgets/items/document_detailed_item.dart b/lib/features/documents/view/widgets/items/document_detailed_item.dart index 8b0634a4..e7ad0e1b 100644 --- a/lib/features/documents/view/widgets/items/document_detailed_item.dart +++ b/lib/features/documents/view/widgets/items/document_detailed_item.dart @@ -8,10 +8,13 @@ import 'package:paperless_mobile/features/documents/view/widgets/items/document_ import 'package:paperless_mobile/features/labels/correspondent/view/widgets/correspondent_widget.dart'; import 'package:paperless_mobile/features/labels/document_type/view/widgets/document_type_widget.dart'; import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart'; +import 'package:flutter_html/flutter_html.dart'; class DocumentDetailedItem extends DocumentItem { + final String? highlights; const DocumentDetailedItem({ super.key, + this.highlights, required super.document, required super.isSelected, required super.isSelectionActive, @@ -37,7 +40,9 @@ class DocumentDetailedItem extends DocumentItem { padding.bottom - kBottomNavigationBarHeight - kToolbarHeight; - final maxHeight = min(500.0, availableHeight); + final maxHeight = highlights != null + ? min(600.0, availableHeight) + : min(500.0, availableHeight); return Card( child: InkWell( enableFeedback: true, @@ -52,90 +57,98 @@ class DocumentDetailedItem extends DocumentItem { onLongPress: () { onSelected?.call(document); }, - child: Stack( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, + ConstrainedBox( + constraints: BoxConstraints.tightFor( + width: double.infinity, + height: maxHeight / 2, + ), + child: DocumentPreview( + document: document, + fit: BoxFit.cover, + alignment: Alignment.topCenter, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - ConstrainedBox( - constraints: BoxConstraints.tightFor( - width: double.infinity, - height: maxHeight / 2, - ), - child: DocumentPreview( - document: document, - fit: BoxFit.cover, - alignment: Alignment.topCenter, + Text( + DateFormat.yMMMMd().format(document.created), + style: Theme.of(context) + .textTheme + .bodySmall + ?.apply(color: Theme.of(context).hintColor), + ), + if (document.archiveSerialNumber != null) + Row( + children: [ + Text( + '#${document.archiveSerialNumber}', + style: Theme.of(context) + .textTheme + .bodySmall + ?.apply(color: Theme.of(context).hintColor), + ), + ], ), + ], + ).paddedLTRB(8, 8, 8, 4), + Text( + document.title, + style: Theme.of(context).textTheme.titleMedium, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ).paddedLTRB(8, 0, 8, 4), + Row( + children: [ + const Icon( + Icons.person_outline, + size: 16, + ).paddedOnly(right: 4.0), + CorrespondentWidget( + onSelected: onCorrespondentSelected, + textStyle: Theme.of(context).textTheme.titleSmall?.apply( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + correspondentId: document.correspondent, ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - DateFormat.yMMMMd().format(document.created), - style: Theme.of(context) - .textTheme - .bodySmall - ?.apply(color: Theme.of(context).hintColor), - ), - if (document.archiveSerialNumber != null) - Row( - children: [ - Text( - '#${document.archiveSerialNumber}', - style: Theme.of(context) - .textTheme - .bodySmall - ?.apply(color: Theme.of(context).hintColor), - ), - ], + ], + ).paddedLTRB(8, 0, 8, 4), + Row( + children: [ + const Icon( + Icons.description_outlined, + size: 16, + ).paddedOnly(right: 4.0), + DocumentTypeWidget( + onSelected: onDocumentTypeSelected, + textStyle: Theme.of(context).textTheme.titleSmall?.apply( + color: Theme.of(context).colorScheme.onSurfaceVariant, ), - ], - ).paddedLTRB(8, 8, 8, 4), - Text( - document.title, - style: Theme.of(context).textTheme.titleMedium, - maxLines: 2, - overflow: TextOverflow.ellipsis, - ).paddedLTRB(8, 0, 8, 4), - Row( - children: [ - const Icon( - Icons.person_outline, - size: 16, - ).paddedOnly(right: 4.0), - CorrespondentWidget( - onSelected: onCorrespondentSelected, - textStyle: Theme.of(context).textTheme.titleSmall?.apply( - color: - Theme.of(context).colorScheme.onSurfaceVariant, - ), - correspondentId: document.correspondent, - ), - ], - ).paddedLTRB(8, 0, 8, 4), - Row( - children: [ - const Icon( - Icons.description_outlined, - size: 16, - ).paddedOnly(right: 4.0), - DocumentTypeWidget( - onSelected: onDocumentTypeSelected, - textStyle: Theme.of(context).textTheme.titleSmall?.apply( - color: - Theme.of(context).colorScheme.onSurfaceVariant, - ), - documentTypeId: document.documentType, - ), - ], - ).paddedLTRB(8, 0, 8, 4), - TagsWidget( - isMultiLine: false, - tagIds: document.tags, - ).padded(), + documentTypeId: document.documentType, + ), ], - ), + ).paddedLTRB(8, 0, 8, 4), + TagsWidget( + isMultiLine: false, + tagIds: document.tags, + ).padded(), + if (highlights != null) + Html( + data: '

${highlights!}

', + style: { + "span": Style( + backgroundColor: Colors.yellow, + color: Colors.black, + ), + "p": Style( + maxLines: 3, + textOverflow: TextOverflow.ellipsis, + ), + }, + ).padded(), ], ), ), diff --git a/lib/features/documents/view/widgets/selection/view_type_selection_widget.dart b/lib/features/documents/view/widgets/selection/view_type_selection_widget.dart index 54e7aada..b3b3f367 100644 --- a/lib/features/documents/view/widgets/selection/view_type_selection_widget.dart +++ b/lib/features/documents/view/widgets/selection/view_type_selection_widget.dart @@ -27,7 +27,9 @@ class ViewTypeSelectionWidget extends StatelessWidget { icon = Icons.article_outlined; break; } + return PopupMenuButton( + position: PopupMenuPosition.under, initialValue: viewType, icon: Icon(icon), itemBuilder: (context) => [ diff --git a/pubspec.lock b/pubspec.lock index b467ab1c..fe5cf803 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -564,6 +564,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.7.0" + flutter_html: + dependency: "direct main" + description: + name: flutter_html + sha256: "342c7908f0a67bcec62b6e0f7cf23e23bafe7f64693665dd35be98d5e783bdfd" + url: "https://pub.dev" + source: hosted + version: "3.0.0-alpha.6" flutter_keyboard_visibility: dependency: transitive description: @@ -1005,6 +1013,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + numerus: + dependency: transitive + description: + name: numerus + sha256: "436759d84f233b40107d0cc31cfa92d24e0960afeb2e506be70926d4cddffd9e" + url: "https://pub.dev" + source: hosted + version: "2.0.0" octo_image: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 604ad73c..34230684 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,6 @@ name: paperless_mobile -description: Application to conveniently scan and share documents with a paperless-ng +description: + Application to conveniently scan and share documents with a paperless-ng server. # The following line prevents the package from being accidentally published to @@ -19,7 +20,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 2.0.7+24 environment: - sdk: '>=2.19.0 <4.0.0' + sdk: ">=2.19.0 <4.0.0" # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -88,7 +89,7 @@ dependencies: open_filex: ^4.3.2 flutter_displaymode: ^0.5.0 dynamic_color: ^1.5.4 - + flutter_html: ^3.0.0-alpha.6 dev_dependencies: integration_test: @@ -147,7 +148,6 @@ flutter: # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages - flutter_native_splash: image: assets/logos/paperless_logo_green.png color: "#f9f9f9" diff --git a/scripts/install_dependencies.sh b/scripts/install_dependencies.sh index 70551fea..1558a610 100644 --- a/scripts/install_dependencies.sh +++ b/scripts/install_dependencies.sh @@ -4,6 +4,6 @@ pushd packages/paperless_api flutter pub get flutter pub run build_runner build --delete-conflicting-outputs popd -flutter pub get +flutter packages pub get flutter gen-l10n flutter pub run build_runner build --delete-conflicting-outputs