mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-06 15:15:50 -06:00
feat: extract snippets into widgets, code cleanup document details
This commit is contained in:
@@ -198,6 +198,6 @@ SPEC CHECKSUMS:
|
|||||||
url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
|
url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
|
||||||
WeScan: fed582f6c38014d529afb5aa9ffd1bad38fc72b7
|
WeScan: fed582f6c38014d529afb5aa9ffd1bad38fc72b7
|
||||||
|
|
||||||
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
|
PODFILE CHECKSUM: 7daa35cc908d9fba025075df27cb57a1ba1ebf13
|
||||||
|
|
||||||
COCOAPODS: 1.11.3
|
COCOAPODS: 1.11.3
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import 'package:open_filex/open_filex.dart';
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||||
import 'package:paperless_mobile/core/service/file_service.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';
|
part 'document_details_state.dart';
|
||||||
|
|
||||||
@@ -73,6 +75,24 @@ class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
|
|||||||
emit(state.copyWith(document: document));
|
emit(state.copyWith(document: document));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> 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
|
@override
|
||||||
Future<void> close() {
|
Future<void> close() {
|
||||||
for (final element in _subscriptions) {
|
for (final element in _subscriptions) {
|
||||||
|
|||||||
@@ -4,31 +4,27 @@ import 'package:badges/badges.dart' as b;
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:open_filex/open_filex.dart';
|
import 'package:open_filex/open_filex.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.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/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/extensions/flutter_extensions.dart';
|
||||||
import 'package:paperless_mobile/features/document_details/cubit/document_details_cubit.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_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/cubit/document_edit_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/document_edit/view/document_edit_page.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/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/delete_document_confirmation_dialog.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/document_preview.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/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/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/helpers/format_helpers.dart';
|
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:share_plus/share_plus.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
|
//TODO: Refactor this into several widgets
|
||||||
class DocumentDetailsPage extends StatefulWidget {
|
class DocumentDetailsPage extends StatefulWidget {
|
||||||
@@ -49,7 +45,7 @@ class DocumentDetailsPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||||
late Future<DocumentMetaData> _metaData;
|
late Future<DocumentMetaData> _metaData;
|
||||||
|
static const double _itemPadding = 24;
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -70,7 +66,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
length: 4,
|
length: 4,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
|
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
|
||||||
floatingActionButton: widget.allowEdit ? _buildAppBar() : null,
|
floatingActionButton: widget.allowEdit ? _buildEditButton() : null,
|
||||||
bottomNavigationBar: _buildBottomAppBar(),
|
bottomNavigationBar: _buildBottomAppBar(),
|
||||||
body: NestedScrollView(
|
body: NestedScrollView(
|
||||||
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||||
@@ -96,27 +92,30 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
child: Text(
|
child: Text(
|
||||||
S.of(context).documentDetailsPageTabOverviewLabel,
|
S.of(context).documentDetailsPageTabOverviewLabel,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.onPrimaryContainer),
|
.onPrimaryContainer,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Tab(
|
Tab(
|
||||||
child: Text(
|
child: Text(
|
||||||
S.of(context).documentDetailsPageTabContentLabel,
|
S.of(context).documentDetailsPageTabContentLabel,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.onPrimaryContainer),
|
.onPrimaryContainer,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Tab(
|
Tab(
|
||||||
child: Text(
|
child: Text(
|
||||||
S.of(context).documentDetailsPageTabMetaDataLabel,
|
S.of(context).documentDetailsPageTabMetaDataLabel,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.onPrimaryContainer),
|
.onPrimaryContainer,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Tab(
|
Tab(
|
||||||
@@ -146,20 +145,26 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
),
|
),
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
children: [
|
children: [
|
||||||
_buildDocumentOverview(
|
DocumentOverviewWidget(
|
||||||
state.document,
|
document: state.document,
|
||||||
|
itemSpacing: _itemPadding,
|
||||||
|
queryString: widget.titleAndContentQueryString,
|
||||||
),
|
),
|
||||||
_buildDocumentContentView(
|
DocumentContentWidget(
|
||||||
state.document,
|
isFullContentLoaded: state.isFullContentLoaded,
|
||||||
state,
|
document: state.document,
|
||||||
|
fullContent: state.fullContent,
|
||||||
|
queryString: widget.titleAndContentQueryString,
|
||||||
),
|
),
|
||||||
_buildDocumentMetaDataView(
|
DocumentMetaDataWidget(
|
||||||
state.document,
|
document: state.document,
|
||||||
|
itemSpacing: _itemPadding,
|
||||||
|
metaData: _metaData,
|
||||||
),
|
),
|
||||||
const SimilarDocumentsView(),
|
const SimilarDocumentsView(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
).paddedSymmetrically(horizontal: 8);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -168,7 +173,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlocBuilder<DocumentDetailsCubit, DocumentDetailsState> _buildAppBar() {
|
Widget _buildEditButton() {
|
||||||
return BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>(
|
return BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final _filteredSuggestions =
|
final _filteredSuggestions =
|
||||||
@@ -176,7 +181,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||||
builder: (context, connectivityState) {
|
builder: (context, connectivityState) {
|
||||||
if (!connectivityState.isConnected) {
|
if (!connectivityState.isConnected) {
|
||||||
return Container();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
return b.Badge(
|
return b.Badge(
|
||||||
position: b.BadgePosition.topEnd(top: -12, end: -6),
|
position: b.BadgePosition.topEnd(top: -12, end: -6),
|
||||||
@@ -244,8 +249,10 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
tooltip: S.of(context).documentDetailsPageShareTooltip,
|
tooltip: S.of(context).documentDetailsPageShareTooltip,
|
||||||
icon: const Icon(Icons.share),
|
icon: const Icon(Icons.share),
|
||||||
onPressed:
|
onPressed: isConnected
|
||||||
isConnected ? () => _onShare(state.document) : null,
|
? () =>
|
||||||
|
context.read<DocumentDetailsCubit>().shareDocument()
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -317,203 +324,6 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDocumentMetaDataView(DocumentModel document) {
|
|
||||||
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
if (!state.isConnected) {
|
|
||||||
return const Center(
|
|
||||||
child: OfflineWidget(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return FutureBuilder<DocumentMetaData>(
|
|
||||||
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<void> _assignAsn(DocumentModel document) async {
|
|
||||||
try {
|
|
||||||
await context.read<DocumentDetailsCubit>().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<DocumentDetailsCubit>().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<DocumentType>(
|
|
||||||
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<Correspondent>(
|
|
||||||
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<void> _onShare(DocumentModel document) async {
|
|
||||||
Uint8List documentBytes =
|
|
||||||
await context.read<PaperlessDocumentsApi>().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 {
|
void _onDelete(DocumentModel document) async {
|
||||||
final delete = await showDialog(
|
final delete = await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -546,42 +356,6 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
class ColoredTabBar extends Container implements PreferredSizeWidget {
|
||||||
ColoredTabBar({
|
ColoredTabBar({
|
||||||
super.key,
|
super.key,
|
||||||
|
|||||||
34
lib/features/document_details/view/widgets/details_item.dart
Normal file
34
lib/features/document_details/view/widgets/details_item.dart
Normal file
@@ -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,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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<DocumentDetailsCubit>().loadFullContent();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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/extensions/flutter_extensions.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/constants.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class DocumentDownloadButton extends StatefulWidget {
|
class DocumentDownloadButton extends StatefulWidget {
|
||||||
|
|||||||
@@ -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<DocumentMetaData> 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<ConnectivityCubit, ConnectivityState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (!state.isConnected) {
|
||||||
|
return const Center(
|
||||||
|
child: OfflineWidget(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return FutureBuilder<DocumentMetaData>(
|
||||||
|
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<void> _assignAsn(BuildContext context) async {
|
||||||
|
try {
|
||||||
|
await context.read<DocumentDetailsCubit>().assignAsn(document);
|
||||||
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
|
showErrorMessage(context, error, stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<DocumentType>(
|
||||||
|
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<Correspondent>(
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,17 +2,17 @@ import 'package:collection/collection.dart';
|
|||||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.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:equatable/equatable.dart';
|
||||||
import 'package:json_annotation/json_annotation.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_state.dart';
|
||||||
|
|
||||||
part 'document_search_cubit.g.dart';
|
part 'document_search_cubit.g.dart';
|
||||||
|
|
||||||
class DocumentSearchCubit extends HydratedCubit<DocumentSearchState>
|
class DocumentSearchCubit extends HydratedCubit<DocumentSearchState>
|
||||||
with PagedDocumentsMixin {
|
with DocumentPagingBlocMixin {
|
||||||
@override
|
@override
|
||||||
final PaperlessDocumentsApi api;
|
final PaperlessDocumentsApi api;
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ enum SearchView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable(ignoreUnannotated: true)
|
@JsonSerializable(ignoreUnannotated: true)
|
||||||
class DocumentSearchState extends PagedDocumentsState {
|
class DocumentSearchState extends DocumentPagingState {
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
final List<String> searchHistory;
|
final List<String> searchHistory;
|
||||||
final SearchView view;
|
final SearchView view;
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.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:paperless_mobile/features/settings/model/view_type.dart';
|
||||||
import 'package:json_annotation/json_annotation.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_state.dart';
|
||||||
part 'documents_cubit.g.dart';
|
part 'documents_cubit.g.dart';
|
||||||
|
|
||||||
class DocumentsCubit extends HydratedCubit<DocumentsState>
|
class DocumentsCubit extends HydratedCubit<DocumentsState>
|
||||||
with PagedDocumentsMixin {
|
with DocumentPagingBlocMixin {
|
||||||
@override
|
@override
|
||||||
final PaperlessDocumentsApi api;
|
final PaperlessDocumentsApi api;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
part of 'documents_cubit.dart';
|
part of 'documents_cubit.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class DocumentsState extends PagedDocumentsState {
|
class DocumentsState extends DocumentPagingState {
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
final List<DocumentModel> selection;
|
final List<DocumentModel> selection;
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/widgets/empty_state.dart';
|
import 'package:paperless_mobile/core/widgets/empty_state.dart';
|
||||||
import 'package:paperless_mobile/extensions/flutter_extensions.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';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
|
||||||
class DocumentsEmptyState extends StatelessWidget {
|
class DocumentsEmptyState extends StatelessWidget {
|
||||||
final PagedDocumentsState state;
|
final DocumentPagingState state;
|
||||||
final VoidCallback? onReset;
|
final VoidCallback? onReset;
|
||||||
const DocumentsEmptyState({
|
const DocumentsEmptyState({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
|||||||
@@ -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/cubit/label_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/labels/view/pages/labels_page.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/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/saved_view/cubit/saved_view_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/sharing/share_intent_queue.dart';
|
import 'package:paperless_mobile/features/sharing/share_intent_queue.dart';
|
||||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||||
@@ -271,8 +272,18 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
|||||||
],
|
],
|
||||||
child: const LabelsPage(),
|
child: const LabelsPage(),
|
||||||
),
|
),
|
||||||
BlocProvider.value(
|
MultiBlocProvider(
|
||||||
value: _inboxCubit,
|
providers: [
|
||||||
|
// We need to manually downcast the inboxcubit to the
|
||||||
|
// mixed-in DocumentPagingBlocMixin to use the
|
||||||
|
// DocumentPagingViewMixin in the inbox.
|
||||||
|
BlocProvider<DocumentPagingBlocMixin>.value(
|
||||||
|
value: _inboxCubit,
|
||||||
|
),
|
||||||
|
BlocProvider<InboxCubit>.value(
|
||||||
|
value: _inboxCubit,
|
||||||
|
),
|
||||||
|
],
|
||||||
child: const InboxPage(),
|
child: const InboxPage(),
|
||||||
),
|
),
|
||||||
// const SettingsPage(),
|
// const SettingsPage(),
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ import 'package:json_annotation/json_annotation.dart';
|
|||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||||
import 'package:paperless_mobile/core/repository/label_repository.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/cubit/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/document_paging_bloc_mixin.dart';
|
||||||
|
|
||||||
part 'inbox_cubit.g.dart';
|
part 'inbox_cubit.g.dart';
|
||||||
part 'inbox_state.dart';
|
part 'inbox_state.dart';
|
||||||
|
|
||||||
class InboxCubit extends HydratedCubit<InboxState> with PagedDocumentsMixin {
|
class InboxCubit extends HydratedCubit<InboxState>
|
||||||
|
with DocumentPagingBlocMixin {
|
||||||
final LabelRepository<Tag> _tagsRepository;
|
final LabelRepository<Tag> _tagsRepository;
|
||||||
final LabelRepository<Correspondent> _correspondentRepository;
|
final LabelRepository<Correspondent> _correspondentRepository;
|
||||||
final LabelRepository<DocumentType> _documentTypeRepository;
|
final LabelRepository<DocumentType> _documentTypeRepository;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
part of 'inbox_cubit.dart';
|
part of 'inbox_cubit.dart';
|
||||||
|
|
||||||
@JsonSerializable(ignoreUnannotated: true)
|
@JsonSerializable(ignoreUnannotated: true)
|
||||||
class InboxState extends PagedDocumentsState {
|
class InboxState extends DocumentPagingState {
|
||||||
final Iterable<int> inboxTags;
|
final Iterable<int> inboxTags;
|
||||||
|
|
||||||
final Map<int, Tag> availableTags;
|
final Map<int, Tag> availableTags;
|
||||||
|
|||||||
@@ -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/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_empty_widget.dart';
|
||||||
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_item.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/features/search_app_bar/view/search_app_bar.dart';
|
||||||
import 'package:paperless_mobile/generated/l10n.dart';
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
@@ -23,35 +24,15 @@ class InboxPage extends StatefulWidget {
|
|||||||
State<InboxPage> createState() => _InboxPageState();
|
State<InboxPage> createState() => _InboxPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InboxPageState extends State<InboxPage> {
|
class _InboxPageState extends State<InboxPage> with DocumentPagingViewMixin {
|
||||||
final ScrollController _scrollController = ScrollController();
|
@override
|
||||||
|
final pagingScrollController = ScrollController();
|
||||||
final _emptyStateRefreshIndicatorKey = GlobalKey<RefreshIndicatorState>();
|
final _emptyStateRefreshIndicatorKey = GlobalKey<RefreshIndicatorState>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
context.read<InboxCubit>().loadInbox();
|
context.read<InboxCubit>().loadInbox();
|
||||||
_scrollController.addListener(_listenForLoadNewData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_scrollController.removeListener(_listenForLoadNewData);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _listenForLoadNewData() {
|
|
||||||
final currState = context.read<InboxCubit>().state;
|
|
||||||
if (_scrollController.offset >=
|
|
||||||
_scrollController.position.maxScrollExtent * 0.75 &&
|
|
||||||
!currState.isLoading &&
|
|
||||||
!currState.isLastPageLoaded) {
|
|
||||||
try {
|
|
||||||
context.read<InboxCubit>().loadMore();
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -143,7 +124,7 @@ class _InboxPageState extends State<InboxPage> {
|
|||||||
physics: state.documents.isEmpty
|
physics: state.documents.isEmpty
|
||||||
? const NeverScrollableScrollPhysics()
|
? const NeverScrollableScrollPhysics()
|
||||||
: const AlwaysScrollableScrollPhysics(),
|
: const AlwaysScrollableScrollPhysics(),
|
||||||
controller: _scrollController,
|
controller: pagingScrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
SearchAppBar(
|
SearchAppBar(
|
||||||
hintText: S.of(context).documentSearchSearchDocuments,
|
hintText: S.of(context).documentSearchSearchDocuments,
|
||||||
|
|||||||
@@ -2,15 +2,15 @@ import 'package:hydrated_bloc/hydrated_bloc.dart';
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.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/cubit/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/document_paging_bloc_mixin.dart';
|
||||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||||
part 'linked_documents_state.dart';
|
part 'linked_documents_state.dart';
|
||||||
|
|
||||||
part 'linked_documents_cubit.g.dart';
|
part 'linked_documents_cubit.g.dart';
|
||||||
|
|
||||||
class LinkedDocumentsCubit extends HydratedCubit<LinkedDocumentsState>
|
class LinkedDocumentsCubit extends HydratedCubit<LinkedDocumentsState>
|
||||||
with PagedDocumentsMixin {
|
with DocumentPagingBlocMixin {
|
||||||
@override
|
@override
|
||||||
final PaperlessDocumentsApi api;
|
final PaperlessDocumentsApi api;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
part of 'linked_documents_cubit.dart';
|
part of 'linked_documents_cubit.dart';
|
||||||
|
|
||||||
@JsonSerializable(ignoreUnannotated: true)
|
@JsonSerializable(ignoreUnannotated: true)
|
||||||
class LinkedDocumentsState extends PagedDocumentsState {
|
class LinkedDocumentsState extends DocumentPagingState {
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
final ViewType viewType;
|
final ViewType viewType;
|
||||||
const LinkedDocumentsState({
|
const LinkedDocumentsState({
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.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/bloc/connectivity_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.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/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/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/generated/l10n.dart';
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
|
||||||
import 'package:paperless_mobile/routes/document_details_route.dart';
|
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||||
|
|
||||||
class LinkedDocumentsPage extends StatefulWidget {
|
class LinkedDocumentsPage extends StatefulWidget {
|
||||||
@@ -16,28 +15,10 @@ class LinkedDocumentsPage extends StatefulWidget {
|
|||||||
State<LinkedDocumentsPage> createState() => _LinkedDocumentsPageState();
|
State<LinkedDocumentsPage> createState() => _LinkedDocumentsPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LinkedDocumentsPageState extends State<LinkedDocumentsPage> {
|
class _LinkedDocumentsPageState extends State<LinkedDocumentsPage>
|
||||||
final _scrollController = ScrollController();
|
with DocumentPagingViewMixin {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
final pagingScrollController = ScrollController();
|
||||||
super.initState();
|
|
||||||
_scrollController.addListener(_listenForLoadNewData);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _listenForLoadNewData() async {
|
|
||||||
final currState = context.read<LinkedDocumentsCubit>().state;
|
|
||||||
if (_scrollController.offset >=
|
|
||||||
_scrollController.position.maxScrollExtent * 0.75 &&
|
|
||||||
!currState.isLoading &&
|
|
||||||
!currState.isLastPageLoaded) {
|
|
||||||
try {
|
|
||||||
await context.read<LinkedDocumentsCubit>().loadMore();
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -60,7 +41,7 @@ class _LinkedDocumentsPageState extends State<LinkedDocumentsPage> {
|
|||||||
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||||
builder: (context, connectivity) {
|
builder: (context, connectivity) {
|
||||||
return CustomScrollView(
|
return CustomScrollView(
|
||||||
controller: _scrollController,
|
controller: pagingScrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverAdaptiveDocumentsView(
|
SliverAdaptiveDocumentsView(
|
||||||
viewType: state.viewType,
|
viewType: state.viewType,
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.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.
|
/// Mixin which can be used on cubits that handle documents.
|
||||||
/// This implements all paging and filtering logic.
|
/// This implements all paging and filtering logic.
|
||||||
///
|
///
|
||||||
mixin PagedDocumentsMixin<State extends PagedDocumentsState>
|
mixin DocumentPagingBlocMixin<State extends DocumentPagingState>
|
||||||
on BlocBase<State> {
|
on BlocBase<State> {
|
||||||
PaperlessDocumentsApi get api;
|
PaperlessDocumentsApi get api;
|
||||||
DocumentChangedNotifier get notifier;
|
DocumentChangedNotifier get notifier;
|
||||||
@@ -5,13 +5,13 @@ import 'package:paperless_api/paperless_api.dart';
|
|||||||
/// Base state for all blocs/cubits using a paged view of documents.
|
/// Base state for all blocs/cubits using a paged view of documents.
|
||||||
/// [T] is the return type of the API call.
|
/// [T] is the return type of the API call.
|
||||||
///
|
///
|
||||||
abstract class PagedDocumentsState extends Equatable {
|
abstract class DocumentPagingState extends Equatable {
|
||||||
final bool hasLoaded;
|
final bool hasLoaded;
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
final List<PagedSearchResult<DocumentModel>> value;
|
final List<PagedSearchResult<DocumentModel>> value;
|
||||||
final DocumentFilter filter;
|
final DocumentFilter filter;
|
||||||
|
|
||||||
const PagedDocumentsState({
|
const DocumentPagingState({
|
||||||
this.value = const [],
|
this.value = const [],
|
||||||
this.hasLoaded = false,
|
this.hasLoaded = false,
|
||||||
this.isLoading = false,
|
this.isLoading = false,
|
||||||
@@ -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<T extends StatefulWidget> on State<T> {
|
||||||
|
ScrollController get pagingScrollController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
pagingScrollController.addListener(shouldLoadMoreDocumentsListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
pagingScrollController.removeListener(shouldLoadMoreDocumentsListener);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
DocumentPagingBlocMixin get _bloc => context.read<DocumentPagingBlocMixin>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,15 +2,15 @@ import 'package:hydrated_bloc/hydrated_bloc.dart';
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.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/cubit/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/document_paging_bloc_mixin.dart';
|
||||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||||
|
|
||||||
part 'saved_view_details_cubit.g.dart';
|
part 'saved_view_details_cubit.g.dart';
|
||||||
part 'saved_view_details_state.dart';
|
part 'saved_view_details_state.dart';
|
||||||
|
|
||||||
class SavedViewDetailsCubit extends HydratedCubit<SavedViewDetailsState>
|
class SavedViewDetailsCubit extends HydratedCubit<SavedViewDetailsState>
|
||||||
with PagedDocumentsMixin {
|
with DocumentPagingBlocMixin {
|
||||||
@override
|
@override
|
||||||
final PaperlessDocumentsApi api;
|
final PaperlessDocumentsApi api;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
part of 'saved_view_details_cubit.dart';
|
part of 'saved_view_details_cubit.dart';
|
||||||
|
|
||||||
@JsonSerializable(ignoreUnannotated: true)
|
@JsonSerializable(ignoreUnannotated: true)
|
||||||
class SavedViewDetailsState extends PagedDocumentsState {
|
class SavedViewDetailsState extends DocumentPagingState {
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
final ViewType viewType;
|
final ViewType viewType;
|
||||||
|
|
||||||
|
|||||||
@@ -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/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/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/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/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';
|
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||||
|
|
||||||
class SavedViewDetailsPage extends StatefulWidget {
|
class SavedViewDetailsPage extends StatefulWidget {
|
||||||
@@ -21,33 +21,14 @@ class SavedViewDetailsPage extends StatefulWidget {
|
|||||||
State<SavedViewDetailsPage> createState() => _SavedViewDetailsPageState();
|
State<SavedViewDetailsPage> createState() => _SavedViewDetailsPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SavedViewDetailsPageState extends State<SavedViewDetailsPage> {
|
class _SavedViewDetailsPageState extends State<SavedViewDetailsPage>
|
||||||
final _scrollController = ScrollController();
|
with DocumentPagingViewMixin {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
final pagingScrollController = ScrollController();
|
||||||
super.initState();
|
|
||||||
_scrollController.addListener(_listenForLoadNewData);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _listenForLoadNewData() async {
|
|
||||||
final currState = context.read<SavedViewDetailsCubit>().state;
|
|
||||||
if (_scrollController.offset >=
|
|
||||||
_scrollController.position.maxScrollExtent * 0.7 &&
|
|
||||||
!currState.isLoading &&
|
|
||||||
!currState.isLastPageLoaded) {
|
|
||||||
try {
|
|
||||||
await context.read<SavedViewDetailsCubit>().loadMore();
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final cubit = context.read<SavedViewDetailsCubit>();
|
final cubit = context.read<SavedViewDetailsCubit>();
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(cubit.savedView.name),
|
title: Text(cubit.savedView.name),
|
||||||
@@ -57,8 +38,9 @@ class _SavedViewDetailsPageState extends State<SavedViewDetailsPage> {
|
|||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final shouldDelete = await showDialog<bool>(
|
final shouldDelete = await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) =>
|
builder: (context) => ConfirmDeleteSavedViewDialog(
|
||||||
ConfirmDeleteSavedViewDialog(view: cubit.savedView),
|
view: cubit.savedView,
|
||||||
|
),
|
||||||
) ??
|
) ??
|
||||||
false;
|
false;
|
||||||
if (shouldDelete) {
|
if (shouldDelete) {
|
||||||
@@ -85,7 +67,7 @@ class _SavedViewDetailsPageState extends State<SavedViewDetailsPage> {
|
|||||||
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||||
builder: (context, connectivity) {
|
builder: (context, connectivity) {
|
||||||
return CustomScrollView(
|
return CustomScrollView(
|
||||||
controller: _scrollController,
|
controller: pagingScrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverAdaptiveDocumentsView(
|
SliverAdaptiveDocumentsView(
|
||||||
documents: state.documents,
|
documents: state.documents,
|
||||||
@@ -93,7 +75,16 @@ class _SavedViewDetailsPageState extends State<SavedViewDetailsPage> {
|
|||||||
isLabelClickable: false,
|
isLabelClickable: false,
|
||||||
isLoading: state.isLoading,
|
isLoading: state.isLoading,
|
||||||
hasLoaded: state.hasLoaded,
|
hasLoaded: state.hasLoaded,
|
||||||
onTap: _onOpenDocumentDetails,
|
onTap: (document) {
|
||||||
|
Navigator.pushNamed(
|
||||||
|
context,
|
||||||
|
DocumentDetailsRoute.routeName,
|
||||||
|
arguments: DocumentDetailsRouteArguments(
|
||||||
|
document: document,
|
||||||
|
isLabelClickable: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
viewType: state.viewType,
|
viewType: state.viewType,
|
||||||
),
|
),
|
||||||
if (state.hasLoaded && state.isLoading)
|
if (state.hasLoaded && state.isLoading)
|
||||||
@@ -110,15 +101,4 @@ class _SavedViewDetailsPageState extends State<SavedViewDetailsPage> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onOpenDocumentDetails(DocumentModel document) {
|
|
||||||
Navigator.pushNamed(
|
|
||||||
context,
|
|
||||||
DocumentDetailsRoute.routeName,
|
|
||||||
arguments: DocumentDetailsRouteArguments(
|
|
||||||
document: document,
|
|
||||||
isLabelClickable: false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.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/paged_document_view/model/paged_documents_state.dart';
|
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
|
||||||
|
|
||||||
part 'similar_documents_state.dart';
|
part 'similar_documents_state.dart';
|
||||||
|
|
||||||
class SimilarDocumentsCubit extends Cubit<SimilarDocumentsState>
|
class SimilarDocumentsCubit extends Cubit<SimilarDocumentsState>
|
||||||
with PagedDocumentsMixin {
|
with DocumentPagingBlocMixin {
|
||||||
final int documentId;
|
final int documentId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
part of 'similar_documents_cubit.dart';
|
part of 'similar_documents_cubit.dart';
|
||||||
|
|
||||||
class SimilarDocumentsState extends PagedDocumentsState {
|
class SimilarDocumentsState extends DocumentPagingState {
|
||||||
const SimilarDocumentsState({
|
const SimilarDocumentsState({
|
||||||
super.filter,
|
super.filter,
|
||||||
super.hasLoaded,
|
super.hasLoaded,
|
||||||
|
|||||||
@@ -2,10 +2,9 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:paperless_api/paperless_api.dart';
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.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/adaptive_documents_view.dart';
|
||||||
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.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/features/similar_documents/cubit/similar_documents_cubit.dart';
|
||||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||||
import 'package:paperless_mobile/routes/document_details_route.dart';
|
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||||
@@ -17,13 +16,14 @@ class SimilarDocumentsView extends StatefulWidget {
|
|||||||
State<SimilarDocumentsView> createState() => _SimilarDocumentsViewState();
|
State<SimilarDocumentsView> createState() => _SimilarDocumentsViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SimilarDocumentsViewState extends State<SimilarDocumentsView> {
|
class _SimilarDocumentsViewState extends State<SimilarDocumentsView>
|
||||||
final _scrollController = ScrollController();
|
with DocumentPagingViewMixin {
|
||||||
|
@override
|
||||||
|
final pagingScrollController = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_scrollController.addListener(_listenForLoadNewData);
|
|
||||||
try {
|
try {
|
||||||
context.read<SimilarDocumentsCubit>().initialize();
|
context.read<SimilarDocumentsCubit>().initialize();
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
} on PaperlessServerException catch (error, stackTrace) {
|
||||||
@@ -31,26 +31,6 @@ class _SimilarDocumentsViewState extends State<SimilarDocumentsView> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_scrollController.removeListener(_listenForLoadNewData);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _listenForLoadNewData() async {
|
|
||||||
final currState = context.read<SimilarDocumentsCubit>().state;
|
|
||||||
if (_scrollController.offset >=
|
|
||||||
_scrollController.position.maxScrollExtent * 0.75 &&
|
|
||||||
!currState.isLoading &&
|
|
||||||
!currState.isLastPageLoaded) {
|
|
||||||
try {
|
|
||||||
await context.read<SimilarDocumentsCubit>().loadMore();
|
|
||||||
} on PaperlessServerException catch (error, stackTrace) {
|
|
||||||
showErrorMessage(context, error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<SimilarDocumentsCubit, SimilarDocumentsState>(
|
return BlocBuilder<SimilarDocumentsCubit, SimilarDocumentsState>(
|
||||||
@@ -70,7 +50,7 @@ class _SimilarDocumentsViewState extends State<SimilarDocumentsView> {
|
|||||||
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||||
builder: (context, connectivity) {
|
builder: (context, connectivity) {
|
||||||
return CustomScrollView(
|
return CustomScrollView(
|
||||||
controller: _scrollController,
|
controller: pagingScrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverAdaptiveDocumentsView(
|
SliverAdaptiveDocumentsView(
|
||||||
documents: state.documents,
|
documents: state.documents,
|
||||||
|
|||||||
Reference in New Issue
Block a user