From a10c6efb00ae93ad94af3db18df5bd940c31e63f Mon Sep 17 00:00:00 2001 From: Anton Stubenbord Date: Sat, 11 Feb 2023 16:37:51 +0100 Subject: [PATCH] feat: extract snippets into widgets, code cleanup document details --- ios/Podfile.lock | 2 +- .../cubit/document_details_cubit.dart | 20 ++ .../view/pages/document_details_page.dart | 302 +++--------------- .../view/widgets/details_item.dart | 34 ++ .../view/widgets/document_content_widget.dart | 52 +++ .../widgets/document_download_button.dart | 1 - .../widgets/document_meta_data_widget.dart | 105 ++++++ .../widgets/document_overview_widget.dart | 89 ++++++ .../cubit/document_search_cubit.dart | 6 +- .../cubit/document_search_state.dart | 2 +- .../documents/cubit/documents_cubit.dart | 6 +- .../documents/cubit/documents_state.dart | 2 +- .../view/widgets/documents_empty_state.dart | 4 +- lib/features/home/view/home_page.dart | 15 +- lib/features/inbox/cubit/inbox_cubit.dart | 7 +- lib/features/inbox/cubit/inbox_state.dart | 2 +- lib/features/inbox/view/pages/inbox_page.dart | 29 +- .../cubit/linked_documents_cubit.dart | 6 +- .../cubit/linked_documents_state.dart | 2 +- .../view/linked_documents_page.dart | 29 +- .../document_paging_bloc_mixin.dart} | 6 +- .../paged_documents_state.dart | 4 +- .../view/document_paging_view_mixin.dart | 43 +++ .../cubit/saved_view_details_cubit.dart | 6 +- .../cubit/saved_view_details_state.dart | 2 +- .../view/saved_view_details_page.dart | 56 ++-- .../cubit/similar_documents_cubit.dart | 6 +- .../cubit/similar_documents_state.dart | 2 +- .../view/similar_documents_view.dart | 32 +- 29 files changed, 460 insertions(+), 412 deletions(-) create mode 100644 lib/features/document_details/view/widgets/details_item.dart create mode 100644 lib/features/document_details/view/widgets/document_content_widget.dart create mode 100644 lib/features/document_details/view/widgets/document_meta_data_widget.dart create mode 100644 lib/features/document_details/view/widgets/document_overview_widget.dart rename lib/features/paged_document_view/{paged_documents_mixin.dart => cubit/document_paging_bloc_mixin.dart} (97%) rename lib/features/paged_document_view/{model => cubit}/paged_documents_state.dart (95%) create mode 100644 lib/features/paged_document_view/view/document_paging_view_mixin.dart diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2c66854..3ae71c9 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -198,6 +198,6 @@ SPEC CHECKSUMS: url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2 WeScan: fed582f6c38014d529afb5aa9ffd1bad38fc72b7 -PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 +PODFILE CHECKSUM: 7daa35cc908d9fba025075df27cb57a1ba1ebf13 COCOAPODS: 1.11.3 diff --git a/lib/features/document_details/cubit/document_details_cubit.dart b/lib/features/document_details/cubit/document_details_cubit.dart index eaed8d2..d5ac4bc 100644 --- a/lib/features/document_details/cubit/document_details_cubit.dart +++ b/lib/features/document_details/cubit/document_details_cubit.dart @@ -7,6 +7,8 @@ import 'package:open_filex/open_filex.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/service/file_service.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; part 'document_details_state.dart'; @@ -73,6 +75,24 @@ class DocumentDetailsCubit extends Cubit { emit(state.copyWith(document: document)); } + Future shareDocument() async { + final documentBytes = await _api.download(state.document); + final dir = await getTemporaryDirectory(); + final String path = "${dir.path}/${state.document.originalFileName}"; + await File(path).writeAsBytes(documentBytes); + Share.shareXFiles( + [ + XFile( + path, + name: state.document.originalFileName, + mimeType: "application/pdf", + lastModified: state.document.modified, + ), + ], + subject: state.document.title, + ); + } + @override Future close() { for (final element in _subscriptions) { diff --git a/lib/features/document_details/view/pages/document_details_page.dart b/lib/features/document_details/view/pages/document_details_page.dart index 8572fb0..4661920 100644 --- a/lib/features/document_details/view/pages/document_details_page.dart +++ b/lib/features/document_details/view/pages/document_details_page.dart @@ -4,31 +4,27 @@ import 'package:badges/badges.dart' as b; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:intl/intl.dart'; import 'package:open_filex/open_filex.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; import 'package:paperless_mobile/core/translation/error_code_localization_mapper.dart'; -import 'package:paperless_mobile/core/widgets/highlighted_text.dart'; -import 'package:paperless_mobile/core/widgets/offline_widget.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/document_details/cubit/document_details_cubit.dart'; +import 'package:paperless_mobile/features/document_details/view/widgets/document_content_widget.dart'; import 'package:paperless_mobile/features/document_details/view/widgets/document_download_button.dart'; +import 'package:paperless_mobile/features/document_details/view/widgets/document_meta_data_widget.dart'; +import 'package:paperless_mobile/features/document_details/view/widgets/document_overview_widget.dart'; import 'package:paperless_mobile/features/document_edit/cubit/document_edit_cubit.dart'; import 'package:paperless_mobile/features/document_edit/view/document_edit_page.dart'; import 'package:paperless_mobile/features/documents/view/pages/document_view.dart'; import 'package:paperless_mobile/features/documents/view/widgets/delete_document_confirmation_dialog.dart'; import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart'; -import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_widget.dart'; -import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart'; -import 'package:paperless_mobile/features/labels/view/widgets/label_text.dart'; import 'package:paperless_mobile/features/similar_documents/cubit/similar_documents_cubit.dart'; +import 'package:paperless_mobile/features/similar_documents/view/similar_documents_view.dart'; import 'package:paperless_mobile/generated/l10n.dart'; -import 'package:paperless_mobile/helpers/format_helpers.dart'; import 'package:paperless_mobile/helpers/message_helpers.dart'; import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; -import 'package:paperless_mobile/features/similar_documents/view/similar_documents_view.dart'; //TODO: Refactor this into several widgets class DocumentDetailsPage extends StatefulWidget { @@ -49,7 +45,7 @@ class DocumentDetailsPage extends StatefulWidget { class _DocumentDetailsPageState extends State { late Future _metaData; - + static const double _itemPadding = 24; @override void initState() { super.initState(); @@ -70,7 +66,7 @@ class _DocumentDetailsPageState extends State { length: 4, child: Scaffold( floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, - floatingActionButton: widget.allowEdit ? _buildAppBar() : null, + floatingActionButton: widget.allowEdit ? _buildEditButton() : null, bottomNavigationBar: _buildBottomAppBar(), body: NestedScrollView( headerSliverBuilder: (context, innerBoxIsScrolled) => [ @@ -96,27 +92,30 @@ class _DocumentDetailsPageState extends State { child: Text( S.of(context).documentDetailsPageTabOverviewLabel, style: TextStyle( - color: Theme.of(context) - .colorScheme - .onPrimaryContainer), + color: Theme.of(context) + .colorScheme + .onPrimaryContainer, + ), ), ), Tab( child: Text( S.of(context).documentDetailsPageTabContentLabel, style: TextStyle( - color: Theme.of(context) - .colorScheme - .onPrimaryContainer), + color: Theme.of(context) + .colorScheme + .onPrimaryContainer, + ), ), ), Tab( child: Text( S.of(context).documentDetailsPageTabMetaDataLabel, style: TextStyle( - color: Theme.of(context) - .colorScheme - .onPrimaryContainer), + color: Theme.of(context) + .colorScheme + .onPrimaryContainer, + ), ), ), Tab( @@ -146,20 +145,26 @@ class _DocumentDetailsPageState extends State { ), child: TabBarView( children: [ - _buildDocumentOverview( - state.document, + DocumentOverviewWidget( + document: state.document, + itemSpacing: _itemPadding, + queryString: widget.titleAndContentQueryString, ), - _buildDocumentContentView( - state.document, - state, + DocumentContentWidget( + isFullContentLoaded: state.isFullContentLoaded, + document: state.document, + fullContent: state.fullContent, + queryString: widget.titleAndContentQueryString, ), - _buildDocumentMetaDataView( - state.document, + DocumentMetaDataWidget( + document: state.document, + itemSpacing: _itemPadding, + metaData: _metaData, ), const SimilarDocumentsView(), ], ), - ).paddedSymmetrically(horizontal: 8); + ); }, ), ), @@ -168,7 +173,7 @@ class _DocumentDetailsPageState extends State { ); } - BlocBuilder _buildAppBar() { + Widget _buildEditButton() { return BlocBuilder( builder: (context, state) { final _filteredSuggestions = @@ -176,7 +181,7 @@ class _DocumentDetailsPageState extends State { return BlocBuilder( builder: (context, connectivityState) { if (!connectivityState.isConnected) { - return Container(); + return const SizedBox.shrink(); } return b.Badge( position: b.BadgePosition.topEnd(top: -12, end: -6), @@ -244,8 +249,10 @@ class _DocumentDetailsPageState extends State { IconButton( tooltip: S.of(context).documentDetailsPageShareTooltip, icon: const Icon(Icons.share), - onPressed: - isConnected ? () => _onShare(state.document) : null, + onPressed: isConnected + ? () => + context.read().shareDocument() + : null, ), ], ); @@ -317,203 +324,6 @@ class _DocumentDetailsPageState extends State { } } - Widget _buildDocumentMetaDataView(DocumentModel document) { - return BlocBuilder( - builder: (context, state) { - if (!state.isConnected) { - return const Center( - child: OfflineWidget(), - ); - } - return FutureBuilder( - future: _metaData, - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const Center(child: CircularProgressIndicator()); - } - final meta = snapshot.data!; - return ListView( - children: [ - _DetailsItem.text(DateFormat().format(document.modified), - label: S.of(context).documentModifiedPropertyLabel, - context: context) - .paddedOnly(bottom: 16), - _DetailsItem.text(DateFormat().format(document.added), - label: S.of(context).documentAddedPropertyLabel, - context: context) - .paddedSymmetrically(vertical: 16), - _DetailsItem( - label: S - .of(context) - .documentArchiveSerialNumberPropertyLongLabel, - content: document.archiveSerialNumber != null - ? Text(document.archiveSerialNumber.toString()) - : TextButton.icon( - icon: const Icon(Icons.archive_outlined), - label: Text(S - .of(context) - .documentDetailsPageAssignAsnButtonLabel), - onPressed: widget.allowEdit - ? () => _assignAsn(document) - : null, - ), - ).paddedSymmetrically(vertical: 16), - _DetailsItem.text( - meta.mediaFilename, - context: context, - label: - S.of(context).documentMetaDataMediaFilenamePropertyLabel, - ).paddedSymmetrically(vertical: 16), - _DetailsItem.text( - meta.originalChecksum, - context: context, - label: S.of(context).documentMetaDataChecksumLabel, - ).paddedSymmetrically(vertical: 16), - _DetailsItem.text(formatBytes(meta.originalSize, 2), - label: - S.of(context).documentMetaDataOriginalFileSizeLabel, - context: context) - .paddedSymmetrically(vertical: 16), - _DetailsItem.text( - meta.originalMimeType, - label: S.of(context).documentMetaDataOriginalMimeTypeLabel, - context: context, - ).paddedSymmetrically(vertical: 16), - ], - ); - }, - ); - }, - ); - } - - Future _assignAsn(DocumentModel document) async { - try { - await context.read().assignAsn(document); - } on PaperlessServerException catch (error, stackTrace) { - showErrorMessage(context, error, stackTrace); - } - } - - Widget _buildDocumentContentView( - DocumentModel document, - DocumentDetailsState state, - ) { - return SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - HighlightedText( - text: (state.isFullContentLoaded - ? state.fullContent - : document.content) ?? - "", - highlights: widget.titleAndContentQueryString != null - ? widget.titleAndContentQueryString!.split(" ") - : [], - style: Theme.of(context).textTheme.bodyMedium, - caseSensitive: false, - ), - if (!state.isFullContentLoaded && (document.content ?? '').isNotEmpty) - Align( - alignment: Alignment.bottomCenter, - child: TextButton( - child: - Text(S.of(context).documentDetailsPageLoadFullContentLabel), - onPressed: () { - context.read().loadFullContent(); - }, - ), - ), - ], - ).padded(8).paddedOnly(top: 14), - ); - } - - Widget _buildDocumentOverview(DocumentModel document) { - return ListView( - children: [ - _DetailsItem( - label: S.of(context).documentTitlePropertyLabel, - content: HighlightedText( - text: document.title, - highlights: widget.titleAndContentQueryString?.split(" ") ?? [], - style: Theme.of(context).textTheme.bodyLarge, - ), - ).paddedOnly(bottom: 16), - _DetailsItem.text( - DateFormat.yMMMMd().format(document.created), - context: context, - label: S.of(context).documentCreatedPropertyLabel, - ).paddedSymmetrically(vertical: 16), - Visibility( - visible: document.documentType != null, - child: _DetailsItem( - label: S.of(context).documentDocumentTypePropertyLabel, - content: LabelText( - style: Theme.of(context).textTheme.bodyLarge, - id: document.documentType, - ), - ).paddedSymmetrically(vertical: 16), - ), - Visibility( - visible: document.correspondent != null, - child: _DetailsItem( - label: S.of(context).documentCorrespondentPropertyLabel, - content: LabelText( - style: Theme.of(context).textTheme.bodyLarge, - id: document.correspondent, - ), - ).paddedSymmetrically(vertical: 16), - ), - Visibility( - visible: document.storagePath != null, - child: _DetailsItem( - label: S.of(context).documentStoragePathPropertyLabel, - content: StoragePathWidget( - pathId: document.storagePath, - ), - ).paddedSymmetrically(vertical: 16), - ), - Visibility( - visible: document.tags.isNotEmpty, - child: _DetailsItem( - label: S.of(context).documentTagsPropertyLabel, - content: Padding( - padding: const EdgeInsets.only(top: 8.0), - child: TagsWidget( - isClickable: widget.isLabelClickable, - tagIds: document.tags, - ), - ), - ).paddedSymmetrically(vertical: 16), - ), - ], - ); - } - - /// - /// Downloads file to temporary directory, from which it can then be shared. - /// - Future _onShare(DocumentModel document) async { - Uint8List documentBytes = - await context.read().download(document); - final dir = await getTemporaryDirectory(); - final String path = "${dir.path}/${document.originalFileName}"; - await File(path).writeAsBytes(documentBytes); - Share.shareXFiles( - [ - XFile( - path, - name: document.originalFileName, - mimeType: "application/pdf", - lastModified: document.modified, - ) - ], - subject: document.title, - ); - } - void _onDelete(DocumentModel document) async { final delete = await showDialog( context: context, @@ -546,42 +356,6 @@ class _DocumentDetailsPageState extends State { } } -class _DetailsItem extends StatelessWidget { - final String label; - final Widget content; - const _DetailsItem({ - Key? key, - required this.label, - required this.content, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - label, - style: Theme.of(context).textTheme.bodySmall, - ), - content, - ], - ), - ); - } - - _DetailsItem.text( - String text, { - required this.label, - required BuildContext context, - }) : content = Text( - text, - style: Theme.of(context).textTheme.bodyLarge, - ); -} - class ColoredTabBar extends Container implements PreferredSizeWidget { ColoredTabBar({ super.key, diff --git a/lib/features/document_details/view/widgets/details_item.dart b/lib/features/document_details/view/widgets/details_item.dart new file mode 100644 index 0000000..e50f24c --- /dev/null +++ b/lib/features/document_details/view/widgets/details_item.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class DetailsItem extends StatelessWidget { + final String label; + final Widget content; + const DetailsItem({ + Key? key, + required this.label, + required this.content, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: Theme.of(context).textTheme.bodySmall, + ), + content, + ], + ); + } + + DetailsItem.text( + String text, { + required this.label, + required BuildContext context, + }) : content = Text( + text, + style: Theme.of(context).textTheme.bodyLarge, + ); +} diff --git a/lib/features/document_details/view/widgets/document_content_widget.dart b/lib/features/document_details/view/widgets/document_content_widget.dart new file mode 100644 index 0000000..82d3ee5 --- /dev/null +++ b/lib/features/document_details/view/widgets/document_content_widget.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/core/widgets/highlighted_text.dart'; +import 'package:paperless_mobile/features/document_details/cubit/document_details_cubit.dart'; +import 'package:paperless_mobile/generated/l10n.dart'; + +class DocumentContentWidget extends StatelessWidget { + final bool isFullContentLoaded; + final String? fullContent; + final String? queryString; + final DocumentModel document; + const DocumentContentWidget({ + super.key, + required this.isFullContentLoaded, + this.fullContent, + required this.document, + this.queryString, + }); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + padding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + HighlightedText( + text: (isFullContentLoaded ? fullContent : document.content) ?? "", + highlights: queryString != null ? queryString!.split(" ") : [], + style: Theme.of(context).textTheme.bodyMedium, + caseSensitive: false, + ), + if (!isFullContentLoaded && (document.content ?? '').isNotEmpty) + Align( + alignment: Alignment.bottomCenter, + child: TextButton( + child: + Text(S.of(context).documentDetailsPageLoadFullContentLabel), + onPressed: () { + context.read().loadFullContent(); + }, + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/document_details/view/widgets/document_download_button.dart b/lib/features/document_details/view/widgets/document_download_button.dart index ccaf894..6aa3b4c 100644 --- a/lib/features/document_details/view/widgets/document_download_button.dart +++ b/lib/features/document_details/view/widgets/document_download_button.dart @@ -6,7 +6,6 @@ import 'package:paperless_mobile/core/service/file_service.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/generated/l10n.dart'; import 'package:paperless_mobile/helpers/message_helpers.dart'; -import 'package:paperless_mobile/constants.dart'; import 'package:provider/provider.dart'; class DocumentDownloadButton extends StatefulWidget { diff --git a/lib/features/document_details/view/widgets/document_meta_data_widget.dart b/lib/features/document_details/view/widgets/document_meta_data_widget.dart new file mode 100644 index 0000000..70c11ec --- /dev/null +++ b/lib/features/document_details/view/widgets/document_meta_data_widget.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:intl/intl.dart'; +import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; +import 'package:paperless_mobile/core/widgets/offline_widget.dart'; +import 'package:paperless_mobile/extensions/flutter_extensions.dart'; +import 'package:paperless_mobile/features/document_details/cubit/document_details_cubit.dart'; +import 'package:paperless_mobile/features/document_details/view/widgets/details_item.dart'; +import 'package:paperless_mobile/generated/l10n.dart'; +import 'package:paperless_mobile/helpers/format_helpers.dart'; +import 'package:paperless_mobile/helpers/message_helpers.dart'; + +class DocumentMetaDataWidget extends StatelessWidget { + final Future metaData; + final DocumentModel document; + final double itemSpacing; + const DocumentMetaDataWidget({ + super.key, + required this.metaData, + required this.document, + required this.itemSpacing, + }); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (!state.isConnected) { + return const Center( + child: OfflineWidget(), + ); + } + return FutureBuilder( + future: metaData, + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const Center(child: CircularProgressIndicator()); + } + final meta = snapshot.data!; + return ListView( + padding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + children: [ + DetailsItem( + label: S + .of(context) + .documentArchiveSerialNumberPropertyLongLabel, + content: document.archiveSerialNumber != null + ? Text(document.archiveSerialNumber.toString()) + : TextButton.icon( + icon: const Icon(Icons.archive_outlined), + label: Text(S + .of(context) + .documentDetailsPageAssignAsnButtonLabel), + onPressed: () => _assignAsn(context), + ), + ).paddedOnly(bottom: itemSpacing), + DetailsItem.text(DateFormat().format(document.modified), + label: S.of(context).documentModifiedPropertyLabel, + context: context) + .paddedOnly(bottom: itemSpacing), + DetailsItem.text(DateFormat().format(document.added), + label: S.of(context).documentAddedPropertyLabel, + context: context) + .paddedOnly(bottom: itemSpacing), + DetailsItem.text( + meta.mediaFilename, + context: context, + label: + S.of(context).documentMetaDataMediaFilenamePropertyLabel, + ).paddedOnly(bottom: itemSpacing), + DetailsItem.text( + meta.originalChecksum, + context: context, + label: S.of(context).documentMetaDataChecksumLabel, + ).paddedOnly(bottom: itemSpacing), + DetailsItem.text(formatBytes(meta.originalSize, 2), + label: + S.of(context).documentMetaDataOriginalFileSizeLabel, + context: context) + .paddedOnly(bottom: itemSpacing), + DetailsItem.text( + meta.originalMimeType, + label: S.of(context).documentMetaDataOriginalMimeTypeLabel, + context: context, + ).paddedOnly(bottom: itemSpacing), + ], + ); + }, + ); + }, + ); + } + + Future _assignAsn(BuildContext context) async { + try { + await context.read().assignAsn(document); + } on PaperlessServerException catch (error, stackTrace) { + showErrorMessage(context, error, stackTrace); + } + } +} diff --git a/lib/features/document_details/view/widgets/document_overview_widget.dart b/lib/features/document_details/view/widgets/document_overview_widget.dart new file mode 100644 index 0000000..f255666 --- /dev/null +++ b/lib/features/document_details/view/widgets/document_overview_widget.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/core/widgets/highlighted_text.dart'; +import 'package:paperless_mobile/extensions/flutter_extensions.dart'; +import 'package:paperless_mobile/features/document_details/view/widgets/details_item.dart'; +import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_widget.dart'; +import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart'; +import 'package:paperless_mobile/features/labels/view/widgets/label_text.dart'; +import 'package:paperless_mobile/generated/l10n.dart'; + +class DocumentOverviewWidget extends StatelessWidget { + final DocumentModel document; + final String? queryString; + final double itemSpacing; + const DocumentOverviewWidget({ + super.key, + required this.document, + this.queryString, + required this.itemSpacing, + }); + + @override + Widget build(BuildContext context) { + return ListView( + padding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + children: [ + DetailsItem( + label: S.of(context).documentTitlePropertyLabel, + content: HighlightedText( + text: document.title, + highlights: queryString?.split(" ") ?? [], + style: Theme.of(context).textTheme.bodyLarge, + ), + ).paddedOnly(bottom: itemSpacing), + DetailsItem.text( + DateFormat.yMMMMd().format(document.created), + context: context, + label: S.of(context).documentCreatedPropertyLabel, + ).paddedOnly(bottom: itemSpacing), + Visibility( + visible: document.documentType != null, + child: DetailsItem( + label: S.of(context).documentDocumentTypePropertyLabel, + content: LabelText( + style: Theme.of(context).textTheme.bodyLarge, + id: document.documentType, + ), + ).paddedOnly(bottom: itemSpacing), + ), + Visibility( + visible: document.correspondent != null, + child: DetailsItem( + label: S.of(context).documentCorrespondentPropertyLabel, + content: LabelText( + style: Theme.of(context).textTheme.bodyLarge, + id: document.correspondent, + ), + ).paddedOnly(bottom: itemSpacing), + ), + Visibility( + visible: document.storagePath != null, + child: DetailsItem( + label: S.of(context).documentStoragePathPropertyLabel, + content: StoragePathWidget( + pathId: document.storagePath, + ), + ).paddedOnly(bottom: itemSpacing), + ), + Visibility( + visible: document.tags.isNotEmpty, + child: DetailsItem( + label: S.of(context).documentTagsPropertyLabel, + content: Padding( + padding: const EdgeInsets.only(top: 8.0), + child: TagsWidget( + isClickable: false, + tagIds: document.tags, + ), + ), + ).paddedOnly(bottom: itemSpacing), + ), + ], + ); + } +} diff --git a/lib/features/document_search/cubit/document_search_cubit.dart b/lib/features/document_search/cubit/document_search_cubit.dart index e11e0f3..8dba0c1 100644 --- a/lib/features/document_search/cubit/document_search_cubit.dart +++ b/lib/features/document_search/cubit/document_search_cubit.dart @@ -2,17 +2,17 @@ import 'package:collection/collection.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; -import 'package:paperless_mobile/features/paged_document_view/paged_documents_mixin.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; -import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; part 'document_search_state.dart'; part 'document_search_cubit.g.dart'; class DocumentSearchCubit extends HydratedCubit - with PagedDocumentsMixin { + with DocumentPagingBlocMixin { @override final PaperlessDocumentsApi api; @override diff --git a/lib/features/document_search/cubit/document_search_state.dart b/lib/features/document_search/cubit/document_search_state.dart index 10c74f2..7b6cda5 100644 --- a/lib/features/document_search/cubit/document_search_state.dart +++ b/lib/features/document_search/cubit/document_search_state.dart @@ -6,7 +6,7 @@ enum SearchView { } @JsonSerializable(ignoreUnannotated: true) -class DocumentSearchState extends PagedDocumentsState { +class DocumentSearchState extends DocumentPagingState { @JsonKey() final List searchHistory; final SearchView view; diff --git a/lib/features/documents/cubit/documents_cubit.dart b/lib/features/documents/cubit/documents_cubit.dart index 80e9e9b..a9aae8d 100644 --- a/lib/features/documents/cubit/documents_cubit.dart +++ b/lib/features/documents/cubit/documents_cubit.dart @@ -5,16 +5,16 @@ import 'package:flutter/foundation.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; -import 'package:paperless_mobile/features/paged_document_view/paged_documents_mixin.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; import 'package:paperless_mobile/features/settings/model/view_type.dart'; import 'package:json_annotation/json_annotation.dart'; -import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; part 'documents_state.dart'; part 'documents_cubit.g.dart'; class DocumentsCubit extends HydratedCubit - with PagedDocumentsMixin { + with DocumentPagingBlocMixin { @override final PaperlessDocumentsApi api; diff --git a/lib/features/documents/cubit/documents_state.dart b/lib/features/documents/cubit/documents_state.dart index 614a6aa..a60b878 100644 --- a/lib/features/documents/cubit/documents_state.dart +++ b/lib/features/documents/cubit/documents_state.dart @@ -1,7 +1,7 @@ part of 'documents_cubit.dart'; @JsonSerializable() -class DocumentsState extends PagedDocumentsState { +class DocumentsState extends DocumentPagingState { @JsonKey(includeFromJson: false, includeToJson: false) final List selection; diff --git a/lib/features/documents/view/widgets/documents_empty_state.dart b/lib/features/documents/view/widgets/documents_empty_state.dart index 4c3d25b..676f052 100644 --- a/lib/features/documents/view/widgets/documents_empty_state.dart +++ b/lib/features/documents/view/widgets/documents_empty_state.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/widgets/empty_state.dart'; import 'package:paperless_mobile/extensions/flutter_extensions.dart'; -import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; import 'package:paperless_mobile/generated/l10n.dart'; class DocumentsEmptyState extends StatelessWidget { - final PagedDocumentsState state; + final DocumentPagingState state; final VoidCallback? onReset; const DocumentsEmptyState({ Key? key, diff --git a/lib/features/home/view/home_page.dart b/lib/features/home/view/home_page.dart index cf8b09c..3ee38c7 100644 --- a/lib/features/home/view/home_page.dart +++ b/lib/features/home/view/home_page.dart @@ -25,6 +25,7 @@ import 'package:paperless_mobile/features/inbox/view/pages/inbox_page.dart'; import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart'; import 'package:paperless_mobile/features/labels/view/pages/labels_page.dart'; import 'package:paperless_mobile/features/notifications/services/local_notification_service.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart'; import 'package:paperless_mobile/features/sharing/share_intent_queue.dart'; import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart'; @@ -271,8 +272,18 @@ class _HomePageState extends State with WidgetsBindingObserver { ], child: const LabelsPage(), ), - BlocProvider.value( - value: _inboxCubit, + MultiBlocProvider( + providers: [ + // We need to manually downcast the inboxcubit to the + // mixed-in DocumentPagingBlocMixin to use the + // DocumentPagingViewMixin in the inbox. + BlocProvider.value( + value: _inboxCubit, + ), + BlocProvider.value( + value: _inboxCubit, + ), + ], child: const InboxPage(), ), // const SettingsPage(), diff --git a/lib/features/inbox/cubit/inbox_cubit.dart b/lib/features/inbox/cubit/inbox_cubit.dart index ed82010..2b008a0 100644 --- a/lib/features/inbox/cubit/inbox_cubit.dart +++ b/lib/features/inbox/cubit/inbox_cubit.dart @@ -5,13 +5,14 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; import 'package:paperless_mobile/core/repository/label_repository.dart'; -import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart'; -import 'package:paperless_mobile/features/paged_document_view/paged_documents_mixin.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; part 'inbox_cubit.g.dart'; part 'inbox_state.dart'; -class InboxCubit extends HydratedCubit with PagedDocumentsMixin { +class InboxCubit extends HydratedCubit + with DocumentPagingBlocMixin { final LabelRepository _tagsRepository; final LabelRepository _correspondentRepository; final LabelRepository _documentTypeRepository; diff --git a/lib/features/inbox/cubit/inbox_state.dart b/lib/features/inbox/cubit/inbox_state.dart index d65ed96..99c0d82 100644 --- a/lib/features/inbox/cubit/inbox_state.dart +++ b/lib/features/inbox/cubit/inbox_state.dart @@ -1,7 +1,7 @@ part of 'inbox_cubit.dart'; @JsonSerializable(ignoreUnannotated: true) -class InboxState extends PagedDocumentsState { +class InboxState extends DocumentPagingState { final Iterable inboxTags; final Map availableTags; diff --git a/lib/features/inbox/view/pages/inbox_page.dart b/lib/features/inbox/view/pages/inbox_page.dart index 41dd3a6..fdbaea1 100644 --- a/lib/features/inbox/view/pages/inbox_page.dart +++ b/lib/features/inbox/view/pages/inbox_page.dart @@ -12,6 +12,7 @@ import 'package:paperless_mobile/extensions/flutter_extensions.dart'; import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart'; import 'package:paperless_mobile/features/inbox/view/widgets/inbox_empty_widget.dart'; import 'package:paperless_mobile/features/inbox/view/widgets/inbox_item.dart'; +import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart'; import 'package:paperless_mobile/features/search_app_bar/view/search_app_bar.dart'; import 'package:paperless_mobile/generated/l10n.dart'; import 'package:paperless_mobile/helpers/message_helpers.dart'; @@ -23,35 +24,15 @@ class InboxPage extends StatefulWidget { State createState() => _InboxPageState(); } -class _InboxPageState extends State { - final ScrollController _scrollController = ScrollController(); +class _InboxPageState extends State with DocumentPagingViewMixin { + @override + final pagingScrollController = ScrollController(); final _emptyStateRefreshIndicatorKey = GlobalKey(); @override void initState() { super.initState(); context.read().loadInbox(); - _scrollController.addListener(_listenForLoadNewData); - } - - @override - void dispose() { - _scrollController.removeListener(_listenForLoadNewData); - super.dispose(); - } - - void _listenForLoadNewData() { - final currState = context.read().state; - if (_scrollController.offset >= - _scrollController.position.maxScrollExtent * 0.75 && - !currState.isLoading && - !currState.isLastPageLoaded) { - try { - context.read().loadMore(); - } on PaperlessServerException catch (error, stackTrace) { - showErrorMessage(context, error, stackTrace); - } - } } @override @@ -143,7 +124,7 @@ class _InboxPageState extends State { physics: state.documents.isEmpty ? const NeverScrollableScrollPhysics() : const AlwaysScrollableScrollPhysics(), - controller: _scrollController, + controller: pagingScrollController, slivers: [ SearchAppBar( hintText: S.of(context).documentSearchSearchDocuments, diff --git a/lib/features/linked_documents/cubit/linked_documents_cubit.dart b/lib/features/linked_documents/cubit/linked_documents_cubit.dart index a4ee175..12731b1 100644 --- a/lib/features/linked_documents/cubit/linked_documents_cubit.dart +++ b/lib/features/linked_documents/cubit/linked_documents_cubit.dart @@ -2,15 +2,15 @@ import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; -import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart'; -import 'package:paperless_mobile/features/paged_document_view/paged_documents_mixin.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; import 'package:paperless_mobile/features/settings/model/view_type.dart'; part 'linked_documents_state.dart'; part 'linked_documents_cubit.g.dart'; class LinkedDocumentsCubit extends HydratedCubit - with PagedDocumentsMixin { + with DocumentPagingBlocMixin { @override final PaperlessDocumentsApi api; diff --git a/lib/features/linked_documents/cubit/linked_documents_state.dart b/lib/features/linked_documents/cubit/linked_documents_state.dart index 29877a6..5545f0f 100644 --- a/lib/features/linked_documents/cubit/linked_documents_state.dart +++ b/lib/features/linked_documents/cubit/linked_documents_state.dart @@ -1,7 +1,7 @@ part of 'linked_documents_cubit.dart'; @JsonSerializable(ignoreUnannotated: true) -class LinkedDocumentsState extends PagedDocumentsState { +class LinkedDocumentsState extends DocumentPagingState { @JsonKey() final ViewType viewType; const LinkedDocumentsState({ diff --git a/lib/features/linked_documents/view/linked_documents_page.dart b/lib/features/linked_documents/view/linked_documents_page.dart index 7b97e7a..8b77a6b 100644 --- a/lib/features/linked_documents/view/linked_documents_page.dart +++ b/lib/features/linked_documents/view/linked_documents_page.dart @@ -1,12 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.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/linked_documents/cubit/linked_documents_cubit.dart'; +import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart'; import 'package:paperless_mobile/generated/l10n.dart'; -import 'package:paperless_mobile/helpers/message_helpers.dart'; import 'package:paperless_mobile/routes/document_details_route.dart'; class LinkedDocumentsPage extends StatefulWidget { @@ -16,28 +15,10 @@ class LinkedDocumentsPage extends StatefulWidget { State createState() => _LinkedDocumentsPageState(); } -class _LinkedDocumentsPageState extends State { - final _scrollController = ScrollController(); - +class _LinkedDocumentsPageState extends State + with DocumentPagingViewMixin { @override - void initState() { - super.initState(); - _scrollController.addListener(_listenForLoadNewData); - } - - void _listenForLoadNewData() async { - final currState = context.read().state; - if (_scrollController.offset >= - _scrollController.position.maxScrollExtent * 0.75 && - !currState.isLoading && - !currState.isLastPageLoaded) { - try { - await context.read().loadMore(); - } on PaperlessServerException catch (error, stackTrace) { - showErrorMessage(context, error, stackTrace); - } - } - } + final pagingScrollController = ScrollController(); @override Widget build(BuildContext context) { @@ -60,7 +41,7 @@ class _LinkedDocumentsPageState extends State { return BlocBuilder( builder: (context, connectivity) { return CustomScrollView( - controller: _scrollController, + controller: pagingScrollController, slivers: [ SliverAdaptiveDocumentsView( viewType: state.viewType, diff --git a/lib/features/paged_document_view/paged_documents_mixin.dart b/lib/features/paged_document_view/cubit/document_paging_bloc_mixin.dart similarity index 97% rename from lib/features/paged_document_view/paged_documents_mixin.dart rename to lib/features/paged_document_view/cubit/document_paging_bloc_mixin.dart index 2800502..0164cea 100644 --- a/lib/features/paged_document_view/paged_documents_mixin.dart +++ b/lib/features/paged_document_view/cubit/document_paging_bloc_mixin.dart @@ -1,17 +1,15 @@ -import 'dart:developer'; - import 'package:collection/collection.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; -import 'model/paged_documents_state.dart'; +import 'paged_documents_state.dart'; /// /// Mixin which can be used on cubits that handle documents. /// This implements all paging and filtering logic. /// -mixin PagedDocumentsMixin +mixin DocumentPagingBlocMixin on BlocBase { PaperlessDocumentsApi get api; DocumentChangedNotifier get notifier; diff --git a/lib/features/paged_document_view/model/paged_documents_state.dart b/lib/features/paged_document_view/cubit/paged_documents_state.dart similarity index 95% rename from lib/features/paged_document_view/model/paged_documents_state.dart rename to lib/features/paged_document_view/cubit/paged_documents_state.dart index e50fe46..298248e 100644 --- a/lib/features/paged_document_view/model/paged_documents_state.dart +++ b/lib/features/paged_document_view/cubit/paged_documents_state.dart @@ -5,13 +5,13 @@ import 'package:paperless_api/paperless_api.dart'; /// Base state for all blocs/cubits using a paged view of documents. /// [T] is the return type of the API call. /// -abstract class PagedDocumentsState extends Equatable { +abstract class DocumentPagingState extends Equatable { final bool hasLoaded; final bool isLoading; final List> value; final DocumentFilter filter; - const PagedDocumentsState({ + const DocumentPagingState({ this.value = const [], this.hasLoaded = false, this.isLoading = false, diff --git a/lib/features/paged_document_view/view/document_paging_view_mixin.dart b/lib/features/paged_document_view/view/document_paging_view_mixin.dart new file mode 100644 index 0000000..470a4b9 --- /dev/null +++ b/lib/features/paged_document_view/view/document_paging_view_mixin.dart @@ -0,0 +1,43 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:paperless_api/paperless_api.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; +import 'package:paperless_mobile/helpers/message_helpers.dart'; +import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart'; + +mixin DocumentPagingViewMixin on State { + ScrollController get pagingScrollController; + + @override + void initState() { + super.initState(); + pagingScrollController.addListener(shouldLoadMoreDocumentsListener); + } + + @override + void dispose() { + pagingScrollController.removeListener(shouldLoadMoreDocumentsListener); + super.dispose(); + } + + DocumentPagingBlocMixin get _bloc => context.read(); + + void shouldLoadMoreDocumentsListener() async { + if (shouldLoadMoreDocuments) { + try { + await _bloc.loadMore(); + } on PaperlessServerException catch (error, stackTrace) { + showErrorMessage(context, error, stackTrace); + } + } + } + + bool get shouldLoadMoreDocuments { + final currState = _bloc.state; + return pagingScrollController.position.maxScrollExtent != 0 && + !currState.isLoading && + !currState.isLastPageLoaded && + pagingScrollController.offset >= + pagingScrollController.position.maxScrollExtent * 0.75; + } +} diff --git a/lib/features/saved_view_details/cubit/saved_view_details_cubit.dart b/lib/features/saved_view_details/cubit/saved_view_details_cubit.dart index f7ea868..044b3c2 100644 --- a/lib/features/saved_view_details/cubit/saved_view_details_cubit.dart +++ b/lib/features/saved_view_details/cubit/saved_view_details_cubit.dart @@ -2,15 +2,15 @@ import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; -import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart'; -import 'package:paperless_mobile/features/paged_document_view/paged_documents_mixin.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; import 'package:paperless_mobile/features/settings/model/view_type.dart'; part 'saved_view_details_cubit.g.dart'; part 'saved_view_details_state.dart'; class SavedViewDetailsCubit extends HydratedCubit - with PagedDocumentsMixin { + with DocumentPagingBlocMixin { @override final PaperlessDocumentsApi api; diff --git a/lib/features/saved_view_details/cubit/saved_view_details_state.dart b/lib/features/saved_view_details/cubit/saved_view_details_state.dart index 96ed250..6b99083 100644 --- a/lib/features/saved_view_details/cubit/saved_view_details_state.dart +++ b/lib/features/saved_view_details/cubit/saved_view_details_state.dart @@ -1,7 +1,7 @@ part of 'saved_view_details_cubit.dart'; @JsonSerializable(ignoreUnannotated: true) -class SavedViewDetailsState extends PagedDocumentsState { +class SavedViewDetailsState extends DocumentPagingState { @JsonKey() final ViewType viewType; diff --git a/lib/features/saved_view_details/view/saved_view_details_page.dart b/lib/features/saved_view_details/view/saved_view_details_page.dart index e45e004..d6a356c 100644 --- a/lib/features/saved_view_details/view/saved_view_details_page.dart +++ b/lib/features/saved_view_details/view/saved_view_details_page.dart @@ -6,8 +6,8 @@ import 'package:paperless_mobile/features/documents/view/widgets/adaptive_docume import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart'; import 'package:paperless_mobile/features/documents/view/widgets/selection/confirm_delete_saved_view_dialog.dart'; import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart'; +import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart'; import 'package:paperless_mobile/features/saved_view_details/cubit/saved_view_details_cubit.dart'; -import 'package:paperless_mobile/helpers/message_helpers.dart'; import 'package:paperless_mobile/routes/document_details_route.dart'; class SavedViewDetailsPage extends StatefulWidget { @@ -21,33 +21,14 @@ class SavedViewDetailsPage extends StatefulWidget { State createState() => _SavedViewDetailsPageState(); } -class _SavedViewDetailsPageState extends State { - final _scrollController = ScrollController(); - +class _SavedViewDetailsPageState extends State + with DocumentPagingViewMixin { @override - void initState() { - super.initState(); - _scrollController.addListener(_listenForLoadNewData); - } - - void _listenForLoadNewData() async { - final currState = context.read().state; - if (_scrollController.offset >= - _scrollController.position.maxScrollExtent * 0.7 && - !currState.isLoading && - !currState.isLastPageLoaded) { - try { - await context.read().loadMore(); - } on PaperlessServerException catch (error, stackTrace) { - showErrorMessage(context, error, stackTrace); - } - } - } + final pagingScrollController = ScrollController(); @override Widget build(BuildContext context) { final cubit = context.read(); - return Scaffold( appBar: AppBar( title: Text(cubit.savedView.name), @@ -57,8 +38,9 @@ class _SavedViewDetailsPageState extends State { onPressed: () async { final shouldDelete = await showDialog( context: context, - builder: (context) => - ConfirmDeleteSavedViewDialog(view: cubit.savedView), + builder: (context) => ConfirmDeleteSavedViewDialog( + view: cubit.savedView, + ), ) ?? false; if (shouldDelete) { @@ -85,7 +67,7 @@ class _SavedViewDetailsPageState extends State { return BlocBuilder( builder: (context, connectivity) { return CustomScrollView( - controller: _scrollController, + controller: pagingScrollController, slivers: [ SliverAdaptiveDocumentsView( documents: state.documents, @@ -93,7 +75,16 @@ class _SavedViewDetailsPageState extends State { isLabelClickable: false, isLoading: state.isLoading, hasLoaded: state.hasLoaded, - onTap: _onOpenDocumentDetails, + onTap: (document) { + Navigator.pushNamed( + context, + DocumentDetailsRoute.routeName, + arguments: DocumentDetailsRouteArguments( + document: document, + isLabelClickable: false, + ), + ); + }, viewType: state.viewType, ), if (state.hasLoaded && state.isLoading) @@ -110,15 +101,4 @@ class _SavedViewDetailsPageState extends State { ), ); } - - void _onOpenDocumentDetails(DocumentModel document) { - Navigator.pushNamed( - context, - DocumentDetailsRoute.routeName, - arguments: DocumentDetailsRouteArguments( - document: document, - isLabelClickable: false, - ), - ); - } } diff --git a/lib/features/similar_documents/cubit/similar_documents_cubit.dart b/lib/features/similar_documents/cubit/similar_documents_cubit.dart index 1edb7fd..6551455 100644 --- a/lib/features/similar_documents/cubit/similar_documents_cubit.dart +++ b/lib/features/similar_documents/cubit/similar_documents_cubit.dart @@ -1,13 +1,13 @@ import 'package:bloc/bloc.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart'; -import 'package:paperless_mobile/features/paged_document_view/paged_documents_mixin.dart'; -import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart'; +import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart'; part 'similar_documents_state.dart'; class SimilarDocumentsCubit extends Cubit - with PagedDocumentsMixin { + with DocumentPagingBlocMixin { final int documentId; @override diff --git a/lib/features/similar_documents/cubit/similar_documents_state.dart b/lib/features/similar_documents/cubit/similar_documents_state.dart index 75b683e..8a1424d 100644 --- a/lib/features/similar_documents/cubit/similar_documents_state.dart +++ b/lib/features/similar_documents/cubit/similar_documents_state.dart @@ -1,6 +1,6 @@ part of 'similar_documents_cubit.dart'; -class SimilarDocumentsState extends PagedDocumentsState { +class SimilarDocumentsState extends DocumentPagingState { const SimilarDocumentsState({ super.filter, super.hasLoaded, diff --git a/lib/features/similar_documents/view/similar_documents_view.dart b/lib/features/similar_documents/view/similar_documents_view.dart index 0092e44..826801e 100644 --- a/lib/features/similar_documents/view/similar_documents_view.dart +++ b/lib/features/similar_documents/view/similar_documents_view.dart @@ -2,10 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:paperless_api/paperless_api.dart'; import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart'; -import 'package:paperless_mobile/core/widgets/hint_card.dart'; import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart'; import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart'; -import 'package:paperless_mobile/features/documents/view/widgets/items/document_list_item.dart'; +import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart'; import 'package:paperless_mobile/features/similar_documents/cubit/similar_documents_cubit.dart'; import 'package:paperless_mobile/helpers/message_helpers.dart'; import 'package:paperless_mobile/routes/document_details_route.dart'; @@ -17,13 +16,14 @@ class SimilarDocumentsView extends StatefulWidget { State createState() => _SimilarDocumentsViewState(); } -class _SimilarDocumentsViewState extends State { - final _scrollController = ScrollController(); +class _SimilarDocumentsViewState extends State + with DocumentPagingViewMixin { + @override + final pagingScrollController = ScrollController(); @override void initState() { super.initState(); - _scrollController.addListener(_listenForLoadNewData); try { context.read().initialize(); } on PaperlessServerException catch (error, stackTrace) { @@ -31,26 +31,6 @@ class _SimilarDocumentsViewState extends State { } } - @override - void dispose() { - _scrollController.removeListener(_listenForLoadNewData); - super.dispose(); - } - - void _listenForLoadNewData() async { - final currState = context.read().state; - if (_scrollController.offset >= - _scrollController.position.maxScrollExtent * 0.75 && - !currState.isLoading && - !currState.isLastPageLoaded) { - try { - await context.read().loadMore(); - } on PaperlessServerException catch (error, stackTrace) { - showErrorMessage(context, error, stackTrace); - } - } - } - @override Widget build(BuildContext context) { return BlocBuilder( @@ -70,7 +50,7 @@ class _SimilarDocumentsViewState extends State { return BlocBuilder( builder: (context, connectivity) { return CustomScrollView( - controller: _scrollController, + controller: pagingScrollController, slivers: [ SliverAdaptiveDocumentsView( documents: state.documents,