mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-07 18:07:44 -06:00
More work on inbox, refactorings (bloc separation of concerns), fixed saved views wrong sort order
This commit is contained in:
@@ -1,494 +0,0 @@
|
||||
import 'dart:developer' as dev;
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/global_state_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/core/logic/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/core/widgets/highlighted_text.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document_meta_data.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/pages/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/correspondent/view/widgets/correspondent_widget.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/view/widgets/document_type_widget.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_widget.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
class DocumentDetailsPage extends StatefulWidget {
|
||||
final int documentId;
|
||||
final bool allowEdit;
|
||||
final bool isLabelClickable;
|
||||
|
||||
const DocumentDetailsPage({
|
||||
Key? key,
|
||||
required this.documentId,
|
||||
this.allowEdit = true,
|
||||
this.isLabelClickable = true,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<DocumentDetailsPage> createState() => _DocumentDetailsPageState();
|
||||
}
|
||||
|
||||
class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
static final DateFormat _detailedDateFormat =
|
||||
DateFormat("MMM d, yyyy HH:mm:ss");
|
||||
|
||||
bool _isDownloadPending = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
// buildWhen required because rebuild would happen after delete causing error.
|
||||
buildWhen: (previous, current) {
|
||||
return current.documents
|
||||
.where((element) => element.id == widget.documentId)
|
||||
.isNotEmpty;
|
||||
},
|
||||
builder: (context, state) {
|
||||
final document =
|
||||
state.documents.where((doc) => doc.id == widget.documentId).first;
|
||||
return DefaultTabController(
|
||||
length: 3,
|
||||
child: Scaffold(
|
||||
floatingActionButtonLocation:
|
||||
FloatingActionButtonLocation.endDocked,
|
||||
floatingActionButton: widget.allowEdit
|
||||
? FloatingActionButton(
|
||||
child: const Icon(Icons.edit),
|
||||
onPressed: () => _onEdit(document),
|
||||
)
|
||||
: null,
|
||||
bottomNavigationBar: BottomAppBar(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed:
|
||||
widget.allowEdit ? () => _onDelete(document) : null,
|
||||
).padded(const EdgeInsets.symmetric(horizontal: 4)),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.download),
|
||||
onPressed:
|
||||
Platform.isAndroid ? () => _onDownload(document) : null,
|
||||
).padded(const EdgeInsets.only(right: 4)),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.open_in_new),
|
||||
onPressed: () => _onOpen(document),
|
||||
).padded(const EdgeInsets.only(right: 4)),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.share),
|
||||
onPressed: () => _onShare(document),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||
SliverAppBar(
|
||||
leading: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.arrow_back,
|
||||
color: Colors
|
||||
.black, //TODO: check if there is a way to dynamically determine color...
|
||||
),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
floating: true,
|
||||
pinned: true,
|
||||
expandedHeight: 200.0,
|
||||
flexibleSpace: DocumentPreview(
|
||||
id: document.id,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
bottom: ColoredTabBar(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.primaryContainer,
|
||||
tabBar: TabBar(
|
||||
tabs: [
|
||||
Tab(
|
||||
child: Text(
|
||||
S.of(context).documentDetailsPageTabOverviewLabel,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer),
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
child: Text(
|
||||
S.of(context).documentDetailsPageTabContentLabel,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer),
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
child: Text(
|
||||
S.of(context).documentDetailsPageTabMetaDataLabel,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
body: TabBarView(
|
||||
children: [
|
||||
_buildDocumentOverview(
|
||||
document, state.filter.titleAndContentMatchString),
|
||||
_buildDocumentContentView(
|
||||
document, state.filter.titleAndContentMatchString),
|
||||
_buildDocumentMetaDataView(document),
|
||||
].padded(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDocumentMetaDataView(DocumentModel document) {
|
||||
return FutureBuilder<DocumentMetaData>(
|
||||
future: getIt<DocumentRepository>().getMetaData(document),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
final meta = snapshot.data!;
|
||||
return ListView(
|
||||
children: [
|
||||
_DetailsItem.text(_detailedDateFormat.format(document.modified),
|
||||
label: S.of(context).documentModifiedPropertyLabel,
|
||||
context: context),
|
||||
_separator(),
|
||||
_DetailsItem.text(_detailedDateFormat.format(document.added),
|
||||
label: S.of(context).documentAddedPropertyLabel,
|
||||
context: context),
|
||||
_separator(),
|
||||
_DetailsItem(
|
||||
label: S.of(context).documentArchiveSerialNumberPropertyLongLabel,
|
||||
content: document.archiveSerialNumber != null
|
||||
? Text(document.archiveSerialNumber.toString())
|
||||
: OutlinedButton(
|
||||
child: Text(S
|
||||
.of(context)
|
||||
.documentDetailsPageAssignAsnButtonLabel),
|
||||
onPressed:
|
||||
widget.allowEdit ? () => _assignAsn(document) : null,
|
||||
),
|
||||
),
|
||||
_separator(),
|
||||
_DetailsItem.text(
|
||||
meta.mediaFilename,
|
||||
context: context,
|
||||
label: S.of(context).documentMetaDataMediaFilenamePropertyLabel,
|
||||
),
|
||||
_separator(),
|
||||
_DetailsItem.text(
|
||||
meta.originalChecksum,
|
||||
context: context,
|
||||
label: S.of(context).documentMetaDataChecksumLabel,
|
||||
),
|
||||
_separator(),
|
||||
_DetailsItem.text(formatBytes(meta.originalSize, 2),
|
||||
label: S.of(context).documentMetaDataOriginalFileSizeLabel,
|
||||
context: context),
|
||||
_separator(),
|
||||
_DetailsItem.text(
|
||||
meta.originalMimeType,
|
||||
label: S.of(context).documentMetaDataOriginalMimeTypeLabel,
|
||||
context: context,
|
||||
),
|
||||
_separator(),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _assignAsn(DocumentModel document) async {
|
||||
try {
|
||||
await BlocProvider.of<DocumentsCubit>(context).assignAsn(document);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildDocumentContentView(DocumentModel document, String? match) {
|
||||
return SingleChildScrollView(
|
||||
child: _DetailsItem(
|
||||
content: HighlightedText(
|
||||
text: document.content ?? "",
|
||||
highlights: match == null ? [] : match.split(" "),
|
||||
style: Theme.of(context).textTheme.bodyText2,
|
||||
caseSensitive: false,
|
||||
),
|
||||
label: S.of(context).documentDetailsPageTabContentLabel,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDocumentOverview(DocumentModel document, String? match) {
|
||||
return ListView(
|
||||
children: [
|
||||
_DetailsItem(
|
||||
content: HighlightedText(
|
||||
text: document.title,
|
||||
highlights: match?.split(" ") ?? <String>[],
|
||||
),
|
||||
label: S.of(context).documentTitlePropertyLabel,
|
||||
),
|
||||
_separator(),
|
||||
_DetailsItem.text(
|
||||
DateFormat.yMMMd(Localizations.localeOf(context).toLanguageTag())
|
||||
.format(document.created),
|
||||
context: context,
|
||||
label: S.of(context).documentCreatedPropertyLabel,
|
||||
),
|
||||
_separator(),
|
||||
_DetailsItem(
|
||||
content: DocumentTypeWidget(
|
||||
isClickable: widget.isLabelClickable,
|
||||
documentTypeId: document.documentType,
|
||||
afterSelected: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
label: S.of(context).documentDocumentTypePropertyLabel,
|
||||
),
|
||||
_separator(),
|
||||
_DetailsItem(
|
||||
label: S.of(context).documentCorrespondentPropertyLabel,
|
||||
content: CorrespondentWidget(
|
||||
isClickable: widget.isLabelClickable,
|
||||
correspondentId: document.correspondent,
|
||||
afterSelected: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
_separator(),
|
||||
_DetailsItem(
|
||||
label: S.of(context).documentStoragePathPropertyLabel,
|
||||
content: StoragePathWidget(
|
||||
isClickable: widget.isLabelClickable,
|
||||
pathId: document.storagePath,
|
||||
afterSelected: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
_separator(),
|
||||
_DetailsItem(
|
||||
label: S.of(context).documentTagsPropertyLabel,
|
||||
content: Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: TagsWidget(
|
||||
isClickable: widget.isLabelClickable,
|
||||
tagIds: document.tags,
|
||||
),
|
||||
),
|
||||
),
|
||||
// _separator(),
|
||||
// FutureBuilder<List<SimilarDocumentModel>>(
|
||||
// future: getIt<DocumentRepository>().findSimilar(document.id),
|
||||
// builder: (context, snapshot) {
|
||||
// if (!snapshot.hasData) {
|
||||
// return CircularProgressIndicator();
|
||||
// }
|
||||
// return ExpansionTile(
|
||||
// tilePadding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
// title: Text(
|
||||
// S.of(context).documentDetailsPageSimilarDocumentsLabel,
|
||||
// style:
|
||||
// Theme.of(context).textTheme.headline5?.copyWith(fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
// children: snapshot.data!
|
||||
// .map((e) => DocumentListItem(
|
||||
// document: e,
|
||||
// onTap: (doc) {},
|
||||
// isSelected: false,
|
||||
// isAtLeastOneSelected: false))
|
||||
// .toList(),
|
||||
// );
|
||||
// }),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _separator() {
|
||||
return const SizedBox(height: 32.0);
|
||||
}
|
||||
|
||||
void _onEdit(DocumentModel document) async {
|
||||
final wasUpdated = await Navigator.push<bool>(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => GlobalStateBlocProvider(
|
||||
child: DocumentEditPage(document: document),
|
||||
),
|
||||
maintainState: true,
|
||||
),
|
||||
) ??
|
||||
false;
|
||||
if (wasUpdated) {
|
||||
BlocProvider.of<PaperlessStatisticsCubit>(context).updateStatistics();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onDownload(DocumentModel document) async {
|
||||
if (!Platform.isAndroid) {
|
||||
showSnackBar(
|
||||
context, "This feature is currently only supported on Android!");
|
||||
return;
|
||||
}
|
||||
setState(() => _isDownloadPending = true);
|
||||
getIt<DocumentRepository>().download(document).then((bytes) async {
|
||||
final Directory dir = (await getExternalStorageDirectories(
|
||||
type: StorageDirectory.downloads))!
|
||||
.first;
|
||||
String filePath = "${dir.path}/${document.originalFileName}";
|
||||
//TODO: Add replacement mechanism here (ask user if file should be replaced if exists)
|
||||
await File(filePath).writeAsBytes(bytes);
|
||||
setState(() => _isDownloadPending = false);
|
||||
dev.log("File downloaded to $filePath");
|
||||
});
|
||||
}
|
||||
|
||||
///
|
||||
/// Downloads file to temporary directory, from which it can then be shared.
|
||||
///
|
||||
Future<void> _onShare(DocumentModel document) async {
|
||||
Uint8List documentBytes =
|
||||
await getIt<DocumentRepository>().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,
|
||||
builder: (context) =>
|
||||
DeleteDocumentConfirmationDialog(document: document),
|
||||
) ??
|
||||
false;
|
||||
if (delete) {
|
||||
try {
|
||||
await BlocProvider.of<DocumentsCubit>(context).remove(document);
|
||||
showSnackBar(context, S.of(context).documentDeleteSuccessMessage);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
} finally {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onOpen(DocumentModel document) async {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => DocumentView(
|
||||
documentBytes: getIt<DocumentRepository>().getPreview(document.id),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static String formatBytes(int bytes, int decimals) {
|
||||
if (bytes <= 0) return "0 B";
|
||||
const suffixes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
var i = (log(bytes) / log(1024)).floor();
|
||||
return ((bytes / pow(1024, i)).toStringAsFixed(decimals)) +
|
||||
' ' +
|
||||
suffixes[i];
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
.headline5
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
content,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_DetailsItem.text(
|
||||
String text, {
|
||||
required this.label,
|
||||
required BuildContext context,
|
||||
}) : content = Text(text, style: Theme.of(context).textTheme.bodyText2);
|
||||
}
|
||||
|
||||
class ColoredTabBar extends Container implements PreferredSizeWidget {
|
||||
ColoredTabBar({
|
||||
super.key,
|
||||
required this.backgroundColor,
|
||||
required this.tabBar,
|
||||
});
|
||||
|
||||
final TabBar tabBar;
|
||||
final Color backgroundColor;
|
||||
@override
|
||||
Size get preferredSize => tabBar.preferredSize;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Container(
|
||||
color: backgroundColor,
|
||||
child: tabBar,
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -33,7 +34,13 @@ import 'package:paperless_mobile/util.dart';
|
||||
|
||||
class DocumentEditPage extends StatefulWidget {
|
||||
final DocumentModel document;
|
||||
const DocumentEditPage({Key? key, required this.document}) : super(key: key);
|
||||
final FutureOr<void> Function(DocumentModel updatedDocument) onEdit;
|
||||
|
||||
const DocumentEditPage({
|
||||
Key? key,
|
||||
required this.document,
|
||||
required this.onEdit,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<DocumentEditPage> createState() => _DocumentEditPageState();
|
||||
@@ -66,7 +73,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||
final values = _formKey.currentState!.value;
|
||||
final updatedDocument = widget.document.copyWith(
|
||||
var updatedDocument = widget.document.copyWith(
|
||||
title: values[fkTitle],
|
||||
created: values[fkCreatedDate],
|
||||
overwriteDocumentType: true,
|
||||
@@ -81,15 +88,17 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
setState(() {
|
||||
_isSubmitLoading = true;
|
||||
});
|
||||
bool wasUpdated = false;
|
||||
|
||||
try {
|
||||
await getIt<DocumentsCubit>().update(updatedDocument);
|
||||
showSnackBar(context, S.of(context).documentUpdateErrorMessage);
|
||||
wasUpdated = true;
|
||||
await widget.onEdit(updatedDocument);
|
||||
showSnackBar(context, S.of(context).documentUpdateSuccessMessage);
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
} finally {
|
||||
Navigator.pop(context, wasUpdated);
|
||||
setState(() {
|
||||
_isSubmitLoading = false;
|
||||
});
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,12 +2,15 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/core/bloc/paperless_statistics_cubit.dart';
|
||||
import 'package:paperless_mobile/core/model/error_message.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/document_details/bloc/document_details_cubit.dart';
|
||||
import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/document.model.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/pages/document_details_page.dart';
|
||||
import 'package:paperless_mobile/features/documents/model/query_parameters/tags_query.dart';
|
||||
import 'package:paperless_mobile/features/documents/repository/document_repository.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/grid/document_grid.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list.dart';
|
||||
@@ -160,22 +163,25 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
switch (settings.preferredViewType) {
|
||||
case ViewType.list:
|
||||
child = DocumentListView(
|
||||
onTap: _openDocumentDetails,
|
||||
onTap: _openDetails,
|
||||
state: state,
|
||||
onSelected: _onSelected,
|
||||
pagingController: _pagingController,
|
||||
hasInternetConnection:
|
||||
connectivityState == ConnectivityState.connected,
|
||||
onTagSelected: _addTagToFilter,
|
||||
);
|
||||
break;
|
||||
case ViewType.grid:
|
||||
child = DocumentGridView(
|
||||
onTap: _openDocumentDetails,
|
||||
state: state,
|
||||
onSelected: _onSelected,
|
||||
pagingController: _pagingController,
|
||||
hasInternetConnection:
|
||||
connectivityState == ConnectivityState.connected);
|
||||
onTap: _openDetails,
|
||||
state: state,
|
||||
onSelected: _onSelected,
|
||||
pagingController: _pagingController,
|
||||
hasInternetConnection:
|
||||
connectivityState == ConnectivityState.connected,
|
||||
onTagSelected: (int tagId) => _addTagToFilter,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -222,26 +228,63 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
);
|
||||
}
|
||||
|
||||
void _openDocumentDetails(DocumentModel model) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider.value(value: BlocProvider.of<DocumentsCubit>(context)),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<CorrespondentCubit>(context)),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<DocumentTypeCubit>(context)),
|
||||
BlocProvider.value(value: BlocProvider.of<TagCubit>(context)),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<StoragePathCubit>(context)),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<PaperlessStatisticsCubit>(context)),
|
||||
],
|
||||
child: DocumentDetailsPage(documentId: model.id),
|
||||
),
|
||||
Future<void> _openDetails(DocumentModel document) async {
|
||||
await Navigator.of(context).push<DocumentModel?>(
|
||||
_buildDetailsPageRoute(document),
|
||||
);
|
||||
BlocProvider.of<DocumentsCubit>(context).reload();
|
||||
}
|
||||
|
||||
MaterialPageRoute<DocumentModel?> _buildDetailsPageRoute(
|
||||
DocumentModel document) {
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<DocumentsCubit>(context),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<CorrespondentCubit>(context),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<DocumentTypeCubit>(context),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<TagCubit>(context),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<StoragePathCubit>(context),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: DocumentDetailsCubit(getIt<DocumentRepository>(), document),
|
||||
),
|
||||
],
|
||||
child: const DocumentDetailsPage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _addTagToFilter(int tagId) {
|
||||
final cubit = BlocProvider.of<DocumentsCubit>(context);
|
||||
try {
|
||||
final tagsQuery = cubit.state.filter.tags is IdsTagsQuery
|
||||
? cubit.state.filter.tags as IdsTagsQuery
|
||||
: const IdsTagsQuery();
|
||||
if (tagsQuery.includedIds.contains(tagId)) {
|
||||
cubit.updateCurrentFilter(
|
||||
(filter) => filter.copyWith(
|
||||
tags: tagsQuery.withIdsRemoved([tagId]),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
cubit.updateCurrentFilter(
|
||||
(filter) => filter.copyWith(
|
||||
tags: tagsQuery.withIdQueriesAdded([IncludeTagIdQuery(tagId)]),
|
||||
),
|
||||
);
|
||||
}
|
||||
} on ErrorMessage catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user