mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-08 10:07:51 -06:00
Merge branch 'main' into download_to_public
This commit is contained in:
@@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/constants.dart';
|
||||
import 'package:paperless_mobile/core/widgets/paperless_logo.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/settings_page.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:url_launcher/link.dart';
|
||||
|
||||
@@ -7,6 +7,8 @@ import 'package:open_filex/open_filex.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||
import 'package:paperless_mobile/core/service/file_service.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
part 'document_details_state.dart';
|
||||
|
||||
@@ -73,6 +75,24 @@ class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
|
||||
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
|
||||
Future<void> close() {
|
||||
for (final element in _subscriptions) {
|
||||
@@ -4,31 +4,27 @@ import 'package:badges/badges.dart' as b;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:open_filex/open_filex.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/core/translation/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/widgets/highlighted_text.dart';
|
||||
import 'package:paperless_mobile/core/widgets/offline_widget.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/document_details/bloc/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/documents/view/pages/document_edit_page.dart';
|
||||
import 'package:paperless_mobile/features/document_details/view/widgets/document_meta_data_widget.dart';
|
||||
import 'package:paperless_mobile/features/document_details/view/widgets/document_overview_widget.dart';
|
||||
import 'package:paperless_mobile/features/document_edit/cubit/document_edit_cubit.dart';
|
||||
import 'package:paperless_mobile/features/document_edit/view/document_edit_page.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/pages/document_view.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/delete_document_confirmation_dialog.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart';
|
||||
import 'package:paperless_mobile/features/edit_document/cubit/edit_document_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/view/widgets/storage_path_widget.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_text.dart';
|
||||
import 'package:paperless_mobile/features/similar_documents/cubit/similar_documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/similar_documents/view/similar_documents_view.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/helpers/format_helpers.dart';
|
||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:paperless_mobile/features/similar_documents/view/similar_documents_view.dart';
|
||||
|
||||
//TODO: Refactor this into several widgets
|
||||
class DocumentDetailsPage extends StatefulWidget {
|
||||
@@ -49,7 +45,7 @@ class DocumentDetailsPage extends StatefulWidget {
|
||||
|
||||
class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
late Future<DocumentMetaData> _metaData;
|
||||
|
||||
static const double _itemPadding = 24;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -70,7 +66,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
length: 4,
|
||||
child: Scaffold(
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
|
||||
floatingActionButton: widget.allowEdit ? _buildAppBar() : null,
|
||||
floatingActionButton: widget.allowEdit ? _buildEditButton() : null,
|
||||
bottomNavigationBar: _buildBottomAppBar(),
|
||||
body: NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||
@@ -96,27 +92,30 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
child: Text(
|
||||
S.of(context).documentDetailsPageTabOverviewLabel,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
child: Text(
|
||||
S.of(context).documentDetailsPageTabContentLabel,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
child: Text(
|
||||
S.of(context).documentDetailsPageTabMetaDataLabel,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
@@ -146,20 +145,26 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
),
|
||||
child: TabBarView(
|
||||
children: [
|
||||
_buildDocumentOverview(
|
||||
state.document,
|
||||
DocumentOverviewWidget(
|
||||
document: state.document,
|
||||
itemSpacing: _itemPadding,
|
||||
queryString: widget.titleAndContentQueryString,
|
||||
),
|
||||
_buildDocumentContentView(
|
||||
state.document,
|
||||
state,
|
||||
DocumentContentWidget(
|
||||
isFullContentLoaded: state.isFullContentLoaded,
|
||||
document: state.document,
|
||||
fullContent: state.fullContent,
|
||||
queryString: widget.titleAndContentQueryString,
|
||||
),
|
||||
_buildDocumentMetaDataView(
|
||||
state.document,
|
||||
DocumentMetaDataWidget(
|
||||
document: state.document,
|
||||
itemSpacing: _itemPadding,
|
||||
metaData: _metaData,
|
||||
),
|
||||
const SimilarDocumentsView(),
|
||||
],
|
||||
),
|
||||
).paddedSymmetrically(horizontal: 8);
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -168,7 +173,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
);
|
||||
}
|
||||
|
||||
BlocBuilder<DocumentDetailsCubit, DocumentDetailsState> _buildAppBar() {
|
||||
Widget _buildEditButton() {
|
||||
return BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>(
|
||||
builder: (context, state) {
|
||||
final _filteredSuggestions =
|
||||
@@ -176,7 +181,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||
builder: (context, connectivityState) {
|
||||
if (!connectivityState.isConnected) {
|
||||
return Container();
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return b.Badge(
|
||||
position: b.BadgePosition.topEnd(top: -12, end: -6),
|
||||
@@ -244,8 +249,10 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
IconButton(
|
||||
tooltip: S.of(context).documentDetailsPageShareTooltip,
|
||||
icon: const Icon(Icons.share),
|
||||
onPressed:
|
||||
isConnected ? () => _onShare(state.document) : null,
|
||||
onPressed: isConnected
|
||||
? () =>
|
||||
context.read<DocumentDetailsCubit>().shareDocument()
|
||||
: null,
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -265,7 +272,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
builder: (_) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider.value(
|
||||
value: EditDocumentCubit(
|
||||
value: DocumentEditCubit(
|
||||
document,
|
||||
documentsApi: context.read(),
|
||||
correspondentRepository: context.read(),
|
||||
@@ -279,7 +286,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
value: cubit,
|
||||
),
|
||||
],
|
||||
child: BlocListener<EditDocumentCubit, EditDocumentState>(
|
||||
child: BlocListener<DocumentEditCubit, DocumentEditState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.document != current.document,
|
||||
listener: (context, state) {
|
||||
@@ -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 {
|
||||
final delete = await showDialog(
|
||||
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 {
|
||||
ColoredTabBar({
|
||||
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();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,15 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
||||
|
||||
part 'edit_document_state.dart';
|
||||
part 'document_edit_state.dart';
|
||||
|
||||
class EditDocumentCubit extends Cubit<EditDocumentState> {
|
||||
class DocumentEditCubit extends Cubit<DocumentEditState> {
|
||||
final DocumentModel _initialDocument;
|
||||
final PaperlessDocumentsApi _docsApi;
|
||||
|
||||
@@ -24,7 +20,7 @@ class EditDocumentCubit extends Cubit<EditDocumentState> {
|
||||
final LabelRepository<Tag> _tagRepository;
|
||||
final List<StreamSubscription> _subscriptions = [];
|
||||
|
||||
EditDocumentCubit(
|
||||
DocumentEditCubit(
|
||||
DocumentModel document, {
|
||||
required PaperlessDocumentsApi documentsApi,
|
||||
required LabelRepository<Correspondent> correspondentRepository,
|
||||
@@ -40,7 +36,7 @@ class EditDocumentCubit extends Cubit<EditDocumentState> {
|
||||
_tagRepository = tagRepository,
|
||||
_notifier = notifier,
|
||||
super(
|
||||
EditDocumentState(
|
||||
DocumentEditState(
|
||||
document: document,
|
||||
correspondents: correspondentRepository.current?.values ?? {},
|
||||
documentTypes: documentTypeRepository.current?.values ?? {},
|
||||
@@ -1,6 +1,6 @@
|
||||
part of 'edit_document_cubit.dart';
|
||||
part of 'document_edit_cubit.dart';
|
||||
|
||||
class EditDocumentState extends Equatable {
|
||||
class DocumentEditState extends Equatable {
|
||||
final DocumentModel document;
|
||||
|
||||
final Map<int, Correspondent> correspondents;
|
||||
@@ -8,7 +8,7 @@ class EditDocumentState extends Equatable {
|
||||
final Map<int, StoragePath> storagePaths;
|
||||
final Map<int, Tag> tags;
|
||||
|
||||
const EditDocumentState({
|
||||
const DocumentEditState({
|
||||
required this.correspondents,
|
||||
required this.documentTypes,
|
||||
required this.storagePaths,
|
||||
@@ -25,14 +25,14 @@ class EditDocumentState extends Equatable {
|
||||
document,
|
||||
];
|
||||
|
||||
EditDocumentState copyWith({
|
||||
DocumentEditState copyWith({
|
||||
Map<int, Correspondent>? correspondents,
|
||||
Map<int, DocumentType>? documentTypes,
|
||||
Map<int, StoragePath>? storagePaths,
|
||||
Map<int, Tag>? tags,
|
||||
DocumentModel? document,
|
||||
}) {
|
||||
return EditDocumentState(
|
||||
return DocumentEditState(
|
||||
document: document ?? this.document,
|
||||
correspondents: correspondents ?? this.correspondents,
|
||||
documentTypes: documentTypes ?? this.documentTypes,
|
||||
@@ -13,7 +13,7 @@ import 'package:paperless_mobile/core/repository/state/impl/document_type_reposi
|
||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/workarounds/colored_chip.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/edit_document/cubit/edit_document_cubit.dart';
|
||||
import 'package:paperless_mobile/features/document_edit/cubit/document_edit_cubit.dart';
|
||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_correspondent_page.dart';
|
||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_document_type_page.dart';
|
||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_storage_path_page.dart';
|
||||
@@ -51,12 +51,12 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_filteredSuggestions = widget.suggestions
|
||||
.documentDifference(context.read<EditDocumentCubit>().state.document);
|
||||
.documentDifference(context.read<DocumentEditCubit>().state.document);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<EditDocumentCubit, EditDocumentState>(
|
||||
return BlocBuilder<DocumentEditCubit, DocumentEditState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
@@ -252,7 +252,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
_isSubmitLoading = true;
|
||||
});
|
||||
try {
|
||||
await context.read<EditDocumentCubit>().updateDocument(mergedDocument);
|
||||
await context.read<DocumentEditCubit>().updateDocument(mergedDocument);
|
||||
showSnackBar(context, S.of(context).documentUpdateSuccessMessage);
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
@@ -19,8 +19,8 @@ import 'package:paperless_mobile/features/document_search/view/document_search_p
|
||||
import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart';
|
||||
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/pages/document_view.dart';
|
||||
import 'package:paperless_mobile/features/scan/bloc/document_scanner_cubit.dart';
|
||||
import 'package:paperless_mobile/features/scan/view/widgets/scanned_image_item.dart';
|
||||
import 'package:paperless_mobile/features/document_scan/cubit/document_scanner_cubit.dart';
|
||||
import 'package:paperless_mobile/features/document_scan/view/widgets/scanned_image_item.dart';
|
||||
import 'package:paperless_mobile/features/search_app_bar/view/search_app_bar.dart';
|
||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
@@ -2,11 +2,17 @@ import 'package:collection/collection.dart';
|
||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||
import 'package:paperless_mobile/features/document_search/cubit/document_search_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:equatable/equatable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
|
||||
|
||||
part 'document_search_state.dart';
|
||||
|
||||
part 'document_search_cubit.g.dart';
|
||||
|
||||
class DocumentSearchCubit extends HydratedCubit<DocumentSearchState>
|
||||
with PagedDocumentsMixin {
|
||||
with DocumentPagingBlocMixin {
|
||||
@override
|
||||
final PaperlessDocumentsApi api;
|
||||
@override
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart';
|
||||
|
||||
part 'document_search_state.g.dart';
|
||||
part of 'document_search_cubit.dart';
|
||||
|
||||
enum SearchView {
|
||||
suggestions,
|
||||
@@ -11,7 +6,7 @@ enum SearchView {
|
||||
}
|
||||
|
||||
@JsonSerializable(ignoreUnannotated: true)
|
||||
class DocumentSearchState extends PagedDocumentsState {
|
||||
class DocumentSearchState extends DocumentPagingState {
|
||||
@JsonKey()
|
||||
final List<String> searchHistory;
|
||||
final SearchView view;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/document_search/cubit/document_search_cubit.dart';
|
||||
import 'package:paperless_mobile/features/document_search/cubit/document_search_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||
|
||||
@@ -5,11 +5,16 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/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:json_annotation/json_annotation.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
|
||||
|
||||
part 'documents_state.dart';
|
||||
part 'documents_cubit.g.dart';
|
||||
|
||||
class DocumentsCubit extends HydratedCubit<DocumentsState>
|
||||
with PagedDocumentsMixin {
|
||||
with DocumentPagingBlocMixin {
|
||||
@override
|
||||
final PaperlessDocumentsApi api;
|
||||
|
||||
@@ -94,4 +99,8 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
||||
notifier.unsubscribe(this);
|
||||
return super.close();
|
||||
}
|
||||
|
||||
void setViewType(ViewType viewType) {
|
||||
emit(state.copyWith(viewType: viewType));
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart';
|
||||
part of 'documents_cubit.dart';
|
||||
|
||||
class DocumentsState extends PagedDocumentsState {
|
||||
@JsonKey(includeFromJson: true, includeToJson: false)
|
||||
@JsonSerializable()
|
||||
class DocumentsState extends DocumentPagingState {
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
final List<DocumentModel> selection;
|
||||
|
||||
final ViewType viewType;
|
||||
|
||||
const DocumentsState({
|
||||
this.selection = const [],
|
||||
this.viewType = ViewType.list,
|
||||
super.value = const [],
|
||||
super.filter = const DocumentFilter(),
|
||||
super.hasLoaded = false,
|
||||
@@ -22,6 +24,7 @@ class DocumentsState extends PagedDocumentsState {
|
||||
List<PagedSearchResult<DocumentModel>>? value,
|
||||
DocumentFilter? filter,
|
||||
List<DocumentModel>? selection,
|
||||
ViewType? viewType,
|
||||
}) {
|
||||
return DocumentsState(
|
||||
hasLoaded: hasLoaded ?? this.hasLoaded,
|
||||
@@ -29,38 +32,22 @@ class DocumentsState extends PagedDocumentsState {
|
||||
value: value ?? this.value,
|
||||
filter: filter ?? this.filter,
|
||||
selection: selection ?? this.selection,
|
||||
viewType: viewType ?? this.viewType,
|
||||
);
|
||||
}
|
||||
|
||||
factory DocumentsState.fromJson(Map<String, dynamic> json) =>
|
||||
_$DocumentsStateFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$DocumentsStateToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
selection,
|
||||
viewType,
|
||||
...super.props,
|
||||
];
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = {
|
||||
'hasLoaded': hasLoaded,
|
||||
'isLoading': isLoading,
|
||||
'filter': filter.toJson(),
|
||||
'value':
|
||||
value.map((e) => e.toJson(DocumentModelJsonConverter())).toList(),
|
||||
};
|
||||
return json;
|
||||
}
|
||||
|
||||
factory DocumentsState.fromJson(Map<String, dynamic> json) {
|
||||
return DocumentsState(
|
||||
hasLoaded: json['hasLoaded'],
|
||||
isLoading: json['isLoading'],
|
||||
value: (json['value'] as List<dynamic>)
|
||||
.map((e) =>
|
||||
PagedSearchResult.fromJsonT(e, DocumentModelJsonConverter()))
|
||||
.toList(),
|
||||
filter: DocumentFilter.fromJson(json['filter']),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
DocumentsState copyWithPaged({
|
||||
bool? hasLoaded,
|
||||
@@ -1,7 +1,4 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:badges/badges.dart' as b;
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
@@ -9,21 +6,18 @@ import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
|
||||
import 'package:paperless_mobile/features/document_search/view/document_search_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/cubit/documents_cubit.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/search/document_filter_panel.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/bulk_delete_confirmation_dialog.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/providers/labels_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/providers/labels_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/view/saved_view_list.dart';
|
||||
import 'package:paperless_mobile/features/search_app_bar/view/search_app_bar.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_state.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||
@@ -263,14 +257,6 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
),
|
||||
_buildViewActions(),
|
||||
BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
// Not required anymore since saved views are now handled separately
|
||||
// buildWhen: (previous, current) =>
|
||||
// !const ListEquality().equals(
|
||||
// previous.documents,
|
||||
// current.documents,
|
||||
// ) ||
|
||||
// previous.selectedIds !=
|
||||
// current.selectedIds,
|
||||
builder: (context, state) {
|
||||
if (state.hasLoaded &&
|
||||
state.documents.isEmpty) {
|
||||
@@ -285,34 +271,27 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
),
|
||||
);
|
||||
}
|
||||
return BlocBuilder<
|
||||
ApplicationSettingsCubit,
|
||||
ApplicationSettingsState>(
|
||||
builder: (context, settings) {
|
||||
return SliverAdaptiveDocumentsView(
|
||||
viewType:
|
||||
settings.preferredViewType,
|
||||
onTap: _openDetails,
|
||||
onSelected: context
|
||||
.read<DocumentsCubit>()
|
||||
.toggleDocumentSelection,
|
||||
hasInternetConnection:
|
||||
connectivityState.isConnected,
|
||||
onTagSelected: _addTagToFilter,
|
||||
onCorrespondentSelected:
|
||||
_addCorrespondentToFilter,
|
||||
onDocumentTypeSelected:
|
||||
_addDocumentTypeToFilter,
|
||||
onStoragePathSelected:
|
||||
_addStoragePathToFilter,
|
||||
documents: state.documents,
|
||||
hasLoaded: state.hasLoaded,
|
||||
isLabelClickable: true,
|
||||
isLoading: state.isLoading,
|
||||
selectedDocumentIds:
|
||||
state.selectedIds,
|
||||
);
|
||||
},
|
||||
|
||||
return SliverAdaptiveDocumentsView(
|
||||
viewType: state.viewType,
|
||||
onTap: _openDetails,
|
||||
onSelected: context
|
||||
.read<DocumentsCubit>()
|
||||
.toggleDocumentSelection,
|
||||
hasInternetConnection:
|
||||
connectivityState.isConnected,
|
||||
onTagSelected: _addTagToFilter,
|
||||
onCorrespondentSelected:
|
||||
_addCorrespondentToFilter,
|
||||
onDocumentTypeSelected:
|
||||
_addDocumentTypeToFilter,
|
||||
onStoragePathSelected:
|
||||
_addStoragePathToFilter,
|
||||
documents: state.documents,
|
||||
hasLoaded: state.hasLoaded,
|
||||
isLabelClickable: true,
|
||||
isLoading: state.isLoading,
|
||||
selectedDocumentIds: state.selectedIds,
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -360,18 +339,11 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SortDocumentsButton(),
|
||||
BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
|
||||
BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
builder: (context, state) {
|
||||
return IconButton(
|
||||
icon: Icon(
|
||||
state.preferredViewType == ViewType.list
|
||||
? Icons.grid_view_rounded
|
||||
: Icons.list,
|
||||
),
|
||||
onPressed: () =>
|
||||
context.read<ApplicationSettingsCubit>().setViewType(
|
||||
state.preferredViewType.toggle(),
|
||||
),
|
||||
return ViewTypeSelectionWidget(
|
||||
viewType: state.viewType,
|
||||
onChanged: context.read<DocumentsCubit>().setViewType,
|
||||
);
|
||||
},
|
||||
)
|
||||
|
||||
@@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/empty_state.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class DocumentsEmptyState extends StatelessWidget {
|
||||
final PagedDocumentsState state;
|
||||
final DocumentPagingState state;
|
||||
final VoidCallback? onReset;
|
||||
const DocumentsEmptyState({
|
||||
Key? key,
|
||||
|
||||
@@ -4,9 +4,8 @@ import 'package:intl/intl.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/items/document_item.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/providers/document_type_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/providers/document_type_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/view/widgets/correspondent_widget.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_widget.dart';
|
||||
|
||||
|
||||
@@ -3,9 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/form_builder_fields/extended_date_range_form_field/form_builder_extended_date_range_picker.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/form_builder_fields/extended_date_range_form_field/form_builder_extended_date_range_picker.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/pages/documents_page.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/search/document_filter_form.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/search/text_query_form_field.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
enum DateRangeSelection { before, after }
|
||||
|
||||
@@ -4,8 +4,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/translation/sort_field_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class SortFieldSelectionBottomSheet extends StatefulWidget {
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/form_builder_fields/form_builder_type_ahead.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class BulkDeleteConfirmationDialog extends StatelessWidget {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||
|
||||
/// Meant to be used with blocbuilder.
|
||||
class ViewTypeSelectionWidget extends StatelessWidget {
|
||||
final ViewType viewType;
|
||||
final void Function(ViewType type) onChanged;
|
||||
|
||||
const ViewTypeSelectionWidget({
|
||||
super.key,
|
||||
required this.viewType,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final next = viewType.toggle();
|
||||
final icon = next == ViewType.grid ? Icons.grid_view_rounded : Icons.list;
|
||||
return IconButton(
|
||||
icon: Icon(icon),
|
||||
onPressed: () {
|
||||
onChanged(next);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,10 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/translation/sort_field_localization_mapper.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/cubit/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
|
||||
class SortDocumentsButton extends StatelessWidget {
|
||||
const SortDocumentsButton({
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_state.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||
|
||||
class ViewActions extends StatelessWidget {
|
||||
const ViewActions({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SortDocumentsButton(),
|
||||
BlocBuilder<ApplicationSettingsCubit, ApplicationSettingsState>(
|
||||
builder: (context, settings) {
|
||||
final cubit = context.read<ApplicationSettingsCubit>();
|
||||
switch (settings.preferredViewType) {
|
||||
case ViewType.grid:
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.list),
|
||||
onPressed: () =>
|
||||
cubit.setViewType(settings.preferredViewType.toggle()),
|
||||
);
|
||||
case ViewType.list:
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.grid_view_rounded),
|
||||
onPressed: () =>
|
||||
cubit.setViewType(settings.preferredViewType.toggle()),
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/indexed_repository_state.dart';
|
||||
import 'package:paperless_mobile/features/edit_label/cubit/edit_label_state.dart';
|
||||
|
||||
part 'edit_label_state.dart';
|
||||
|
||||
class EditLabelCubit<T extends Label> extends Cubit<EditLabelState<T>> {
|
||||
final LabelRepository<T> _repository;
|
||||
@@ -13,7 +14,7 @@ class EditLabelCubit<T extends Label> extends Cubit<EditLabelState<T>> {
|
||||
|
||||
EditLabelCubit(LabelRepository<T> repository)
|
||||
: _repository = repository,
|
||||
super(const EditLabelInitial()) {
|
||||
super(EditLabelState<T>(labels: repository.current?.values ?? {})) {
|
||||
_subscription = repository.values.listen(
|
||||
(event) => emit(EditLabelState(labels: event?.values ?? {})),
|
||||
);
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
part of 'edit_label_cubit.dart';
|
||||
|
||||
@immutable
|
||||
class EditLabelState<T> extends Equatable {
|
||||
final Map<int, T> labels;
|
||||
|
||||
const EditLabelState({required this.labels});
|
||||
const EditLabelState({this.labels = const {}});
|
||||
|
||||
@override
|
||||
List<Object> get props => [labels];
|
||||
}
|
||||
|
||||
class EditLabelInitial<T> extends EditLabelState<T> {
|
||||
const EditLabelInitial() : super(labels: const {});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -10,25 +12,21 @@ import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.da
|
||||
import 'package:paperless_mobile/core/global/constants.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/translation/error_code_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/features/document_scan/cubit/document_scanner_cubit.dart';
|
||||
import 'package:paperless_mobile/features/document_scan/view/scanner_page.dart';
|
||||
import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart';
|
||||
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/pages/documents_page.dart';
|
||||
import 'package:paperless_mobile/features/home/view/route_description.dart';
|
||||
import 'package:paperless_mobile/features/inbox/bloc/inbox_cubit.dart';
|
||||
import 'package:paperless_mobile/features/inbox/bloc/state/inbox_state.dart';
|
||||
import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart';
|
||||
import 'package:paperless_mobile/features/inbox/view/pages/inbox_page.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/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/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/scan/bloc/document_scanner_cubit.dart';
|
||||
import 'package:paperless_mobile/features/scan/view/scanner_page.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/generated/l10n.dart';
|
||||
@@ -45,14 +43,16 @@ class HomePage extends StatefulWidget {
|
||||
_HomePageState createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> {
|
||||
class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
|
||||
int _currentIndex = 0;
|
||||
final DocumentScannerCubit _scannerCubit = DocumentScannerCubit();
|
||||
late final InboxCubit _inboxCubit;
|
||||
late Timer _inboxTimer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
_initializeData(context);
|
||||
_inboxCubit = InboxCubit(
|
||||
context.read(),
|
||||
@@ -62,12 +62,43 @@ class _HomePageState extends State<HomePage> {
|
||||
context.read(),
|
||||
context.read(),
|
||||
);
|
||||
_listenToInboxChanges();
|
||||
context.read<ConnectivityCubit>().reload();
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
_listenForReceivedFiles();
|
||||
});
|
||||
}
|
||||
|
||||
void _listenToInboxChanges() {
|
||||
_inboxCubit.refreshItemsInInboxCount();
|
||||
_inboxTimer = Timer.periodic(const Duration(seconds: 10), (timer) {
|
||||
if (!mounted) {
|
||||
timer.cancel();
|
||||
} else {
|
||||
_inboxCubit.refreshItemsInInboxCount();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed && !_inboxTimer.isActive) {
|
||||
log('App is now in foreground, start polling for statistics.');
|
||||
_listenToInboxChanges();
|
||||
} else if (state != AppLifecycleState.resumed) {
|
||||
log('App is now in background, stop polling for statistics.');
|
||||
_inboxTimer.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_inboxTimer.cancel();
|
||||
_inboxCubit.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _listenForReceivedFiles() async {
|
||||
if (ShareIntentQueue.instance.hasUnhandledFiles) {
|
||||
await _handleReceivedFile(ShareIntentQueue.instance.pop()!);
|
||||
@@ -156,12 +187,6 @@ class _HomePageState extends State<HomePage> {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_inboxCubit.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final destinations = [
|
||||
@@ -247,8 +272,18 @@ class _HomePageState extends State<HomePage> {
|
||||
],
|
||||
child: const LabelsPage(),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: _inboxCubit,
|
||||
MultiBlocProvider(
|
||||
providers: [
|
||||
// We need to manually downcast the inboxcubit to the
|
||||
// mixed-in DocumentPagingBlocMixin to use the
|
||||
// DocumentPagingViewMixin in the inbox.
|
||||
BlocProvider<DocumentPagingBlocMixin>.value(
|
||||
value: _inboxCubit,
|
||||
),
|
||||
BlocProvider<InboxCubit>.value(
|
||||
value: _inboxCubit,
|
||||
),
|
||||
],
|
||||
child: const InboxPage(),
|
||||
),
|
||||
// const SettingsPage(),
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
||||
// import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
||||
// import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
// import 'package:paperless_mobile/features/inbox/bloc/inbox_cubit.dart';
|
||||
// import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart';
|
||||
// import 'package:paperless_mobile/features/inbox/view/pages/inbox_page.dart';
|
||||
// import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||
// import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||
|
||||
@@ -3,13 +3,9 @@ import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/features/inbox/bloc/state/inbox_state.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/paged_documents_mixin.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart';
|
||||
|
||||
class InboxCubit extends HydratedCubit<InboxState> with PagedDocumentsMixin {
|
||||
part 'inbox_cubit.g.dart';
|
||||
part 'inbox_state.dart';
|
||||
|
||||
class InboxCubit extends HydratedCubit<InboxState>
|
||||
with DocumentPagingBlocMixin {
|
||||
final LabelRepository<Tag> _tagsRepository;
|
||||
final LabelRepository<Correspondent> _correspondentRepository;
|
||||
final LabelRepository<DocumentType> _documentTypeRepository;
|
||||
@@ -82,13 +86,6 @@ class InboxCubit extends HydratedCubit<InboxState> with PagedDocumentsMixin {
|
||||
|
||||
refreshItemsInInboxCount(false);
|
||||
loadInbox();
|
||||
|
||||
Timer.periodic(const Duration(seconds: 5), (timer) {
|
||||
if (isClosed) {
|
||||
timer.cancel();
|
||||
}
|
||||
refreshItemsInInboxCount();
|
||||
});
|
||||
}
|
||||
|
||||
void refreshItemsInInboxCount([bool shouldLoadInbox = true]) async {
|
||||
@@ -1,11 +1,7 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart';
|
||||
|
||||
part 'inbox_state.g.dart';
|
||||
part of 'inbox_cubit.dart';
|
||||
|
||||
@JsonSerializable(ignoreUnannotated: true)
|
||||
class InboxState extends PagedDocumentsState {
|
||||
class InboxState extends DocumentPagingState {
|
||||
final Iterable<int> inboxTags;
|
||||
|
||||
final Map<int, Tag> availableTags;
|
||||
@@ -9,10 +9,10 @@ import 'package:paperless_mobile/features/documents/view/widgets/documents_list_
|
||||
import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
||||
import 'package:paperless_mobile/extensions/dart_extensions.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/inbox/bloc/inbox_cubit.dart';
|
||||
import 'package:paperless_mobile/features/inbox/bloc/state/inbox_state.dart';
|
||||
import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart';
|
||||
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_empty_widget.dart';
|
||||
import 'package:paperless_mobile/features/inbox/view/widgets/inbox_item.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart';
|
||||
import 'package:paperless_mobile/features/search_app_bar/view/search_app_bar.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||
@@ -24,35 +24,15 @@ class InboxPage extends StatefulWidget {
|
||||
State<InboxPage> createState() => _InboxPageState();
|
||||
}
|
||||
|
||||
class _InboxPageState extends State<InboxPage> {
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
class _InboxPageState extends State<InboxPage> with DocumentPagingViewMixin {
|
||||
@override
|
||||
final pagingScrollController = ScrollController();
|
||||
final _emptyStateRefreshIndicatorKey = GlobalKey<RefreshIndicatorState>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
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
|
||||
@@ -144,7 +124,7 @@ class _InboxPageState extends State<InboxPage> {
|
||||
physics: state.documents.isEmpty
|
||||
? const NeverScrollableScrollPhysics()
|
||||
: const AlwaysScrollableScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
controller: pagingScrollController,
|
||||
slivers: [
|
||||
SearchAppBar(
|
||||
hintText: S.of(context).documentSearchSearchDocuments,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/features/inbox/bloc/inbox_cubit.dart';
|
||||
import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class InboxEmptyWidget extends StatelessWidget {
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:paperless_mobile/core/workarounds/colored_chip.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.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/inbox/bloc/inbox_cubit.dart';
|
||||
import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.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';
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/providers/correspondent_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/providers/correspondent_bloc_provider.dart';
|
||||
|
||||
class CorrespondentWidget extends StatelessWidget {
|
||||
final int? correspondentId;
|
||||
|
||||
@@ -3,7 +3,8 @@ import 'dart:async';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
|
||||
part 'label_state.dart';
|
||||
|
||||
class LabelCubit<T extends Label> extends Cubit<LabelState<T>> {
|
||||
final LabelRepository<T> _repository;
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
part of 'label_cubit.dart';
|
||||
|
||||
class LabelState<T extends Label> {
|
||||
LabelState.initial() : this(isLoaded: false, labels: {});
|
||||
@@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
|
||||
class CorrespondentBlocProvider extends StatelessWidget {
|
||||
final Widget child;
|
||||
@@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
|
||||
class DocumentTypeBlocProvider extends StatelessWidget {
|
||||
final Widget child;
|
||||
@@ -6,7 +6,7 @@ import 'package:paperless_mobile/core/repository/state/impl/correspondent_reposi
|
||||
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
|
||||
class LabelsBlocProvider extends StatelessWidget {
|
||||
final Widget child;
|
||||
@@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
|
||||
class StoragePathBlocProvider extends StatelessWidget {
|
||||
final Widget child;
|
||||
@@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
|
||||
class TagBlocProvider extends StatelessWidget {
|
||||
final Widget child;
|
||||
@@ -1,9 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/providers/document_type_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/providers/document_type_bloc_provider.dart';
|
||||
|
||||
class DocumentTypeWidget extends StatelessWidget {
|
||||
final int? documentTypeId;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/providers/storage_path_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/providers/storage_path_bloc_provider.dart';
|
||||
|
||||
class StoragePathWidget extends StatelessWidget {
|
||||
final int? pathId;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/providers/tag_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/providers/tag_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tag_widget.dart';
|
||||
|
||||
class TagsWidget extends StatelessWidget {
|
||||
|
||||
@@ -15,7 +15,7 @@ import 'package:paperless_mobile/features/edit_label/view/impl/edit_corresponden
|
||||
import 'package:paperless_mobile/features/edit_label/view/impl/edit_document_type_page.dart';
|
||||
import 'package:paperless_mobile/features/edit_label/view/impl/edit_storage_path_page.dart';
|
||||
import 'package:paperless_mobile/features/edit_label/view/impl/edit_tag_page.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_tab_view.dart';
|
||||
import 'package:paperless_mobile/features/search_app_bar/view/search_app_bar.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/linked_documents/bloc/linked_documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/linked_documents/view/pages/linked_documents_page.dart';
|
||||
import 'package:paperless_mobile/features/linked_documents/cubit/linked_documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/linked_documents/view/linked_documents_page.dart';
|
||||
import 'package:paperless_mobile/helpers/format_helpers.dart';
|
||||
|
||||
class LabelItem<T extends Label> extends StatelessWidget {
|
||||
|
||||
@@ -3,9 +3,8 @@ 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/translation/matching_algorithm_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
import 'package:paperless_mobile/core/widgets/offline_widget.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_item.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
|
||||
|
||||
class LabelText<T extends Label> extends StatelessWidget {
|
||||
final int? id;
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||
import 'package:paperless_mobile/features/linked_documents/bloc/state/linked_documents_state.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/paged_documents_mixin.dart';
|
||||
|
||||
class LinkedDocumentsCubit extends Cubit<LinkedDocumentsState>
|
||||
with PagedDocumentsMixin {
|
||||
@override
|
||||
final PaperlessDocumentsApi api;
|
||||
|
||||
@override
|
||||
final DocumentChangedNotifier notifier;
|
||||
|
||||
LinkedDocumentsCubit(
|
||||
DocumentFilter filter,
|
||||
this.api,
|
||||
this.notifier,
|
||||
) : super(const LinkedDocumentsState()) {
|
||||
updateFilter(filter: filter);
|
||||
notifier.subscribe(
|
||||
this,
|
||||
onUpdated: replace,
|
||||
onDeleted: remove,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> update(DocumentModel document) async {
|
||||
final updated = await api.update(document);
|
||||
if (!state.filter.matches(updated)) {
|
||||
remove(document);
|
||||
} else {
|
||||
replace(document);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||
part 'linked_documents_state.dart';
|
||||
|
||||
part 'linked_documents_cubit.g.dart';
|
||||
|
||||
class LinkedDocumentsCubit extends HydratedCubit<LinkedDocumentsState>
|
||||
with DocumentPagingBlocMixin {
|
||||
@override
|
||||
final PaperlessDocumentsApi api;
|
||||
|
||||
@override
|
||||
final DocumentChangedNotifier notifier;
|
||||
|
||||
LinkedDocumentsCubit(
|
||||
DocumentFilter filter,
|
||||
this.api,
|
||||
this.notifier,
|
||||
) : super(const LinkedDocumentsState()) {
|
||||
updateFilter(filter: filter);
|
||||
notifier.subscribe(
|
||||
this,
|
||||
onUpdated: replace,
|
||||
onDeleted: remove,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> update(DocumentModel document) async {
|
||||
final updated = await api.update(document);
|
||||
if (!state.filter.matches(updated)) {
|
||||
remove(document);
|
||||
} else {
|
||||
replace(document);
|
||||
}
|
||||
}
|
||||
|
||||
void setViewType(ViewType type) {
|
||||
emit(state.copyWith(viewType: type));
|
||||
}
|
||||
|
||||
@override
|
||||
LinkedDocumentsState? fromJson(Map<String, dynamic> json) {
|
||||
return LinkedDocumentsState.fromJson(json);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic>? toJson(LinkedDocumentsState state) {
|
||||
return state.toJson();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart';
|
||||
part of 'linked_documents_cubit.dart';
|
||||
|
||||
class LinkedDocumentsState extends PagedDocumentsState {
|
||||
@JsonSerializable(ignoreUnannotated: true)
|
||||
class LinkedDocumentsState extends DocumentPagingState {
|
||||
@JsonKey()
|
||||
final ViewType viewType;
|
||||
const LinkedDocumentsState({
|
||||
this.viewType = ViewType.list,
|
||||
super.filter,
|
||||
super.isLoading,
|
||||
super.hasLoaded,
|
||||
@@ -14,12 +17,14 @@ class LinkedDocumentsState extends PagedDocumentsState {
|
||||
bool? isLoading,
|
||||
bool? hasLoaded,
|
||||
List<PagedSearchResult<DocumentModel>>? value,
|
||||
ViewType? viewType,
|
||||
}) {
|
||||
return LinkedDocumentsState(
|
||||
filter: filter ?? this.filter,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
hasLoaded: hasLoaded ?? this.hasLoaded,
|
||||
value: value ?? this.value,
|
||||
viewType: viewType ?? this.viewType,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -40,9 +45,12 @@ class LinkedDocumentsState extends PagedDocumentsState {
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
filter,
|
||||
isLoading,
|
||||
hasLoaded,
|
||||
value,
|
||||
viewType,
|
||||
...super.props,
|
||||
];
|
||||
|
||||
factory LinkedDocumentsState.fromJson(Map<String, dynamic> json) =>
|
||||
_$LinkedDocumentsStateFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LinkedDocumentsStateToJson(this);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart';
|
||||
import 'package:paperless_mobile/features/linked_documents/cubit/linked_documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||
|
||||
class LinkedDocumentsPage extends StatefulWidget {
|
||||
const LinkedDocumentsPage({super.key});
|
||||
|
||||
@override
|
||||
State<LinkedDocumentsPage> createState() => _LinkedDocumentsPageState();
|
||||
}
|
||||
|
||||
class _LinkedDocumentsPageState extends State<LinkedDocumentsPage>
|
||||
with DocumentPagingViewMixin {
|
||||
@override
|
||||
final pagingScrollController = ScrollController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(S.of(context).linkedDocumentsPageTitle),
|
||||
actions: [
|
||||
BlocBuilder<LinkedDocumentsCubit, LinkedDocumentsState>(
|
||||
builder: (context, state) {
|
||||
return ViewTypeSelectionWidget(
|
||||
viewType: state.viewType,
|
||||
onChanged: context.read<LinkedDocumentsCubit>().setViewType,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: BlocBuilder<LinkedDocumentsCubit, LinkedDocumentsState>(
|
||||
builder: (context, state) {
|
||||
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||
builder: (context, connectivity) {
|
||||
return CustomScrollView(
|
||||
controller: pagingScrollController,
|
||||
slivers: [
|
||||
SliverAdaptiveDocumentsView(
|
||||
viewType: state.viewType,
|
||||
documents: state.documents,
|
||||
hasInternetConnection: connectivity.isConnected,
|
||||
isLabelClickable: false,
|
||||
isLoading: state.isLoading,
|
||||
hasLoaded: state.hasLoaded,
|
||||
onTap: (document) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
DocumentDetailsRoute.routeName,
|
||||
arguments: DocumentDetailsRouteArguments(
|
||||
document: document,
|
||||
isLabelClickable: false,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart';
|
||||
import 'package:paperless_mobile/features/linked_documents/bloc/linked_documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/linked_documents/bloc/state/linked_documents_state.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||
|
||||
class LinkedDocumentsPage extends StatefulWidget {
|
||||
const LinkedDocumentsPage({super.key});
|
||||
|
||||
@override
|
||||
State<LinkedDocumentsPage> createState() => _LinkedDocumentsPageState();
|
||||
}
|
||||
|
||||
class _LinkedDocumentsPageState extends State<LinkedDocumentsPage> {
|
||||
final _scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(S.of(context).linkedDocumentsPageTitle),
|
||||
),
|
||||
body: BlocBuilder<LinkedDocumentsCubit, LinkedDocumentsState>(
|
||||
builder: (context, state) {
|
||||
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||
builder: (context, connectivity) {
|
||||
return DefaultAdaptiveDocumentsView(
|
||||
scrollController: _scrollController,
|
||||
documents: state.documents,
|
||||
hasInternetConnection: connectivity.isConnected,
|
||||
isLabelClickable: false,
|
||||
isLoading: state.isLoading,
|
||||
hasLoaded: state.hasLoaded,
|
||||
onTap: (document) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
DocumentDetailsRoute.routeName,
|
||||
arguments: DocumentDetailsRouteArguments(
|
||||
document: document,
|
||||
isLabelClickable: false,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/security/session_manager.dart';
|
||||
import 'package:paperless_mobile/features/login/bloc/authentication_state.dart';
|
||||
import 'package:paperless_mobile/features/login/model/authentication_information.dart';
|
||||
import 'package:paperless_mobile/features/login/model/client_certificate.dart';
|
||||
import 'package:paperless_mobile/features/login/model/user_credentials.model.dart';
|
||||
import 'package:paperless_mobile/features/login/services/authentication_service.dart';
|
||||
part 'authentication_state.dart';
|
||||
part 'authentication_cubit.g.dart';
|
||||
|
||||
class AuthenticationCubit extends Cubit<AuthenticationState>
|
||||
with HydratedMixin<AuthenticationState> {
|
||||
@@ -1,12 +1,9 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:paperless_mobile/features/login/model/authentication_information.dart';
|
||||
|
||||
part 'authentication_state.g.dart';
|
||||
part of 'authentication_cubit.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class AuthenticationState {
|
||||
final bool wasLoginStored;
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
final bool? wasLocalAuthenticationSuccessful;
|
||||
final AuthenticationInformation? authentication;
|
||||
|
||||
@@ -3,17 +3,15 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/type/types.dart';
|
||||
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
|
||||
import 'package:paperless_mobile/features/login/view/widgets/form_fields/client_certificate_form_field.dart';
|
||||
import 'package:paperless_mobile/features/login/view/widgets/form_fields/server_address_form_field.dart';
|
||||
import 'package:paperless_mobile/features/login/view/widgets/form_fields/user_credentials_form_field.dart';
|
||||
import 'package:paperless_mobile/features/login/view/widgets/login_pages/server_connection_page.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||
import 'package:paperless_mobile/constants.dart';
|
||||
|
||||
import 'widgets/never_scrollable_scroll_behavior.dart';
|
||||
import 'widgets/login_pages/server_login_page.dart';
|
||||
import 'widgets/never_scrollable_scroll_behavior.dart';
|
||||
|
||||
class LoginPage extends StatefulWidget {
|
||||
const LoginPage({Key? key}) : super(key: key);
|
||||
|
||||
@@ -63,25 +63,48 @@ class _ClientCertificateFormFieldState
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: ElevatedButton(
|
||||
onPressed: () => _onSelectFile(field),
|
||||
child: Text(S.of(context).genericActionSelectText),
|
||||
),
|
||||
title: _buildSelectedFileText(field),
|
||||
trailing: AbsorbPointer(
|
||||
absorbing: field.value == null,
|
||||
child: _selectedFile != null
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () => setState(() {
|
||||
_selectedFile = null;
|
||||
field.didChange(null);
|
||||
}),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () => _onSelectFile(field),
|
||||
child:
|
||||
Text(S.of(context).genericActionSelectText),
|
||||
),
|
||||
_buildSelectedFileText(field).paddedOnly(left: 8),
|
||||
],
|
||||
),
|
||||
if (_selectedFile != null)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () => setState(() {
|
||||
_selectedFile = null;
|
||||
field.didChange(null);
|
||||
}),
|
||||
)
|
||||
],
|
||||
).padded(8),
|
||||
// ListTile(
|
||||
// leading: ElevatedButton(
|
||||
// onPressed: () => _onSelectFile(field),
|
||||
// child: Text(S.of(context).genericActionSelectText),
|
||||
// ),
|
||||
// title: _buildSelectedFileText(field),
|
||||
// trailing: AbsorbPointer(
|
||||
// absorbing: field.value == null,
|
||||
// child: _selectedFile != null
|
||||
// ? IconButton(
|
||||
// icon: const Icon(Icons.close),
|
||||
// onPressed: () => setState(() {
|
||||
// _selectedFile = null;
|
||||
// field.didChange(null);
|
||||
// }),
|
||||
// )
|
||||
// : null,
|
||||
// ),
|
||||
// ),
|
||||
if (_selectedFile != null) ...[
|
||||
ObscuredInputTextFormField(
|
||||
key: const ValueKey('login-client-cert-passphrase'),
|
||||
@@ -127,7 +150,9 @@ class _ClientCertificateFormFieldState
|
||||
assert(_selectedFile == null);
|
||||
return Text(
|
||||
S.of(context).loginPageClientCertificateSettingSelectFileText,
|
||||
style: TextStyle(color: Theme.of(context).hintColor),
|
||||
style: Theme.of(context).textTheme.labelMedium?.apply(
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
assert(_selectedFile != null);
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||
|
||||
import 'model/paged_documents_state.dart';
|
||||
import 'paged_documents_state.dart';
|
||||
|
||||
///
|
||||
/// Mixin which can be used on cubits that handle documents.
|
||||
/// This implements all paging and filtering logic.
|
||||
///
|
||||
mixin PagedDocumentsMixin<State extends PagedDocumentsState>
|
||||
mixin DocumentPagingBlocMixin<State extends DocumentPagingState>
|
||||
on BlocBase<State> {
|
||||
PaperlessDocumentsApi get api;
|
||||
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.
|
||||
/// [T] is the return type of the API call.
|
||||
///
|
||||
abstract class PagedDocumentsState extends Equatable {
|
||||
abstract class DocumentPagingState extends Equatable {
|
||||
final bool hasLoaded;
|
||||
final bool isLoading;
|
||||
final List<PagedSearchResult<DocumentModel>> value;
|
||||
final DocumentFilter filter;
|
||||
|
||||
const PagedDocumentsState({
|
||||
const DocumentPagingState({
|
||||
this.value = const [],
|
||||
this.hasLoaded = 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;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.dart';
|
||||
|
||||
part 'saved_view_state.dart';
|
||||
|
||||
class SavedViewCubit extends Cubit<SavedViewState> {
|
||||
final SavedViewRepository _repository;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/paged_documents_mixin.dart';
|
||||
|
||||
part 'saved_view_details_state.dart';
|
||||
|
||||
class SavedViewDetailsCubit extends Cubit<SavedViewDetailsState>
|
||||
with PagedDocumentsMixin {
|
||||
@override
|
||||
final PaperlessDocumentsApi api;
|
||||
|
||||
@override
|
||||
final DocumentChangedNotifier notifier;
|
||||
|
||||
final SavedView savedView;
|
||||
SavedViewDetailsCubit(
|
||||
this.api,
|
||||
this.notifier, {
|
||||
required this.savedView,
|
||||
}) : super(const SavedViewDetailsState()) {
|
||||
notifier.subscribe(
|
||||
this,
|
||||
onDeleted: remove,
|
||||
onUpdated: replace,
|
||||
);
|
||||
updateFilter(filter: savedView.toDocumentFilter());
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
part of 'saved_view_cubit.dart';
|
||||
|
||||
class SavedViewState with EquatableMixin {
|
||||
class SavedViewState extends Equatable {
|
||||
final bool hasLoaded;
|
||||
final Map<int, SavedView> value;
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/search/document_filter_form.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/providers/labels_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
|
||||
class AddSavedViewPage extends StatefulWidget {
|
||||
final DocumentFilter currentFilter;
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_details_cubit.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/view/saved_view_page.dart';
|
||||
import 'package:paperless_mobile/features/saved_view_details/cubit/saved_view_details_cubit.dart';
|
||||
import 'package:paperless_mobile/features/saved_view_details/view/saved_view_details_page.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class SavedViewList extends StatelessWidget {
|
||||
@@ -48,7 +45,7 @@ class SavedViewList extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
],
|
||||
child: SavedViewPage(
|
||||
child: SavedViewDetailsPage(
|
||||
onDelete: savedViewCubit.remove,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.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/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/selection/confirm_delete_saved_view_dialog.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/view_actions.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_details_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||
|
||||
class SavedViewPage extends StatefulWidget {
|
||||
final Future<void> Function(SavedView savedView) onDelete;
|
||||
const SavedViewPage({
|
||||
super.key,
|
||||
required this.onDelete,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SavedViewPage> createState() => _SavedViewPageState();
|
||||
}
|
||||
|
||||
class _SavedViewPageState extends State<SavedViewPage> {
|
||||
final _scrollController = ScrollController();
|
||||
ViewType _viewType = ViewType.list;
|
||||
SavedView get _savedView => context.read<SavedViewDetailsCubit>().savedView;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: BlocBuilder<SavedViewDetailsCubit, SavedViewDetailsState>(
|
||||
builder: (context, state) {
|
||||
return Text(_savedView.name);
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () async {
|
||||
final shouldDelete = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
ConfirmDeleteSavedViewDialog(view: _savedView),
|
||||
) ??
|
||||
false;
|
||||
if (shouldDelete) {
|
||||
await widget.onDelete(_savedView);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
_viewType == ViewType.list ? Icons.grid_view_rounded : Icons.list,
|
||||
),
|
||||
onPressed: () => setState(() => _viewType = _viewType.toggle()),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: BlocBuilder<SavedViewDetailsCubit, SavedViewDetailsState>(
|
||||
builder: (context, state) {
|
||||
if (state.hasLoaded && state.documents.isEmpty) {
|
||||
return DocumentsEmptyState(state: state);
|
||||
}
|
||||
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||
builder: (context, connectivity) {
|
||||
return CustomScrollView(
|
||||
controller: _scrollController,
|
||||
slivers: [
|
||||
SliverAdaptiveDocumentsView(
|
||||
documents: state.documents,
|
||||
hasInternetConnection: connectivity.isConnected,
|
||||
isLabelClickable: false,
|
||||
isLoading: state.isLoading,
|
||||
hasLoaded: state.hasLoaded,
|
||||
onTap: _onOpenDocumentDetails,
|
||||
viewType: _viewType,
|
||||
),
|
||||
if (state.hasLoaded && state.isLoading)
|
||||
const SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onOpenDocumentDetails(DocumentModel document) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
DocumentDetailsRoute.routeName,
|
||||
arguments: DocumentDetailsRouteArguments(
|
||||
document: document,
|
||||
isLabelClickable: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
// import 'dart:math';
|
||||
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
// import 'package:paperless_api/paperless_api.dart';
|
||||
// import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
// import 'package:paperless_mobile/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/view/widgets/selection/confirm_delete_saved_view_dialog.dart';
|
||||
// import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||
// import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.dart';
|
||||
// import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
|
||||
// import 'package:paperless_mobile/generated/l10n.dart';
|
||||
// import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||
// import 'package:paperless_mobile/constants.dart';
|
||||
// import 'package:shimmer/shimmer.dart';
|
||||
|
||||
// class SavedViewSelectionWidget extends StatelessWidget {
|
||||
// final DocumentFilter currentFilter;
|
||||
// const SavedViewSelectionWidget({
|
||||
// Key? key,
|
||||
// required this.height,
|
||||
// required this.enabled,
|
||||
// required this.currentFilter,
|
||||
// }) : super(key: key);
|
||||
|
||||
// final double height;
|
||||
// final bool enabled;
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||
// builder: (context, connectivityState) {
|
||||
// final hasInternetConnection = connectivityState.isConnected;
|
||||
// return SizedBox(
|
||||
// height: height,
|
||||
// child: Column(
|
||||
// mainAxisAlignment: MainAxisAlignment.start,
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// BlocBuilder<SavedViewCubit, SavedViewState>(
|
||||
// builder: (context, state) {
|
||||
// if (!state.hasLoaded) {
|
||||
// return _buildLoadingWidget(context);
|
||||
// }
|
||||
// if (state.value.isEmpty) {
|
||||
// return Text(S.of(context).savedViewsEmptyStateText);
|
||||
// }
|
||||
// return SizedBox(
|
||||
// height: 38,
|
||||
// child: ListView.separated(
|
||||
// itemCount: state.value.length,
|
||||
// scrollDirection: Axis.horizontal,
|
||||
// itemBuilder: (context, index) {
|
||||
// final view = state.value.values.elementAt(index);
|
||||
// return GestureDetector(
|
||||
// onLongPress: hasInternetConnection
|
||||
// ? () => _onDelete(context, view)
|
||||
// : null,
|
||||
// child: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
// builder: (context, docState) {
|
||||
// final view = state.value.values.toList()[index];
|
||||
// return FilterChip(
|
||||
// label: Text(
|
||||
// view.name,
|
||||
// ),
|
||||
// selected:
|
||||
// view.id == docState.selectedSavedViewId,
|
||||
// onSelected: enabled && hasInternetConnection
|
||||
// ? (isSelected) =>
|
||||
// _onSelected(isSelected, context, view)
|
||||
// : null,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// separatorBuilder: (context, index) => const SizedBox(
|
||||
// width: 4.0,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// BlocBuilder<SavedViewCubit, SavedViewState>(
|
||||
// builder: (context, state) {
|
||||
// return Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// Text(
|
||||
// S.of(context).savedViewsLabel,
|
||||
// style: Theme.of(context).textTheme.titleSmall,
|
||||
// ),
|
||||
// BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
// buildWhen: (previous, current) =>
|
||||
// previous.filter != current.filter,
|
||||
// builder: (context, docState) {
|
||||
// return TextButton.icon(
|
||||
// icon: const Icon(Icons.add),
|
||||
// onPressed: (enabled &&
|
||||
// state.hasLoaded &&
|
||||
// hasInternetConnection)
|
||||
// ? () =>
|
||||
// _onCreatePressed(context, docState.filter)
|
||||
// : null,
|
||||
// label: Text(S.of(context).savedViewCreateNewLabel),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ).padded(),
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
|
||||
// Widget _buildLoadingWidget(BuildContext context) {
|
||||
// return SizedBox(
|
||||
// height: 38,
|
||||
// width: MediaQuery.of(context).size.width,
|
||||
// child: Shimmer.fromColors(
|
||||
// baseColor: Theme.of(context).brightness == Brightness.light
|
||||
// ? Colors.grey[300]!
|
||||
// : Colors.grey[900]!,
|
||||
// highlightColor: Theme.of(context).brightness == Brightness.light
|
||||
// ? Colors.grey[100]!
|
||||
// : Colors.grey[600]!,
|
||||
// child: ListView(
|
||||
// scrollDirection: Axis.horizontal,
|
||||
// physics: const NeverScrollableScrollPhysics(),
|
||||
// children: [
|
||||
// FilterChip(
|
||||
// label: const SizedBox(width: 32),
|
||||
// onSelected: (_) {},
|
||||
// ),
|
||||
// const SizedBox(width: 4.0),
|
||||
// FilterChip(
|
||||
// label: const SizedBox(width: 64),
|
||||
// onSelected: (_) {},
|
||||
// ),
|
||||
// const SizedBox(width: 4.0),
|
||||
// FilterChip(
|
||||
// label: const SizedBox(width: 100),
|
||||
// onSelected: (_) {},
|
||||
// ),
|
||||
// const SizedBox(width: 4.0),
|
||||
// FilterChip(
|
||||
// label: const SizedBox(width: 32),
|
||||
// onSelected: (_) {},
|
||||
// ),
|
||||
// const SizedBox(width: 4.0),
|
||||
// FilterChip(
|
||||
// label: const SizedBox(width: 48),
|
||||
// onSelected: (_) {},
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
// void _onCreatePressed(BuildContext context, DocumentFilter filter) async {
|
||||
// final newView = await Navigator.of(context).push<SavedView?>(
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) => AddSavedViewPage(
|
||||
// currentFilter: filter,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// if (newView != null) {
|
||||
// try {
|
||||
// await context.read<SavedViewCubit>().add(newView);
|
||||
// } on PaperlessServerException catch (error, stackTrace) {
|
||||
// showErrorMessage(context, error, stackTrace);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// void _onSelected(
|
||||
// bool selectionIntent,
|
||||
// BuildContext context,
|
||||
// SavedView view,
|
||||
// ) async {
|
||||
// if (selectionIntent) {
|
||||
// context.read<DocumentsCubit>().selectView(view.id!);
|
||||
// } else {
|
||||
// context.read<DocumentsCubit>().unselectView();
|
||||
// context.read<DocumentsCubit>().resetFilter();
|
||||
// }
|
||||
// }
|
||||
|
||||
// void _onDelete(BuildContext context, SavedView view) async {
|
||||
// {
|
||||
// final delete = await showDialog<bool>(
|
||||
// context: context,
|
||||
// builder: (context) => ConfirmDeleteSavedViewDialog(view: view),
|
||||
// ) ??
|
||||
// false;
|
||||
// if (delete) {
|
||||
// try {
|
||||
// context.read<SavedViewCubit>().remove(view);
|
||||
// if (context.read<DocumentsCubit>().state.selectedSavedViewId ==
|
||||
// view.id) {
|
||||
// await context.read<DocumentsCubit>().resetFilter();
|
||||
// }
|
||||
// } on PaperlessServerException catch (error, stackTrace) {
|
||||
// showErrorMessage(context, error, stackTrace);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,47 @@
|
||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||
|
||||
part 'saved_view_details_cubit.g.dart';
|
||||
part 'saved_view_details_state.dart';
|
||||
|
||||
class SavedViewDetailsCubit extends HydratedCubit<SavedViewDetailsState>
|
||||
with DocumentPagingBlocMixin {
|
||||
@override
|
||||
final PaperlessDocumentsApi api;
|
||||
|
||||
@override
|
||||
final DocumentChangedNotifier notifier;
|
||||
|
||||
final SavedView savedView;
|
||||
SavedViewDetailsCubit(
|
||||
this.api,
|
||||
this.notifier, {
|
||||
required this.savedView,
|
||||
}) : super(const SavedViewDetailsState()) {
|
||||
notifier.subscribe(
|
||||
this,
|
||||
onDeleted: remove,
|
||||
onUpdated: replace,
|
||||
);
|
||||
updateFilter(filter: savedView.toDocumentFilter());
|
||||
}
|
||||
|
||||
void setViewType(ViewType viewType) {
|
||||
emit(state.copyWith(viewType: viewType));
|
||||
}
|
||||
|
||||
@override
|
||||
SavedViewDetailsState? fromJson(Map<String, dynamic> json) {
|
||||
return SavedViewDetailsState.fromJson(json);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic>? toJson(SavedViewDetailsState state) {
|
||||
return state.toJson();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
part of 'saved_view_details_cubit.dart';
|
||||
|
||||
class SavedViewDetailsState extends PagedDocumentsState {
|
||||
@JsonSerializable(ignoreUnannotated: true)
|
||||
class SavedViewDetailsState extends DocumentPagingState {
|
||||
@JsonKey()
|
||||
final ViewType viewType;
|
||||
|
||||
const SavedViewDetailsState({
|
||||
this.viewType = ViewType.list,
|
||||
super.filter,
|
||||
super.hasLoaded,
|
||||
super.isLoading,
|
||||
@@ -10,10 +15,8 @@ class SavedViewDetailsState extends PagedDocumentsState {
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
filter,
|
||||
hasLoaded,
|
||||
isLoading,
|
||||
value,
|
||||
viewType,
|
||||
...super.props,
|
||||
];
|
||||
|
||||
@override
|
||||
@@ -36,12 +39,19 @@ class SavedViewDetailsState extends PagedDocumentsState {
|
||||
bool? isLoading,
|
||||
List<PagedSearchResult<DocumentModel>>? value,
|
||||
DocumentFilter? filter,
|
||||
ViewType? viewType,
|
||||
}) {
|
||||
return SavedViewDetailsState(
|
||||
hasLoaded: hasLoaded ?? this.hasLoaded,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
value: value ?? this.value,
|
||||
filter: filter ?? this.filter,
|
||||
viewType: viewType ?? this.viewType,
|
||||
);
|
||||
}
|
||||
|
||||
factory SavedViewDetailsState.fromJson(Map<String, dynamic> json) =>
|
||||
_$SavedViewDetailsStateFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$SavedViewDetailsStateToJson(this);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/confirm_delete_saved_view_dialog.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart';
|
||||
import 'package:paperless_mobile/features/saved_view_details/cubit/saved_view_details_cubit.dart';
|
||||
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||
|
||||
class SavedViewDetailsPage extends StatefulWidget {
|
||||
final Future<void> Function(SavedView savedView) onDelete;
|
||||
const SavedViewDetailsPage({
|
||||
super.key,
|
||||
required this.onDelete,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SavedViewDetailsPage> createState() => _SavedViewDetailsPageState();
|
||||
}
|
||||
|
||||
class _SavedViewDetailsPageState extends State<SavedViewDetailsPage>
|
||||
with DocumentPagingViewMixin {
|
||||
@override
|
||||
final pagingScrollController = ScrollController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cubit = context.read<SavedViewDetailsCubit>();
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(cubit.savedView.name),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () async {
|
||||
final shouldDelete = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => ConfirmDeleteSavedViewDialog(
|
||||
view: cubit.savedView,
|
||||
),
|
||||
) ??
|
||||
false;
|
||||
if (shouldDelete) {
|
||||
await widget.onDelete(cubit.savedView);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
BlocBuilder<SavedViewDetailsCubit, SavedViewDetailsState>(
|
||||
builder: (context, state) {
|
||||
return ViewTypeSelectionWidget(
|
||||
viewType: state.viewType,
|
||||
onChanged: cubit.setViewType,
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: BlocBuilder<SavedViewDetailsCubit, SavedViewDetailsState>(
|
||||
builder: (context, state) {
|
||||
if (state.hasLoaded && state.documents.isEmpty) {
|
||||
return DocumentsEmptyState(state: state);
|
||||
}
|
||||
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||
builder: (context, connectivity) {
|
||||
return CustomScrollView(
|
||||
controller: pagingScrollController,
|
||||
slivers: [
|
||||
SliverAdaptiveDocumentsView(
|
||||
documents: state.documents,
|
||||
hasInternetConnection: connectivity.isConnected,
|
||||
isLabelClickable: false,
|
||||
isLoading: state.isLoading,
|
||||
hasLoaded: state.hasLoaded,
|
||||
onTap: (document) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
DocumentDetailsRoute.routeName,
|
||||
arguments: DocumentDetailsRouteArguments(
|
||||
document: document,
|
||||
isLabelClickable: false,
|
||||
),
|
||||
);
|
||||
},
|
||||
viewType: state.viewType,
|
||||
),
|
||||
if (state.hasLoaded && state.isLoading)
|
||||
const SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:paperless_mobile/features/login/services/authentication_service.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_state.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/color_scheme_option.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
part 'application_settings_cubit.g.dart';
|
||||
part 'application_settings_state.dart';
|
||||
|
||||
class ApplicationSettingsCubit extends HydratedCubit<ApplicationSettingsState> {
|
||||
final LocalAuthenticationService _localAuthenticationService;
|
||||
@@ -33,11 +38,6 @@ class ApplicationSettingsCubit extends HydratedCubit<ApplicationSettingsState> {
|
||||
_updateSettings(updatedSettings);
|
||||
}
|
||||
|
||||
void setViewType(ViewType viewType) {
|
||||
final updatedSettings = state.copyWith(preferredViewType: viewType);
|
||||
_updateSettings(updatedSettings);
|
||||
}
|
||||
|
||||
void setColorSchemeOption(ColorSchemeOption schemeOption) {
|
||||
final updatedSettings =
|
||||
state.copyWith(preferredColorSchemeOption: schemeOption);
|
||||
@@ -1,12 +1,4 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/color_scheme_option.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
part 'application_settings_state.g.dart';
|
||||
part of 'application_settings_cubit.dart';
|
||||
|
||||
///
|
||||
/// State holding the current application settings such as selected language, theme mode and more.
|
||||
@@ -20,14 +12,12 @@ class ApplicationSettingsState {
|
||||
final bool isLocalAuthenticationEnabled;
|
||||
final String preferredLocaleSubtag;
|
||||
final ThemeMode preferredThemeMode;
|
||||
final ViewType preferredViewType;
|
||||
final ColorSchemeOption preferredColorSchemeOption;
|
||||
|
||||
ApplicationSettingsState({
|
||||
required this.preferredLocaleSubtag,
|
||||
this.preferredThemeMode = ThemeMode.system,
|
||||
this.isLocalAuthenticationEnabled = false,
|
||||
this.preferredViewType = ViewType.list,
|
||||
this.preferredColorSchemeOption = ColorSchemeOption.classic,
|
||||
});
|
||||
|
||||
@@ -39,7 +29,6 @@ class ApplicationSettingsState {
|
||||
bool? isLocalAuthenticationEnabled,
|
||||
String? preferredLocaleSubtag,
|
||||
ThemeMode? preferredThemeMode,
|
||||
ViewType? preferredViewType,
|
||||
ColorSchemeOption? preferredColorSchemeOption,
|
||||
}) {
|
||||
return ApplicationSettingsState(
|
||||
@@ -48,7 +37,6 @@ class ApplicationSettingsState {
|
||||
preferredLocaleSubtag:
|
||||
preferredLocaleSubtag ?? this.preferredLocaleSubtag,
|
||||
preferredThemeMode: preferredThemeMode ?? this.preferredThemeMode,
|
||||
preferredViewType: preferredViewType ?? this.preferredViewType,
|
||||
preferredColorSchemeOption:
|
||||
preferredColorSchemeOption ?? this.preferredColorSchemeOption,
|
||||
);
|
||||
@@ -8,8 +8,8 @@ import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||
import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||
|
||||
@@ -48,14 +48,14 @@ class AccountSettingsDialog extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
Divider(),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
dense: true,
|
||||
leading: const Icon(Icons.person_add_rounded),
|
||||
title: Text(S.of(context).accountSettingsAddAnotherAccount),
|
||||
onTap: () {},
|
||||
),
|
||||
Divider(),
|
||||
const Divider(),
|
||||
FilledButton(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStatePropertyAll(
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/bloc/paperless_server_information_cubit.dart';
|
||||
import 'package:paperless_mobile/core/bloc/paperless_server_information_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/correspondent_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/document_type_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/storage_path_repository_state.dart';
|
||||
import 'package:paperless_mobile/core/repository/state/impl/tag_repository_state.dart';
|
||||
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/pages/application_settings_page.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/pages/security_settings_page.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/pages/storage_settings_page.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||
|
||||
class SettingsPage extends StatelessWidget {
|
||||
const SettingsPage({super.key});
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/features/login/services/authentication_service.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_state.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class BiometricAuthenticationSetting extends StatelessWidget {
|
||||
const BiometricAuthenticationSetting({super.key});
|
||||
|
||||
@@ -2,16 +2,13 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/constants.dart';
|
||||
import 'package:paperless_mobile/core/translation/color_scheme_option_localization_mapper.dart';
|
||||
import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
||||
import 'package:paperless_mobile/features/login/services/authentication_service.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_state.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/color_scheme_option.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/radio_settings_dialog.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/constants.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ColorSchemeOptionSetting extends StatelessWidget {
|
||||
const ColorSchemeOptionSetting({super.key});
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_state.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/radio_settings_dialog.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_state.dart';
|
||||
import 'package:paperless_mobile/features/settings/cubit/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/radio_settings_dialog.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/paged_documents_mixin.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/cubit/document_paging_bloc_mixin.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/cubit/paged_documents_state.dart';
|
||||
|
||||
part 'similar_documents_state.dart';
|
||||
|
||||
class SimilarDocumentsCubit extends Cubit<SimilarDocumentsState>
|
||||
with PagedDocumentsMixin {
|
||||
with DocumentPagingBlocMixin {
|
||||
final int documentId;
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
part of 'similar_documents_cubit.dart';
|
||||
|
||||
class SimilarDocumentsState extends PagedDocumentsState {
|
||||
class SimilarDocumentsState extends DocumentPagingState {
|
||||
const SimilarDocumentsState({
|
||||
super.filter,
|
||||
super.hasLoaded,
|
||||
|
||||
@@ -2,10 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/items/document_list_item.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart';
|
||||
import 'package:paperless_mobile/features/similar_documents/cubit/similar_documents_cubit.dart';
|
||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||
import 'package:paperless_mobile/routes/document_details_route.dart';
|
||||
@@ -17,13 +16,14 @@ class SimilarDocumentsView extends StatefulWidget {
|
||||
State<SimilarDocumentsView> createState() => _SimilarDocumentsViewState();
|
||||
}
|
||||
|
||||
class _SimilarDocumentsViewState extends State<SimilarDocumentsView> {
|
||||
final _scrollController = ScrollController();
|
||||
class _SimilarDocumentsViewState extends State<SimilarDocumentsView>
|
||||
with DocumentPagingViewMixin {
|
||||
@override
|
||||
final pagingScrollController = ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scrollController.addListener(_listenForLoadNewData);
|
||||
try {
|
||||
context.read<SimilarDocumentsCubit>().initialize();
|
||||
} 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
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SimilarDocumentsCubit, SimilarDocumentsState>(
|
||||
@@ -70,7 +50,7 @@ class _SimilarDocumentsViewState extends State<SimilarDocumentsView> {
|
||||
return BlocBuilder<ConnectivityCubit, ConnectivityState>(
|
||||
builder: (context, connectivity) {
|
||||
return CustomScrollView(
|
||||
controller: _scrollController,
|
||||
controller: pagingScrollController,
|
||||
slivers: [
|
||||
SliverAdaptiveDocumentsView(
|
||||
documents: state.documents,
|
||||
|
||||
Reference in New Issue
Block a user