Added quick actions to inbox, disabled created at field in document upload for now and added hint

This commit is contained in:
Anton Stubenbord
2023-01-15 17:42:27 +01:00
parent 31b6335f95
commit 6f1722bfd9
11 changed files with 191 additions and 59 deletions

View File

@@ -6,12 +6,12 @@ import 'package:paperless_mobile/generated/l10n.dart';
class HintCard extends StatelessWidget {
final String hintText;
final double elevation;
final VoidCallback onHintAcknowledged;
final VoidCallback? onHintAcknowledged;
final bool show;
const HintCard({
super.key,
required this.hintText,
required this.onHintAcknowledged,
this.onHintAcknowledged,
this.elevation = 1,
required this.show,
});
@@ -43,13 +43,16 @@ class HintCard extends StatelessWidget {
style: Theme.of(context).textTheme.bodySmall,
),
),
Align(
alignment: Alignment.bottomRight,
child: TextButton(
child: Text(S.of(context).genericAcknowledgeLabel),
onPressed: onHintAcknowledged,
),
),
if (onHintAcknowledged != null)
Align(
alignment: Alignment.bottomRight,
child: TextButton(
child: Text(S.of(context).genericAcknowledgeLabel),
onPressed: onHintAcknowledged,
),
)
else
Padding(padding: EdgeInsets.only(bottom: 24)),
],
).padded(),
).padded(),

View File

@@ -0,0 +1,5 @@
extension DateComparisons on DateTime {
bool isEqualToIgnoringDate(DateTime other) {
return day == other.day && month == other.month && year == other.year;
}
}

View File

@@ -11,6 +11,7 @@ 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/type/types.dart';
import 'package:paperless_mobile/core/widgets/hint_card.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart';
import 'package:paperless_mobile/features/edit_label/view/impl/add_correspondent_page.dart';
@@ -148,11 +149,14 @@ class _DocumentUploadPreparationPageState
}
}
},
title: Text(S
.of(context)
.documentUploadPageSynchronizeTitleAndFilenameLabel), //TODO: INTL
title: Text(
S
.of(context)
.documentUploadPageSynchronizeTitleAndFilenameLabel,
), //TODO: INTL
),
FormBuilderDateTimePicker(
enabled: false,
autovalidateMode: AutovalidateMode.always,
format: DateFormat("dd. MMMM yyyy"), //TODO: INTL
inputType: InputType.date,
@@ -164,6 +168,11 @@ class _DocumentUploadPreparationPageState
S.of(context).documentCreatedPropertyLabel + " *",
),
),
const HintCard(
hintText:
"Due to an apparent parsing bug with Paperless, setting the 'created at' date will cause the document consumption to fail! Therefore this field is disabled for now until this is fixed or I find a workaround!",
show: true,
),
LabelFormField<DocumentType>(
notAssignedSelectable: false,
formBuilderState: _formKey.currentState,

View File

@@ -64,7 +64,6 @@ class InboxCubit extends HydratedCubit<InboxState> {
/// Fetches inbox tag ids and loads the inbox items (documents).
///
Future<void> initializeInbox() async {
if (state.isLoaded) return;
final inboxTags = await _tagsRepository.findAll().then(
(tags) => tags.where((t) => t.isInboxTag ?? false).map((t) => t.id!),
);
@@ -206,13 +205,14 @@ class InboxCubit extends HydratedCubit<InboxState> {
}
void loadSuggestions() {
Future.wait(state.inboxItems
state.inboxItems
.whereNot((doc) => state.suggestions.containsKey(doc.id))
.map((e) => _documentsApi.findSuggestions(e))).then((results) {
emit(state.copyWith(suggestions: {
...state.suggestions,
for (var r in results) r.documentId!: r
}));
.map((e) => _documentsApi.findSuggestions(e))
.forEach((suggestion) async {
final s = await suggestion;
emit(state.copyWith(
suggestions: {...state.suggestions, s.documentId!: s},
));
});
}

View File

@@ -115,9 +115,24 @@ class _InboxPageState extends State<InboxPage> {
SliverList(
delegate: SliverChildBuilderDelegate(
childCount: entry.value.length,
(context, index) => _buildListItem(
entry.value[index],
),
(context, index) {
if (index < entry.value.length - 1) {
return Column(
children: [
_buildListItem(
entry.value[index],
),
const Divider(
indent: 16,
endIndent: 16,
),
],
);
}
return _buildListItem(
entry.value[index],
);
},
),
),
],

View File

@@ -1,9 +1,14 @@
import 'dart:developer';
import 'package:collection/collection.dart';
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/repository/provider/label_repositories_provider.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/extensions/date_time_extensions.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/view/pages/document_details_page.dart';
@@ -14,6 +19,7 @@ import 'package:paperless_mobile/features/inbox/bloc/state/inbox_state.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';
import 'package:paperless_mobile/util.dart';
class InboxItem extends StatefulWidget {
static const _a4AspectRatio = 1 / 1.4142;
@@ -36,6 +42,7 @@ class _InboxItemState extends State<InboxItem> {
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () async {
final returnedDocument = await Navigator.push<DocumentModel?>(
context,
@@ -57,8 +64,7 @@ class _InboxItemState extends State<InboxItem> {
widget.onDocumentUpdated(returnedDocument);
}
},
child: Container(
padding: const EdgeInsets.all(4),
child: SizedBox(
height: 180,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -74,29 +80,31 @@ class _InboxItemState extends State<InboxItem> {
alignment: Alignment.topCenter,
enableHero: false,
),
),
).padded(),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTitle(),
_buildTitle().paddedOnly(left: 8, right: 8, top: 8),
const Spacer(),
_buildCorrespondent(context),
_buildDocumentType(context),
_buildCorrespondent(context)
.paddedSymmetrically(horizontal: 8),
_buildDocumentType(context)
.paddedSymmetrically(horizontal: 8),
const Spacer(),
_buildTags(),
_buildTags().paddedOnly(left: 8, bottom: 8),
],
).padded(),
),
),
],
),
),
SizedBox(
height: 48,
height: 56,
child: _buildActions(context),
),
],
).padded(),
).paddedOnly(left: 8, top: 8, bottom: 8),
),
);
}
@@ -127,25 +135,51 @@ class _InboxItemState extends State<InboxItem> {
];
return BlocBuilder<InboxCubit, InboxState>(
builder: (context, state) {
return ListView(
scrollDirection: Axis.horizontal,
return Row(
children: [
...actions,
if (state.suggestions[widget.document.id] != null) ...[
SizedBox(width: 4),
..._buildSuggestionChips(
chipShape,
state.suggestions[widget.document.id]!,
state,
)
]
Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.bolt_outlined),
SizedBox(
width: 40,
child: Text(
S.of(context).inboxPageQuickActionsLabel,
textAlign: TextAlign.center,
maxLines: 2,
style: Theme.of(context).textTheme.labelSmall,
),
),
const VerticalDivider(
indent: 16,
endIndent: 16,
),
],
),
const SizedBox(width: 4.0),
Expanded(
child: ListView(
scrollDirection: Axis.horizontal,
children: [
...actions,
if (state.suggestions[widget.document.id] != null) ...[
const SizedBox(width: 4),
..._buildSuggestionChips(
chipShape,
state.suggestions[widget.document.id]!,
state,
)
]
],
),
),
],
);
},
);
}
ActionChip _buildAssignAsnAction(
Widget _buildAssignAsnAction(
RoundedRectangleBorder chipShape,
BuildContext context,
) {
@@ -247,6 +281,7 @@ class _InboxItemState extends State<InboxItem> {
) {
return [
...suggestions.correspondents
.whereNot((e) => widget.document.correspondent == e)
.map(
(e) => ActionChip(
avatar: const Icon(Icons.person_outline),
@@ -258,28 +293,78 @@ class _InboxItemState extends State<InboxItem> {
.updateDocument(widget.document.copyWith(
correspondent: e,
overwriteCorrespondent: true,
));
))
.then((value) => showSnackBar(
context,
S
.of(context)
.inboxPageSuggestionSuccessfullyAppliedMessage));
},
),
)
.toList(),
...suggestions.documentTypes
.whereNot((e) => widget.document.documentType == e)
.map(
(e) => ActionChip(
avatar: const Icon(Icons.description_outlined),
shape: chipShape,
label: Text(state.availableDocumentTypes[e]?.name ?? ''),
onPressed: () => context
.read<InboxCubit>()
.updateDocument(widget.document
.copyWith(documentType: e, overwriteDocumentType: true))
.then((value) => showSnackBar(
context,
S
.of(context)
.inboxPageSuggestionSuccessfullyAppliedMessage)),
),
)
.toList(),
...suggestions.tags
.whereNot((e) => widget.document.tags.contains(e))
.map(
(e) => ActionChip(
avatar: const Icon(Icons.label_outline),
shape: chipShape,
label: Text(state.availableTags[e]?.name ?? ''),
onPressed: () {
context
.read<InboxCubit>()
.updateDocument(widget.document.copyWith(
documentType: e,
overwriteDocumentType: true,
));
tags: {...widget.document.tags, e}.toList(),
overwriteTags: true,
))
.then((value) => showSnackBar(
context,
S
.of(context)
.inboxPageSuggestionSuccessfullyAppliedMessage));
},
),
)
.toList(),
];
...suggestions.dates
.whereNot((e) => widget.document.created.isEqualToIgnoringDate(e))
.map(
(e) => ActionChip(
avatar: const Icon(Icons.calendar_today_outlined),
shape: chipShape,
label: Text(
"${S.of(context).documentCreatedPropertyLabel}: ${DateFormat.yMd().format(e)}",
),
onPressed: () => context
.read<InboxCubit>()
.updateDocument(widget.document.copyWith(created: e))
.then((value) => showSnackBar(
context,
S
.of(context)
.inboxPageSuggestionSuccessfullyAppliedMessage)),
),
)
.toList(),
].expand((element) => [element, const SizedBox(width: 4)]).toList();
}
}

View File

@@ -364,6 +364,8 @@
"@genericActionUploadLabel": {},
"genericMessageOfflineText": "Jste offline.",
"@genericMessageOfflineText": {},
"inboxPageAssignAsnLabel": "",
"@inboxPageAssignAsnLabel": {},
"inboxPageDocumentRemovedMessageText": "Dokument odstraněn z inboxu.",
"@inboxPageDocumentRemovedMessageText": {},
"inboxPageMarkAllAsSeenConfirmationDialogText": "Opravdu chcete označit všechny dokumenty jako shlédnuté? Toto provede hromadnou úpravu a odstraní inbox tag u všech dokumentů.\nToto je nevratná akce! Opravdu chcete pokračovat?",
@@ -378,6 +380,10 @@
"@inboxPageNoNewDocumentsRefreshLabel": {},
"inboxPageNoNewDocumentsText": "Nemáte neshlédnuté dokumenty.",
"@inboxPageNoNewDocumentsText": {},
"inboxPageQuickActionsLabel": "",
"@inboxPageQuickActionsLabel": {},
"inboxPageSuggestionSuccessfullyAppliedMessage": "",
"@inboxPageSuggestionSuccessfullyAppliedMessage": {},
"inboxPageTodayText": "Dnes",
"@inboxPageTodayText": {},
"inboxPageUndoRemoveText": "VRÁTIT",
@@ -563,6 +569,5 @@
"verifyIdentityPageTitle": "",
"@verifyIdentityPageTitle": {},
"verifyIdentityPageVerifyIdentityButtonLabel": "",
"@verifyIdentityPageVerifyIdentityButtonLabel": {},
"inboxPageAssignAsnLabel": "Assign ASN"
"@verifyIdentityPageVerifyIdentityButtonLabel": {}
}

View File

@@ -364,6 +364,8 @@
"@genericActionUploadLabel": {},
"genericMessageOfflineText": "Du bist offline.",
"@genericMessageOfflineText": {},
"inboxPageAssignAsnLabel": "",
"@inboxPageAssignAsnLabel": {},
"inboxPageDocumentRemovedMessageText": "Dokument aus Posteingang entfernt.",
"@inboxPageDocumentRemovedMessageText": {},
"inboxPageMarkAllAsSeenConfirmationDialogText": "Bist Du sicher, dass Du alle Dokumente als gesehen markieren möchtest? Dadurch wird eine Massenbearbeitung durchgeführt, bei der alle Posteingangs-Tags von den Dokumenten entfernt werden. Diese Aktion kann nicht rückgängig gemacht werden! Möchtest Du trotzdem fortfahren?",
@@ -378,6 +380,10 @@
"@inboxPageNoNewDocumentsRefreshLabel": {},
"inboxPageNoNewDocumentsText": "Du hast keine ungesehenen Dokumente.",
"@inboxPageNoNewDocumentsText": {},
"inboxPageQuickActionsLabel": "Schnell Aktion",
"@inboxPageQuickActionsLabel": {},
"inboxPageSuggestionSuccessfullyAppliedMessage": "Vorschlag wurde erfolgreich angewendet.",
"@inboxPageSuggestionSuccessfullyAppliedMessage": {},
"inboxPageTodayText": "Heute",
"@inboxPageTodayText": {},
"inboxPageUndoRemoveText": "Undo",
@@ -563,6 +569,5 @@
"verifyIdentityPageTitle": "Verifiziere deine Identität",
"@verifyIdentityPageTitle": {},
"verifyIdentityPageVerifyIdentityButtonLabel": "Identität verifizieren",
"@verifyIdentityPageVerifyIdentityButtonLabel": {},
"inboxPageAssignAsnLabel": "Assign ASN"
"@verifyIdentityPageVerifyIdentityButtonLabel": {}
}

View File

@@ -364,6 +364,8 @@
"@genericActionUploadLabel": {},
"genericMessageOfflineText": "You're offline.",
"@genericMessageOfflineText": {},
"inboxPageAssignAsnLabel": "Assign ASN",
"@inboxPageAssignAsnLabel": {},
"inboxPageDocumentRemovedMessageText": "Document removed from inbox.",
"@inboxPageDocumentRemovedMessageText": {},
"inboxPageMarkAllAsSeenConfirmationDialogText": "Are you sure you want to mark all documents as seen? This will perform a bulk edit operation removing all inbox tags from the documents. This action is not reversible! Are you sure you want to continue?",
@@ -378,6 +380,10 @@
"@inboxPageNoNewDocumentsRefreshLabel": {},
"inboxPageNoNewDocumentsText": "You do not have unseen documents.",
"@inboxPageNoNewDocumentsText": {},
"inboxPageQuickActionsLabel": "Quick Action",
"@inboxPageQuickActionsLabel": {},
"inboxPageSuggestionSuccessfullyAppliedMessage": "Suggestion successfully applied.",
"@inboxPageSuggestionSuccessfullyAppliedMessage": {},
"inboxPageTodayText": "Today",
"@inboxPageTodayText": {},
"inboxPageUndoRemoveText": "Undo",
@@ -563,6 +569,5 @@
"verifyIdentityPageTitle": "Verify your identity",
"@verifyIdentityPageTitle": {},
"verifyIdentityPageVerifyIdentityButtonLabel": "Verify Identity",
"@verifyIdentityPageVerifyIdentityButtonLabel": {},
"inboxPageAssignAsnLabel": "Assign ASN"
"@verifyIdentityPageVerifyIdentityButtonLabel": {}
}

View File

@@ -51,9 +51,9 @@ class DocumentModel extends Equatable {
: id = json[idKey],
title = json[titleKey],
content = json[contentKey],
created = DateTime.parse(json[createdKey]).toLocal(),
modified = DateTime.parse(json[modifiedKey]).toLocal(),
added = DateTime.parse(json[addedKey]).toLocal(),
created = DateTime.parse(json[createdKey]),
modified = DateTime.parse(json[modifiedKey]),
added = DateTime.parse(json[addedKey]),
archiveSerialNumber = json[asnKey],
originalFileName = json[originalFileNameKey],
archivedFileName = json[archivedFileNameKey],

View File

@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.5.0+13
version: 1.5.1+14
environment:
sdk: '>=3.0.0-35.0.dev <4.0.0'