Merge pull request #265 from astubenbord/bugfix/incorrect-locale-in-datepicker

Bugfix/incorrect locale in datepicker
This commit is contained in:
Anton Stubenbord
2023-10-09 00:24:59 +02:00
committed by GitHub
57 changed files with 591 additions and 521 deletions
@@ -0,0 +1,2 @@
* Beheben von inkorrekter Lokalisierung in der App
* Hinzufügen der Unterscheidung zwischen US und GB Englisch
@@ -0,0 +1,2 @@
* Fixed incorrect localizations of dates and other texts
* Add discrimination between US and GB English (useful for date input)
@@ -146,7 +146,7 @@ class _ExtendedDateRangeDialogState extends State<ExtendedDateRangeDialog> {
)
: null,
),
format: DateFormat.yMd(),
format: DateFormat.yMd(Localizations.localeOf(context).toString()),
lastDate: _dateTimeMax(_before, DateTime.now()),
inputType: InputType.date,
onChanged: (after) {
@@ -174,7 +174,7 @@ class _ExtendedDateRangeDialogState extends State<ExtendedDateRangeDialog> {
)
: null,
),
format: DateFormat.yMd(),
format: DateFormat.yMd(Localizations.localeOf(context).toString()),
firstDate: _after,
lastDate: DateTime.now(),
onChanged: (before) {
@@ -79,7 +79,7 @@ class _FormBuilderExtendedDateRangePickerState
}
String _dateRangeQueryToString(DateRangeQuery query) {
final df = DateFormat.yMd();
final df = DateFormat.yMd(Localizations.localeOf(context).toString());
if (query is UnsetDateRangeQuery) {
return '';
} else if (query is AbsoluteDateRangeQuery) {
+63 -22
View File
@@ -4,6 +4,7 @@ import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:paperless_mobile/constants.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/widgets/paperless_logo.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
@@ -12,6 +13,7 @@ import 'package:paperless_mobile/features/sharing/cubit/receive_share_cubit.dart
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
import 'package:paperless_mobile/routes/typed/branches/upload_queue_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/settings_route.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
@@ -21,6 +23,10 @@ class AppDrawer extends StatelessWidget {
@override
Widget build(BuildContext context) {
final currentAccount = context.watch<LocalUserAccount>();
final username = currentAccount.paperlessUser.username;
final serverUrl =
currentAccount.serverUrl.replaceAll(RegExp(r'https?://'), '');
return SafeArea(
child: Drawer(
child: Column(
@@ -28,13 +34,43 @@ class AppDrawer extends StatelessWidget {
children: [
Row(
children: [
const PaperlessLogo.green(),
const PaperlessLogo.green(
width: 32,
height: 32,
),
Text(
"Paperless Mobile",
style: Theme.of(context).textTheme.titleMedium,
),
],
).padded(),
).paddedLTRB(8, 8, 8, 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
S.of(context)!.loggedInAs(username),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.labelMedium?.copyWith(
color: Theme.of(context)
.colorScheme
.onBackground
.withOpacity(0.5),
),
),
Text(
serverUrl,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.labelMedium?.copyWith(
color: Theme.of(context)
.colorScheme
.onBackground
.withOpacity(0.5),
),
),
],
).paddedSymmetrically(horizontal: 16),
const Divider(),
ListTile(
dense: true,
@@ -42,6 +78,31 @@ class AppDrawer extends StatelessWidget {
leading: const Icon(Icons.info_outline),
onTap: () => _showAboutDialog(context),
),
ListTile(
dense: true,
leading: const Icon(Icons.favorite_outline),
title: Text(S.of(context)!.donate),
onTap: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
icon: const Icon(Icons.favorite),
title: Text(S.of(context)!.donate),
content: Text(
S.of(context)!.donationDialogContent,
),
actionsAlignment: MainAxisAlignment.spaceBetween,
actions: [
Text("~ Anton"),
TextButton(
onPressed: Navigator.of(context).pop,
child: Text(S.of(context)!.gotIt),
),
],
),
);
},
),
ListTile(
dense: true,
leading: const Icon(Icons.bug_report_outlined),
@@ -62,26 +123,6 @@ class AppDrawer extends StatelessWidget {
);
},
),
ListTile(
dense: true,
leading: const Icon(Icons.favorite_outline),
title: Text(S.of(context)!.donate),
onTap: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
icon: const Icon(Icons.favorite),
title: Text(S.of(context)!.donate),
content: Text(
S.of(context)!.donationDialogContent,
),
actions: const [
Text("~ Anton"),
],
),
);
},
),
ListTile(
dense: true,
leading: SvgPicture.asset(
@@ -218,7 +218,7 @@ class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
throw Exception("An error occurred while downloading the document.");
}
Printing.layoutPdf(
name: state.document.title,
name: state.document.title ?? 'Document',
onLayout: (format) => file.readAsBytesSync(),
);
}
@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/date_symbol_data_local.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';
@@ -23,6 +25,7 @@ import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/helpers/connectivity_aware_action_wrapper.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
class DocumentDetailsPage extends StatefulWidget {
final bool isLabelClickable;
@@ -42,12 +45,18 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
static const double _itemSpacing = 24;
final _pagingScrollController = ScrollController();
@override
void didChangeDependencies() {
super.didChangeDependencies();
initializeDateFormatting(Localizations.localeOf(context).toString());
}
@override
Widget build(BuildContext context) {
final hasMultiUserSupport =
context.watch<LocalUserAccount>().hasMultiUserSupport;
final tabLength = 4 + (hasMultiUserSupport && false ? 1 : 0);
final title = context.watch<DocumentDetailsCubit>().state.document.title;
return WillPopScope(
onWillPop: () async {
Navigator.of(context)
@@ -74,11 +83,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
handle:
NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar(
title: Text(context
.watch<DocumentDetailsCubit>()
.state
.document
.title),
title: title != null ? Text(title) : null,
leading: const BackButton(),
pinned: true,
forceElevated: innerBoxIsScrolled,
@@ -103,6 +108,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
enableHero: false,
document: state.document,
fit: BoxFit.cover,
alignment: Alignment.topCenter,
),
),
Positioned.fill(
@@ -221,10 +227,6 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
document: state.document,
itemSpacing: _itemSpacing,
queryString: widget.titleAndContentQueryString,
availableCorrespondents: state.correspondents,
availableDocumentTypes: state.documentTypes,
availableTags: state.tags,
availableStoragePaths: state.storagePaths,
),
],
),
@@ -44,12 +44,14 @@ class _DocumentMetaDataWidgetState extends State<DocumentMetaDataWidget> {
document: widget.document,
).paddedOnly(bottom: widget.itemSpacing),
DetailsItem.text(
DateFormat().format(widget.document.modified),
DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(widget.document.modified),
context: context,
label: S.of(context)!.modifiedAt,
).paddedOnly(bottom: widget.itemSpacing),
DetailsItem.text(
DateFormat().format(widget.document.added),
DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(widget.document.added),
context: context,
label: S.of(context)!.addedAt,
).paddedOnly(bottom: widget.itemSpacing),
@@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/repository/label_repository.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';
@@ -12,81 +13,69 @@ import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
class DocumentOverviewWidget extends StatelessWidget {
final DocumentModel document;
final Map<int, Correspondent> availableCorrespondents;
final Map<int, DocumentType> availableDocumentTypes;
final Map<int, Tag> availableTags;
final Map<int, StoragePath> availableStoragePaths;
final String? queryString;
final double itemSpacing;
const DocumentOverviewWidget({
super.key,
required this.document,
this.queryString,
required this.itemSpacing,
required this.availableCorrespondents,
required this.availableDocumentTypes,
required this.availableTags,
required this.availableStoragePaths,
});
@override
Widget build(BuildContext context) {
final user = context.watch<LocalUserAccount>().paperlessUser;
final availableLabels = context.watch<LabelRepository>().state;
return SliverList.list(
children: [
DetailsItem(
label: S.of(context)!.title,
content: HighlightedText(
text: document.title,
highlights: queryString?.split(" ") ?? [],
style: Theme.of(context).textTheme.bodyLarge,
),
).paddedOnly(bottom: itemSpacing),
if (document.title != null)
DetailsItem(
label: S.of(context)!.title,
content: HighlightedText(
text: document.title!,
highlights: queryString?.split(" ") ?? [],
style: Theme.of(context).textTheme.bodyLarge,
),
).paddedOnly(bottom: itemSpacing),
DetailsItem.text(
DateFormat.yMMMMd().format(document.created),
DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(document.created),
context: context,
label: S.of(context)!.createdAt,
).paddedOnly(bottom: itemSpacing),
if (document.documentType != null &&
context
.watch<LocalUserAccount>()
.paperlessUser
.canViewDocumentTypes)
if (document.documentType != null && user.canViewDocumentTypes)
DetailsItem(
label: S.of(context)!.documentType,
content: LabelText<DocumentType>(
style: Theme.of(context).textTheme.bodyLarge,
label: availableDocumentTypes[document.documentType],
label: availableLabels.documentTypes[document.documentType],
),
).paddedOnly(bottom: itemSpacing),
if (document.correspondent != null &&
context
.watch<LocalUserAccount>()
.paperlessUser
.canViewCorrespondents)
if (document.correspondent != null && user.canViewCorrespondents)
DetailsItem(
label: S.of(context)!.correspondent,
content: LabelText<Correspondent>(
style: Theme.of(context).textTheme.bodyLarge,
label: availableCorrespondents[document.correspondent],
label: availableLabels.correspondents[document.correspondent],
),
).paddedOnly(bottom: itemSpacing),
if (document.storagePath != null &&
context.watch<LocalUserAccount>().paperlessUser.canViewStoragePaths)
if (document.storagePath != null && user.canViewStoragePaths)
DetailsItem(
label: S.of(context)!.storagePath,
content: LabelText<StoragePath>(
label: availableStoragePaths[document.storagePath],
label: availableLabels.storagePaths[document.storagePath],
),
).paddedOnly(bottom: itemSpacing),
if (document.tags.isNotEmpty &&
context.watch<LocalUserAccount>().paperlessUser.canViewTags)
if (document.tags.isNotEmpty && user.canViewTags)
DetailsItem(
label: S.of(context)!.tags,
content: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: TagsWidget(
isClickable: false,
tags: document.tags.map((e) => availableTags[e]!).toList(),
tags:
document.tags.map((e) => availableLabels.tags[e]!).toList(),
),
),
).paddedOnly(bottom: itemSpacing),
@@ -376,14 +376,16 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
label: Text(S.of(context)!.createdAt),
),
initialValue: initialCreatedAtDate,
format: DateFormat.yMMMMd(),
format: DateFormat.yMMMMd(Localizations.localeOf(context).toString()),
initialEntryMode: DatePickerEntryMode.calendar,
),
if (filteredSuggestions?.hasSuggestedDates ?? false)
_buildSuggestionsSkeleton<DateTime>(
suggestions: filteredSuggestions!.dates,
itemBuilder: (context, itemData) => ActionChip(
label: Text(DateFormat.yMMMd().format(itemData)),
label: Text(
DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(itemData)),
onPressed: () => _formKey.currentState?.fields[fkCreatedDate]
?.didChange(itemData),
),
@@ -26,6 +26,7 @@ import 'package:paperless_mobile/helpers/connectivity_aware_action_wrapper.dart'
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/helpers/permission_helpers.dart';
import 'package:paperless_mobile/routes/typed/branches/scanner_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
import 'package:path/path.dart' as p;
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
@@ -11,6 +11,7 @@ import 'package:paperless_mobile/features/documents/view/widgets/adaptive_docume
import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
class DocumentSearchPage extends StatefulWidget {
const DocumentSearchPage({super.key});
@@ -10,12 +10,10 @@ import 'package:paperless_mobile/features/settings/view/widgets/user_avatar.dart
import 'package:provider/provider.dart';
class SliverSearchBar extends StatelessWidget {
final bool floating;
final bool pinned;
final String titleText;
const SliverSearchBar({
super.key,
this.floating = false,
this.pinned = false,
required this.titleText,
});
@@ -68,7 +68,6 @@ class _DocumentUploadPreparationPageState
void initState() {
super.initState();
_syncTitleAndFilename = widget.filename == null && widget.title == null;
initializeDateFormatting();
}
@override
@@ -219,7 +218,8 @@ class _DocumentUploadPreparationPageState
// Created at
FormBuilderDateTimePicker(
autovalidateMode: AutovalidateMode.always,
format: DateFormat.yMMMMd(),
format: DateFormat.yMMMMd(
Localizations.localeOf(context).toString()),
inputType: InputType.date,
name: DocumentModel.createdKey,
initialValue: null,
@@ -25,6 +25,7 @@ import 'package:paperless_mobile/features/tasks/model/pending_tasks_notifier.dar
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
import 'package:sliver_tools/sliver_tools.dart';
class DocumentFilterIntent {
@@ -176,13 +177,9 @@ class _DocumentsPageState extends State<DocumentsPage> {
child: _showExtendedFab
? Row(
children: [
const Icon(
Icons.filter_alt_outlined,
),
const Icon(Icons.filter_alt_outlined),
const SizedBox(width: 8),
Text(
S.of(context)!.filterDocuments,
),
Text(S.of(context)!.filterDocuments),
],
)
: const Icon(Icons.filter_alt_outlined),
@@ -269,7 +266,6 @@ class _DocumentsPageState extends State<DocumentsPage> {
builder: (context, state) {
if (state.selection.isEmpty) {
return SliverSearchBar(
floating: true,
titleText: S.of(context)!.documents,
);
} else {
@@ -620,9 +616,9 @@ class _DocumentsPageState extends State<DocumentsPage> {
final cubit = context.read<DocumentsCubit>();
try {
switch (cubit.state.filter.storagePath){
case SetIdQueryParameter(id: var id):
if (id == pathId) {
switch (cubit.state.filter.storagePath) {
case SetIdQueryParameter(id: var id):
if (id == pathId) {
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(storagePath: const UnsetIdQueryParameter()),
@@ -634,14 +630,13 @@ class _DocumentsPageState extends State<DocumentsPage> {
);
}
break;
default:
cubit.updateCurrentFilter(
default:
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(storagePath: SetIdQueryParameter(id: pathId)),
filter.copyWith(storagePath: SetIdQueryParameter(id: pathId)),
);
break;
}
} on PaperlessApiException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
}
@@ -20,7 +20,7 @@ class DeleteDocumentConfirmationDialog extends StatelessWidget {
),
const SizedBox(height: 16),
Text(
document.title,
document.title ?? document.originalFileName ?? '-',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
@@ -4,6 +4,7 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/helpers/connectivity_aware_action_wrapper.dart';
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
import 'package:provider/provider.dart';
import 'package:shimmer/shimmer.dart';
@@ -89,7 +89,8 @@ class DocumentDetailedItem extends DocumentItem {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
DateFormat.yMMMMd().format(document.created),
DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(document.created),
style: Theme.of(context)
.textTheme
.bodySmall
@@ -110,7 +111,7 @@ class DocumentDetailedItem extends DocumentItem {
],
).paddedLTRB(8, 8, 8, 4),
Text(
document.title,
document.title ?? '-',
style: Theme.of(context).textTheme.titleMedium,
maxLines: 2,
overflow: TextOverflow.ellipsis,
@@ -113,7 +113,7 @@ class DocumentGridItem extends DocumentItem {
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
document.title,
document.title ?? '-',
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.titleMedium,
@@ -121,7 +121,9 @@ class DocumentGridItem extends DocumentItem {
),
const Spacer(),
Text(
DateFormat.yMMMd().format(document.created),
DateFormat.yMMMMd(
Localizations.localeOf(context).toString())
.format(document.created),
style: Theme.of(context).textTheme.bodySmall,
),
],
@@ -58,7 +58,7 @@ class DocumentListItem extends DocumentItem {
],
),
Text(
document.title,
document.title ?? '-',
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
@@ -81,7 +81,8 @@ class DocumentListItem extends DocumentItem {
maxLines: 1,
overflow: TextOverflow.ellipsis,
text: TextSpan(
text: DateFormat.yMMMd().format(document.created),
text: DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(document.created),
style: Theme.of(context)
.textTheme
.labelSmall
@@ -44,7 +44,7 @@ class BulkDeleteConfirmationDialog extends StatelessWidget {
return ListTile(
dense: true,
title: Text(
doc.title,
doc.title ?? doc.originalFileName ?? '-',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
@@ -7,6 +7,7 @@ import 'package:paperless_mobile/features/documents/view/widgets/selection/bulk_
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
class DocumentSelectionSliverAppBar extends StatelessWidget {
final DocumentsState state;
@@ -331,7 +331,8 @@ class _InboxPageState extends State<InboxPage>
if (doc.added.isYesterday) {
return S.of(context)!.yesterday;
}
return DateFormat.yMMMMd().format(doc.added);
return DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(doc.added);
},
);
}
@@ -16,6 +16,7 @@ import 'package:paperless_mobile/features/labels/view/widgets/label_text.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/helpers/connectivity_aware_action_wrapper.dart';
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
class InboxItemPlaceholder extends StatelessWidget {
const InboxItemPlaceholder({super.key});
@@ -350,7 +351,7 @@ class _InboxItemState extends State<InboxItem> {
Text _buildTitle() {
return Text(
widget.document.title,
widget.document.title ?? '-',
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: Theme.of(context).textTheme.titleSmall,
@@ -15,6 +15,7 @@ import 'package:paperless_mobile/features/labels/view/widgets/label_tab_view.dar
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/helpers/connectivity_aware_action_wrapper.dart';
import 'package:paperless_mobile/routes/typed/branches/labels_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
class LabelsPage extends StatefulWidget {
const LabelsPage({Key? key}) : super(key: key);
@@ -4,6 +4,7 @@ import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/helpers/format_helpers.dart';
import 'package:paperless_mobile/routes/typed/branches/labels_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
class LabelItem<T extends Label> extends StatelessWidget {
final T label;
+1 -1
View File
@@ -12,6 +12,7 @@ import 'package:paperless_mobile/features/saved_view_details/view/saved_view_pre
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
import 'package:paperless_mobile/routes/typed/branches/inbox_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
class LandingPage extends StatefulWidget {
const LandingPage({super.key});
@@ -34,7 +35,6 @@ class _LandingPageState extends State<LandingPage> {
SliverOverlapAbsorber(
handle: _searchBarHandle,
sliver: SliverSearchBar(
floating: true,
titleText: S.of(context)!.documents,
),
),
@@ -7,6 +7,7 @@ import 'package:paperless_mobile/features/linked_documents/cubit/linked_document
import 'package:paperless_mobile/features/paged_document_view/view/document_paging_view_mixin.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
class LinkedDocumentsPage extends StatefulWidget {
const LinkedDocumentsPage({super.key});
+1
View File
@@ -13,6 +13,7 @@ import 'package:paperless_mobile/features/login/model/login_form_credentials.dar
import 'package:paperless_mobile/features/login/view/add_account_page.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/login_route.dart';
class LoginPage extends StatelessWidget {
@@ -5,6 +5,7 @@ import 'package:paperless_mobile/core/config/hive/hive_extensions.dart';
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
import 'package:paperless_mobile/features/users/view/widgets/user_account_list_tile.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/login_route.dart';
class LoginToExistingAccountPage extends StatelessWidget {
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/login_route.dart';
import 'package:provider/provider.dart';
@@ -8,6 +8,7 @@ import 'package:paperless_mobile/features/landing/view/widgets/expansion_card.da
import 'package:paperless_mobile/features/saved_view_details/cubit/saved_view_preview_cubit.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
import 'package:provider/provider.dart';
class SavedViewPreview extends StatelessWidget {
@@ -7,6 +7,7 @@ import 'package:paperless_mobile/features/settings/view/dialogs/switch_account_d
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
import 'package:paperless_mobile/features/users/view/widgets/user_account_list_tile.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/add_account_route.dart';
import 'package:provider/provider.dart';
@@ -146,35 +147,7 @@ class ManageAccountsPage extends StatelessWidget {
Future<void> _onAddAccount(BuildContext context, String currentUser) async {
Navigator.of(context).pop();
AddAccountRoute().push<String>(context);
// final userId = await Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => AddAccountPage(
// titleText: S.of(context)!.addAccount,
// onSubmit: (context, username, password, serverUrl,
// clientCertificate) async {
// try {
// final userId =
// await context.read<AuthenticationCubit>().addAccount(
// credentials: LoginFormCredentials(
// username: username,
// password: password,
// ),
// clientCertificate: clientCertificate,
// serverUrl: serverUrl,
// //TODO: Ask user whether to enable biometric authentication
// enableBiometricAuthentication: false,
// );
// Navigator.of(context).pop<String?>(userId);
// } on PaperlessFormValidationException catch (error) {}
// },
// submitText: S.of(context)!.addAccount,
// ),
// ),
// );
const AddAccountRoute().push<String>(context);
}
void _onSwitchAccount(
@@ -13,7 +13,8 @@ class LanguageSelectionSetting extends StatefulWidget {
class _LanguageSelectionSettingState extends State<LanguageSelectionSetting> {
static const _languageOptions = {
'en': LanguageOption('English', true),
'en': LanguageOption('English (US)', true),
'en_GB': LanguageOption('English (GB)', true),
'de': LanguageOption('Deutsch', true),
'es': LanguageOption("Español", true),
'fr': LanguageOption('Français', true),
@@ -40,31 +40,34 @@ class _RadioSettingsDialogState<T> extends State<RadioSettingsDialog<T>> {
return AlertDialog(
actions: [
const DialogCancelButton(),
widget.confirmButton ??
DialogConfirmButton(
returnValue: _groupValue,
),
widget.confirmButton ?? DialogConfirmButton(returnValue: _groupValue),
],
title: widget.titleText != null ? Text(widget.titleText!) : null,
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (widget.descriptionText != null)
Text(widget.descriptionText!,
style: Theme.of(context).textTheme.bodySmall),
...widget.options.map(_buildOptionListTile),
if (widget.footer != null) widget.footer!,
],
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (widget.descriptionText != null)
Text(
widget.descriptionText!,
style: Theme.of(context).textTheme.bodySmall,
),
...widget.options.map(_buildOptionListTile).toList(),
if (widget.footer != null) widget.footer!,
],
),
),
);
}
Widget _buildOptionListTile(RadioOption<T> option) => RadioListTile<T>(
groupValue: _groupValue,
onChanged: (value) => setState(() => _groupValue = value!),
value: option.value,
title: Text(option.label),
);
Widget _buildOptionListTile(RadioOption<T> option) {
return RadioListTile<T>(
groupValue: _groupValue,
onChanged: (value) => setState(() => _groupValue = value!),
value: option.value,
title: Text(option.label),
);
}
}
class RadioOption<T> {
@@ -15,16 +15,26 @@ class UserAvatar extends StatelessWidget {
Colors.primaries[account.id.hashCode % Colors.primaries.length];
final foregroundColor =
backgroundColor.computeLuminance() > 0.5 ? Colors.black : Colors.white;
return CircleAvatar(
child: Text(
return Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: backgroundColor.shade900.withOpacity(0.4),
width: 2,
),
),
child: CircleAvatar(
child: Text(
(account.paperlessUser.fullName ?? account.paperlessUser.username)
.split(" ")
.take(2)
.map((e) => e.substring(0, 1))
.map((e) => e.toUpperCase())
.join("")),
backgroundColor: backgroundColor,
foregroundColor: foregroundColor,
.join(""),
),
backgroundColor: backgroundColor,
foregroundColor: foregroundColor,
),
);
}
}
@@ -23,6 +23,7 @@ import 'package:paperless_mobile/features/tasks/model/pending_tasks_notifier.dar
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/routes/typed/branches/scanner_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
import 'package:path/path.dart' as p;
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
@@ -9,6 +9,7 @@ import 'package:paperless_mobile/features/similar_documents/cubit/similar_docume
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
class SimilarDocumentsView extends StatefulWidget {
final ScrollController pagingScrollController;
+110 -110
View File
@@ -36,31 +36,27 @@ import 'package:paperless_mobile/features/notifications/services/local_notificat
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/navigation_keys.dart';
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
import 'package:paperless_mobile/routes/typed/branches/inbox_route.dart';
import 'package:paperless_mobile/routes/typed/branches/labels_route.dart';
import 'package:paperless_mobile/routes/typed/branches/landing_route.dart';
import 'package:paperless_mobile/routes/typed/branches/saved_views_route.dart';
import 'package:paperless_mobile/routes/typed/branches/scanner_route.dart';
import 'package:paperless_mobile/routes/typed/branches/upload_queue_route.dart';
import 'package:paperless_mobile/routes/typed/shells/provider_shell_route.dart';
import 'package:paperless_mobile/routes/typed/shells/scaffold_shell_route.dart';
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/add_account_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/logging_out_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/login_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/settings_route.dart';
import 'package:paperless_mobile/theme.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
String get defaultPreferredLocaleSubtag {
String preferredLocale = Platform.localeName.split("_").first;
if (!S.supportedLocales
.any((locale) => locale.languageCode == preferredLocale)) {
preferredLocale = 'en';
Locale get defaultPreferredLocale {
final deviceLocale = _stringToLocale(Platform.localeName);
if (S.supportedLocales.contains(deviceLocale)) {
return deviceLocale;
} else if (S.supportedLocales
.map((e) => e.languageCode)
.contains(deviceLocale.languageCode)) {
return Locale(deviceLocale.languageCode);
} else {
return const Locale('en');
}
return preferredLocale;
}
Map<String, Future<void> Function()> _migrations = {
@@ -106,7 +102,7 @@ Future<void> _initHive() async {
if (!globalSettingsBox.hasValue) {
await globalSettingsBox.setValue(
GlobalSettings(preferredLocaleSubtag: defaultPreferredLocaleSubtag),
GlobalSettings(preferredLocaleSubtag: defaultPreferredLocale.toString()),
);
}
}
@@ -197,12 +193,14 @@ void main() async {
value: localNotificationService),
Provider.value(value: DocumentChangedNotifier()),
],
child: MultiBlocProvider(
child: MultiProvider(
providers: [
BlocProvider<ConnectivityCubit>.value(value: connectivityCubit),
BlocProvider.value(value: authenticationCubit),
Provider<ConnectivityCubit>.value(value: connectivityCubit),
Provider.value(value: authenticationCubit),
],
child: GoRouterShell(apiFactory: apiFactory),
child: GoRouterShell(
apiFactory: apiFactory,
),
),
),
);
@@ -221,10 +219,7 @@ void main() async {
class GoRouterShell extends StatefulWidget {
final PaperlessApiFactory apiFactory;
const GoRouterShell({
super.key,
required this.apiFactory,
});
const GoRouterShell({super.key, required this.apiFactory});
@override
State<GoRouterShell> createState() => _GoRouterShellState();
@@ -267,50 +262,53 @@ class _GoRouterShellState extends State<GoRouterShell> {
routes: [
ShellRoute(
builder: (context, state, child) {
return BlocListener<AuthenticationCubit, AuthenticationState>(
listener: (context, state) {
switch (state) {
case UnauthenticatedState(
redirectToAccountSelection: var shouldRedirect
):
if (shouldRedirect) {
const LoginToExistingAccountRoute().go(context);
} else {
const LoginRoute().go(context);
}
break;
case RestoringSessionState():
const RestoringSessionRoute().go(context);
break;
case VerifyIdentityState(userId: var userId):
VerifyIdentityRoute(userId: userId).go(context);
break;
case SwitchingAccountsState():
const SwitchingAccountsRoute().push(context);
break;
case AuthenticatedState():
const LandingRoute().go(context);
break;
case AuthenticatingState state:
AuthenticatingRoute(state.currentStage.name).push(context);
break;
case LoggingOutState():
const LoggingOutRoute().go(context);
break;
case AuthenticationErrorState():
if (context.canPop()) {
context.pop();
}
// LoginRoute(
// $extra: errorState.clientCertificate,
// password: errorState.password,
// serverUrl: errorState.serverUrl,
// username: errorState.username,
// ).go(context);
break;
}
},
child: child,
return Provider.value(
value: widget.apiFactory,
child: BlocListener<AuthenticationCubit, AuthenticationState>(
listener: (context, state) {
switch (state) {
case UnauthenticatedState(
redirectToAccountSelection: var shouldRedirect
):
if (shouldRedirect) {
const LoginToExistingAccountRoute().go(context);
} else {
const LoginRoute().go(context);
}
break;
case RestoringSessionState():
const RestoringSessionRoute().go(context);
break;
case VerifyIdentityState(userId: var userId):
VerifyIdentityRoute(userId: userId).go(context);
break;
case SwitchingAccountsState():
const SwitchingAccountsRoute().push(context);
break;
case AuthenticatedState():
const LandingRoute().go(context);
break;
case AuthenticatingState state:
AuthenticatingRoute(state.currentStage.name).push(context);
break;
case LoggingOutState():
const LoggingOutRoute().go(context);
break;
case AuthenticationErrorState():
if (context.canPop()) {
context.pop();
}
// LoginRoute(
// $extra: errorState.clientCertificate,
// password: errorState.password,
// serverUrl: errorState.serverUrl,
// username: errorState.username,
// ).go(context);
break;
}
},
child: child,
),
);
},
navigatorKey: rootNavigatorKey,
@@ -318,44 +316,7 @@ class _GoRouterShellState extends State<GoRouterShell> {
$loginRoute,
$loggingOutRoute,
$addAccountRoute,
ShellRoute(
navigatorKey: outerShellNavigatorKey,
builder: ProviderShellRoute(widget.apiFactory).build,
routes: [
$settingsRoute,
$savedViewsRoute,
$uploadQueueRoute,
StatefulShellRoute(
navigatorContainerBuilder:
(context, navigationShell, children) {
return children[navigationShell.currentIndex];
},
builder: const ScaffoldShellRoute().builder,
branches: [
StatefulShellBranch(
navigatorKey: landingNavigatorKey,
routes: [$landingRoute],
),
StatefulShellBranch(
navigatorKey: documentsNavigatorKey,
routes: [$documentsRoute],
),
StatefulShellBranch(
navigatorKey: scannerNavigatorKey,
routes: [$scannerRoute],
),
StatefulShellBranch(
navigatorKey: labelsNavigatorKey,
routes: [$labelsRoute],
),
StatefulShellBranch(
navigatorKey: inboxNavigatorKey,
routes: [$inboxRoute],
),
],
),
],
),
$providerShellRoute,
],
),
],
@@ -365,6 +326,7 @@ class _GoRouterShellState extends State<GoRouterShell> {
Widget build(BuildContext context) {
return GlobalSettingsBuilder(
builder: (context, settings) {
final locale = _stringToLocale(settings.preferredLocaleSubtag);
return DynamicColorBuilder(
builder: (lightDynamic, darkDynamic) {
return MaterialApp.router(
@@ -382,10 +344,41 @@ class _GoRouterShellState extends State<GoRouterShell> {
preferredColorScheme: settings.preferredColorSchemeOption,
),
themeMode: settings.preferredThemeMode,
supportedLocales: S.supportedLocales,
locale: Locale.fromSubtags(
languageCode: settings.preferredLocaleSubtag,
),
supportedLocales: const [
Locale('en'),
Locale('de'),
Locale('en', 'GB'),
Locale('ca'),
Locale('cs'),
Locale('es'),
Locale('fr'),
Locale('pl'),
Locale('ru'),
Locale('tr'),
],
localeResolutionCallback: (locale, supportedLocales) {
if (locale == null) {
return supportedLocales.first;
}
final exactMatch = supportedLocales
.where((element) =>
element.languageCode == locale.languageCode &&
element.countryCode == locale.countryCode)
.toList();
if (exactMatch.isNotEmpty) {
return exactMatch.first;
}
final superLanguageMatch = supportedLocales
.where((element) =>
element.languageCode == locale.languageCode)
.toList();
if (superLanguageMatch.isNotEmpty) {
return superLanguageMatch.first;
}
return supportedLocales.first;
},
locale: locale,
localizationsDelegates: S.localizationsDelegates,
);
},
@@ -394,3 +387,10 @@ class _GoRouterShellState extends State<GoRouterShell> {
);
}
}
Locale _stringToLocale(String code) {
final codes = code.split("_");
final languageCode = codes[0];
final countryCode = codes.length > 1 ? codes[1] : null;
return Locale(languageCode, countryCode);
}
@@ -17,35 +17,11 @@ import 'package:paperless_mobile/routes/navigation_keys.dart';
import 'package:paperless_mobile/routes/routes.dart';
import 'package:paperless_mobile/theme.dart';
part 'documents_route.g.dart';
class DocumentsBranch extends StatefulShellBranchData {
static final GlobalKey<NavigatorState> $navigatorKey = documentsNavigatorKey;
const DocumentsBranch();
}
@TypedGoRoute<DocumentsRoute>(
path: "/documents",
name: R.documents,
routes: [
TypedGoRoute<EditDocumentRoute>(
path: "edit",
name: R.editDocument,
),
TypedGoRoute<DocumentDetailsRoute>(
path: "details",
name: R.documentDetails,
),
TypedGoRoute<DocumentPreviewRoute>(
path: "preview",
name: R.documentPreview,
),
TypedGoRoute<BulkEditDocumentsRoute>(
path: "bulk-edit",
name: R.bulkEditDocuments,
),
],
)
class DocumentsRoute extends GoRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
+7 -7
View File
@@ -1,14 +1,14 @@
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:paperless_mobile/features/inbox/view/pages/inbox_page.dart';
import 'package:paperless_mobile/routes/routes.dart';
import 'package:paperless_mobile/routes/navigation_keys.dart';
part 'inbox_route.g.dart';
class InboxBranch extends StatefulShellBranchData {
static final GlobalKey<NavigatorState> $navigatorKey = inboxNavigatorKey;
const InboxBranch();
}
@TypedGoRoute<InboxRoute>(
path: "/inbox",
name: R.inbox,
)
class InboxRoute extends GoRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
@@ -14,33 +14,12 @@ import 'package:paperless_mobile/features/labels/view/pages/labels_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/routes/navigation_keys.dart';
import 'package:paperless_mobile/routes/routes.dart';
part 'labels_route.g.dart';
class LabelsBranch extends StatefulShellBranchData {
static final GlobalKey<NavigatorState> $navigatorKey = labelsNavigatorKey;
const LabelsBranch();
}
@TypedGoRoute<LabelsRoute>(
path: "/labels",
name: R.labels,
routes: [
TypedGoRoute<EditLabelRoute>(
path: "edit",
name: R.editLabel,
),
TypedGoRoute<CreateLabelRoute>(
path: "create",
name: R.createLabel,
),
TypedGoRoute<LinkedDocumentsRoute>(
path: "linked-documents",
name: R.linkedDocuments,
),
],
)
class LabelsRoute extends GoRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
@@ -4,18 +4,12 @@ import 'package:paperless_mobile/features/landing/view/landing_page.dart';
import 'package:paperless_mobile/routes/navigation_keys.dart';
import 'package:paperless_mobile/routes/routes.dart';
part 'landing_route.g.dart';
class LandingBranch extends StatefulShellBranchData {
static final GlobalKey<NavigatorState> $navigatorKey = landingNavigatorKey;
const LandingBranch();
}
@TypedGoRoute<LandingRoute>(
path: "/landing",
name: R.landing,
)
class LandingRoute extends GoRouteData {
const LandingRoute();
@override
@@ -10,38 +10,12 @@ import 'package:paperless_mobile/features/document_upload/view/document_upload_p
import 'package:paperless_mobile/routes/navigation_keys.dart';
import 'package:paperless_mobile/routes/routes.dart';
part 'scanner_route.g.dart';
// @TypedStatefulShellBranch<ScannerBranch>(
// routes: [
// TypedGoRoute<ScannerRoute>(
// path: "/scanner",
// name: R.scanner,
// routes: [
// TypedGoRoute<DocumentUploadRoute>(
// path: "upload",
// name: R.uploadDocument,
// ),
// ],
// ),
// ],
// )
class ScannerBranch extends StatefulShellBranchData {
static final GlobalKey<NavigatorState> $navigatorKey = scannerNavigatorKey;
const ScannerBranch();
}
@TypedGoRoute<ScannerRoute>(
path: "/scanner",
name: R.scanner,
routes: [
TypedGoRoute<DocumentUploadRoute>(
path: "upload",
name: R.uploadDocument,
),
],
)
class ScannerRoute extends GoRouteData {
const ScannerRoute();
@@ -0,0 +1,155 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/widgets.dart';
import 'package:go_router/go_router.dart';
import 'package:hive_flutter/adapters.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/factory/paperless_api_factory.dart';
import 'package:paperless_mobile/features/home/view/home_shell_widget.dart';
import 'package:paperless_mobile/features/home/view/scaffold_with_navigation_bar.dart';
import 'package:paperless_mobile/features/sharing/cubit/receive_share_cubit.dart';
import 'package:paperless_mobile/features/sharing/view/widgets/event_listener_shell.dart';
import 'package:paperless_mobile/routes/navigation_keys.dart';
import 'package:paperless_mobile/routes/routes.dart';
import 'package:paperless_mobile/routes/typed/branches/documents_route.dart';
import 'package:paperless_mobile/routes/typed/branches/inbox_route.dart';
import 'package:paperless_mobile/routes/typed/branches/labels_route.dart';
import 'package:paperless_mobile/routes/typed/branches/landing_route.dart';
import 'package:paperless_mobile/routes/typed/branches/scanner_route.dart';
import 'package:paperless_mobile/routes/typed/shells/scaffold_shell_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/settings_route.dart';
import 'package:provider/provider.dart';
/// Key used to access
part 'authenticated_route.g.dart';
@TypedShellRoute<ProviderShellRoute>(
routes: [
TypedGoRoute<SettingsRoute>(
path: "/settings",
name: R.settings,
),
TypedStatefulShellRoute<ScaffoldShellRoute>(
branches: [
TypedStatefulShellBranch<LandingBranch>(
routes: [
TypedGoRoute<LandingRoute>(
path: "/landing",
name: R.landing,
)
],
),
TypedStatefulShellBranch<DocumentsBranch>(
routes: [
TypedGoRoute<DocumentsRoute>(
path: "/documents",
routes: [
TypedGoRoute<DocumentDetailsRoute>(
path: "details",
name: R.documentDetails,
),
TypedGoRoute<EditDocumentRoute>(
path: "edit",
name: R.editDocument,
),
TypedGoRoute<BulkEditDocumentsRoute>(
path: "bulk-edit",
name: R.bulkEditDocuments,
),
TypedGoRoute<DocumentPreviewRoute>(
path: 'preview',
name: R.documentPreview,
),
],
)
],
),
TypedStatefulShellBranch<ScannerBranch>(
routes: [
TypedGoRoute<ScannerRoute>(
path: "/scanner",
name: R.scanner,
routes: [
TypedGoRoute<DocumentUploadRoute>(
path: "upload",
name: R.uploadDocument,
),
],
),
],
),
TypedStatefulShellBranch<LabelsBranch>(
routes: [
TypedGoRoute<LabelsRoute>(
path: "/labels",
name: R.labels,
routes: [
TypedGoRoute<EditLabelRoute>(
path: "edit",
name: R.editLabel,
),
TypedGoRoute<CreateLabelRoute>(
path: "create",
name: R.createLabel,
),
TypedGoRoute<LinkedDocumentsRoute>(
path: "linked-documents",
name: R.linkedDocuments,
),
],
),
],
),
TypedStatefulShellBranch<InboxBranch>(
routes: [
TypedGoRoute<InboxRoute>(
path: "/inbox",
name: R.inbox,
)
],
),
],
),
],
)
class ProviderShellRoute extends ShellRouteData {
static final GlobalKey<NavigatorState> $navigatorKey = outerShellNavigatorKey;
const ProviderShellRoute();
@override
Widget builder(
BuildContext context,
GoRouterState state,
Widget navigator,
) {
final currentUserId = Hive.box<GlobalSettings>(HiveBoxes.globalSettings)
.getValue()!
.loggedInUserId;
if (currentUserId == null) {
return const SizedBox.shrink();
}
final authenticatedUser =
Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount).get(
currentUserId,
)!;
final apiFactory = context.read<PaperlessApiFactory>();
return HomeShellWidget(
localUserId: authenticatedUser.id,
paperlessApiVersion: authenticatedUser.apiVersion,
paperlessProviderFactory: apiFactory,
child: ChangeNotifierProvider(
create: (context) => ConsumptionChangeNotifier()
..loadFromConsumptionDirectory(userId: currentUserId),
child: EventListenerShell(
child: navigator,
),
),
);
}
}
@@ -1,84 +0,0 @@
import 'package:flutter/widgets.dart';
import 'package:go_router/go_router.dart';
import 'package:hive_flutter/adapters.dart';
import 'package:paperless_mobile/core/config/hive/hive_config.dart';
import 'package:paperless_mobile/core/database/tables/global_settings.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/factory/paperless_api_factory.dart';
import 'package:paperless_mobile/features/home/view/home_shell_widget.dart';
import 'package:paperless_mobile/features/sharing/cubit/receive_share_cubit.dart';
import 'package:paperless_mobile/features/sharing/view/widgets/event_listener_shell.dart';
import 'package:paperless_mobile/routes/navigation_keys.dart';
import 'package:provider/provider.dart';
/// Key used to access
//part 'provider_shell_route.g.dart';
//TODO: Wait for https://github.com/flutter/flutter/issues/127371 to be merged
// @TypedShellRoute<ProviderShellRoute>(
// routes: [
// TypedStatefulShellRoute(
// branches: [
// TypedStatefulShellBranch<LandingBranch>(
// routes: [
// TypedGoRoute<LandingRoute>(
// path: "/landing",
// // name: R.landing,
// )
// ],
// ),
// TypedStatefulShellBranch<DocumentsBranch>(
// routes: [
// TypedGoRoute<DocumentsRoute>(
// path: "/documents",
// routes: [
// TypedGoRoute<DocumentDetailsRoute>(
// path: "details",
// // name: R.documentDetails,
// ),
// TypedGoRoute<DocumentEditRoute>(
// path: "edit",
// // name: R.editDocument,
// ),
// ],
// )
// ],
// ),
// ],
// ),
// ],
// )
class ProviderShellRoute extends ShellRouteData {
final PaperlessApiFactory apiFactory;
static final GlobalKey<NavigatorState> $navigatorKey = outerShellNavigatorKey;
const ProviderShellRoute(this.apiFactory);
Widget build(
BuildContext context,
GoRouterState state,
Widget navigator,
) {
final currentUserId = Hive.box<GlobalSettings>(HiveBoxes.globalSettings)
.getValue()!
.loggedInUserId;
if (currentUserId == null) {
return const SizedBox.shrink();
}
final authenticatedUser =
Hive.box<LocalUserAccount>(HiveBoxes.localUserAccount).get(
currentUserId,
)!;
return HomeShellWidget(
localUserId: authenticatedUser.id,
paperlessApiVersion: authenticatedUser.apiVersion,
paperlessProviderFactory: apiFactory,
child: ChangeNotifierProvider(
create: (context) => ConsumptionChangeNotifier()
..loadFromConsumptionDirectory(userId: currentUserId),
child: EventListenerShell(child: navigator),
),
);
}
}
@@ -8,6 +8,12 @@ import 'package:paperless_mobile/features/home/view/scaffold_with_navigation_bar
class ScaffoldShellRoute extends StatefulShellRouteData {
const ScaffoldShellRoute();
static Widget $navigatorContainerBuilder(BuildContext context,
StatefulNavigationShell navigationShell, List<Widget> children) {
return children[navigationShell.currentIndex];
}
@override
Widget builder(
BuildContext context,
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/model/info_message_exception.dart';
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
@@ -23,58 +23,61 @@ class AddAccountRoute extends GoRouteData {
const AddAccountRoute();
static final $parentNavigatorKey = rootNavigatorKey;
@override
Widget build(BuildContext context, GoRouterState state) {
return AddAccountPage(
titleText: S.of(context)!.addAccount,
onSubmit:
(context, username, password, serverUrl, clientCertificate) async {
try {
final userId = await context.read<AuthenticationCubit>().addAccount(
credentials: LoginFormCredentials(
username: username,
password: password,
),
clientCertificate: clientCertificate,
serverUrl: serverUrl,
enableBiometricAuthentication: false,
locale: Localizations.localeOf(context).languageCode,
);
final shoudSwitch = await showDialog<bool>(
context: context,
builder: (context) => const SwitchAccountDialog(),
) ??
false;
if (shoudSwitch) {
await context.read<AuthenticationCubit>().switchAccount(userId);
} else {
while (context.canPop()) {
context.pop();
Page<void> buildPage(BuildContext context, GoRouterState state) {
return NoTransitionPage(
child: AddAccountPage(
titleText: S.of(context)!.addAccount,
onSubmit:
(context, username, password, serverUrl, clientCertificate) async {
try {
final userId = await context.read<AuthenticationCubit>().addAccount(
credentials: LoginFormCredentials(
username: username,
password: password,
),
clientCertificate: clientCertificate,
serverUrl: serverUrl,
enableBiometricAuthentication: false,
locale: Intl.getCurrentLocale(),
);
final shoudSwitch = await showDialog<bool>(
context: context,
builder: (context) => const SwitchAccountDialog(),
) ??
false;
if (shoudSwitch) {
await context.read<AuthenticationCubit>().switchAccount(userId);
} else {
while (context.canPop()) {
context.pop();
}
}
}
} on PaperlessApiException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
// context.pop();
} on PaperlessFormValidationException catch (exception, stackTrace) {
if (exception.hasUnspecificErrorMessage()) {
showLocalizedError(context, exception.unspecificErrorMessage()!);
} on PaperlessApiException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
// context.pop();
} on PaperlessFormValidationException catch (exception, stackTrace) {
if (exception.hasUnspecificErrorMessage()) {
showLocalizedError(context, exception.unspecificErrorMessage()!);
// context.pop();
} else {
showGenericError(
context,
exception.validationMessages.values.first,
stackTrace,
); //TODO: Check if we can show error message directly on field here.
}
} on InfoMessageException catch (error) {
showInfoMessage(context, error);
// context.pop();
} catch (unknownError, stackTrace) {
showGenericError(context, unknownError.toString(), stackTrace);
// context.pop();
} else {
showGenericError(
context,
exception.validationMessages.values.first,
stackTrace,
); //TODO: Check if we can show error message directly on field here.
}
} on InfoMessageException catch (error) {
showInfoMessage(context, error);
// context.pop();
} catch (unknownError, stackTrace) {
showGenericError(context, unknownError.toString(), stackTrace);
// context.pop();
}
},
submitText: S.of(context)!.addAccount,
},
submitText: S.of(context)!.addAccount,
),
);
}
}
@@ -1,7 +1,6 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:paperless_mobile/routes/navigation_keys.dart';
import 'package:paperless_mobile/routes/routes.dart';
part 'logging_out_route.g.dart';
@@ -11,12 +10,16 @@ part 'logging_out_route.g.dart';
name: R.loggingOut,
)
class LoggingOutRoute extends GoRouteData {
static final $parentNavigatorKey = rootNavigatorKey;
const LoggingOutRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return Scaffold(
body: Center(
child: Text("Logging out..."),
Page<void> buildPage(BuildContext context, GoRouterState state) {
return const NoTransitionPage(
child: Scaffold(
body: Center(
child: Text("Logging out..."), //TODO: INTL
),
),
);
}
+27 -13
View File
@@ -14,7 +14,6 @@ import 'package:paperless_mobile/features/login/view/widgets/login_transition_pa
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
import 'package:paperless_mobile/routes/navigation_keys.dart';
import 'package:paperless_mobile/routes/routes.dart';
part 'login_route.g.dart';
@TypedGoRoute<LoginRoute>(
@@ -80,10 +79,13 @@ class SwitchingAccountsRoute extends GoRouteData {
static final $parentNavigatorKey = rootNavigatorKey;
const SwitchingAccountsRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return LoginTransitionPage(
text: S.of(context)!.switchingAccountsPleaseWait,
Page<void> buildPage(BuildContext context, GoRouterState state) {
return NoTransitionPage(
child: LoginTransitionPage(
text: S.of(context)!.switchingAccountsPleaseWait,
),
);
}
}
@@ -93,8 +95,9 @@ class AuthenticatingRoute extends GoRouteData {
final String checkLoginStageName;
const AuthenticatingRoute(this.checkLoginStageName);
@override
Widget build(BuildContext context, GoRouterState state) {
Page<void> buildPage(BuildContext context, GoRouterState state) {
final stage = AuthenticatingStage.values.byName(checkLoginStageName);
final text = switch (stage) {
AuthenticatingStage.authenticating => S.of(context)!.authenticatingDots,
@@ -103,8 +106,11 @@ class AuthenticatingRoute extends GoRouteData {
AuthenticatingStage.fetchingUserInformation =>
S.of(context)!.fetchingUserInformation,
};
return LoginTransitionPage(text: text);
return NoTransitionPage(
child: LoginTransitionPage(
text: text,
),
);
}
}
@@ -115,8 +121,10 @@ class VerifyIdentityRoute extends GoRouteData {
const VerifyIdentityRoute({required this.userId});
@override
Widget build(BuildContext context, GoRouterState state) {
return VerifyIdentityPage(userId: userId);
Page<void> buildPage(BuildContext context, GoRouterState state) {
return NoTransitionPage(
child: VerifyIdentityPage(userId: userId),
);
}
}
@@ -134,8 +142,10 @@ class LoginToExistingAccountRoute extends GoRouteData {
}
@override
Widget build(BuildContext context, GoRouterState state) {
return const LoginToExistingAccountPage();
Page<void> buildPage(BuildContext context, GoRouterState state) {
return const NoTransitionPage(
child: LoginToExistingAccountPage(),
);
}
}
@@ -145,7 +155,11 @@ class RestoringSessionRoute extends GoRouteData {
const RestoringSessionRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return LoginTransitionPage(text: S.of(context)!.restoringSession);
Page<void> buildPage(BuildContext context, GoRouterState state) {
return NoTransitionPage(
child: LoginTransitionPage(
text: S.of(context)!.restoringSession,
),
);
}
}
@@ -6,14 +6,9 @@ import 'package:paperless_mobile/routes/navigation_keys.dart';
import 'package:paperless_mobile/routes/routes.dart';
import 'package:paperless_mobile/theme.dart';
part 'settings_route.g.dart';
@TypedGoRoute<SettingsRoute>(
path: "/settings",
name: R.settings,
)
class SettingsRoute extends GoRouteData {
static final GlobalKey<NavigatorState> $parentNavigatorKey = outerShellNavigatorKey;
static final GlobalKey<NavigatorState> $parentNavigatorKey =
outerShellNavigatorKey;
@override
Widget build(BuildContext context, GoRouterState state) {
@@ -0,0 +1,9 @@
import 'package:paperless_mobile/generated/l10n/app_localizations_en.dart';
class SEnGb extends SEn {
SEnGb() : super('en_GB');
}
class SEnUs extends SEn {
SEnUs() : super('en_US');
}
@@ -185,7 +185,7 @@ class DocumentFilter extends Equatable {
added.matches(document.added) &&
modified.matches(document.modified) &&
query.matches(
title: document.title,
title: document.title ?? '',
content: document.content,
asn: document.archiveSerialNumber,
);
@@ -26,7 +26,7 @@ class DocumentModel extends Equatable {
static const storagePathKey = 'storage_path';
final int id;
final String title;
final String? title;
final String? content;
final Iterable<int> tags;
final int? documentType;
@@ -71,7 +71,8 @@ class DocumentModel extends Equatable {
this.permissions,
});
factory DocumentModel.fromJson(Map<String, dynamic> json) => _$DocumentModelFromJson(json);
factory DocumentModel.fromJson(Map<String, dynamic> json) =>
_$DocumentModelFromJson(json);
Map<String, dynamic> toJson() => _$DocumentModelToJson(this);
@@ -94,15 +95,17 @@ class DocumentModel extends Equatable {
title: title ?? this.title,
content: content ?? this.content,
documentType: documentType != null ? documentType() : this.documentType,
correspondent: correspondent != null ? correspondent() : this.correspondent,
correspondent:
correspondent != null ? correspondent() : this.correspondent,
storagePath: storagePath != null ? storagePath() : this.storagePath,
tags: tags ?? this.tags,
created: created ?? this.created,
modified: modified ?? this.modified,
added: added ?? this.added,
originalFileName: originalFileName ?? this.originalFileName,
archiveSerialNumber:
archiveSerialNumber != null ? archiveSerialNumber() : this.archiveSerialNumber,
archiveSerialNumber: archiveSerialNumber != null
? archiveSerialNumber()
: this.archiveSerialNumber,
archivedFileName: archivedFileName ?? this.archivedFileName,
);
}
+2 -2
View File
@@ -748,10 +748,10 @@ packages:
dependency: "direct dev"
description:
name: go_router_builder
sha256: "89585f7cf2ddd35a3f05908c5bb54339d3f891fc5aac4f30e2864469d7ddc92b"
sha256: b004ed761578fd1326054ff9c97daaf7b94f109b24cad843ca8bd349a810f947
url: "https://pub.dev"
source: hosted
version: "2.3.1"
version: "2.3.3"
graphs:
dependency: transitive
description:
+2 -3
View File
@@ -15,7 +15,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: 3.0.2+49
version: 3.0.3+50
environment:
sdk: ">=3.0.0 <4.0.0"
@@ -118,8 +118,7 @@ dev_dependencies:
hive_generator: ^2.0.1
mock_server:
path: packages/mock_server
go_router_builder: ^2.2.4
go_router_builder: ^2.3.3
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
echo "Updating source language..."
crowdin download sources --identity=../crowdin_credentials.yml --config ../crowdin.yml --no-preserve-hierarchy
echo "Updating translations..."
crowdin download --identity=../crowdin_credentials.yml --config ../crowdin.yml