mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-06 03:15:48 -06:00
feat: Allow empty document titles, fix saved view routes not being accessible
This commit is contained in:
@@ -214,7 +214,7 @@ class DocumentDetailsCubit extends Cubit<DocumentDetailsState> {
|
||||
throw Exception("An error occurred while downloading the document.");
|
||||
}
|
||||
Printing.layoutPdf(
|
||||
name: state.document.title ?? 'Document',
|
||||
name: state.document.title,
|
||||
onLayout: (format) => file.readAsBytesSync(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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/routes/typed/branches/documents_route.dart';
|
||||
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
|
||||
import 'package:paperless_mobile/theme.dart';
|
||||
|
||||
class DocumentDetailsPage extends StatefulWidget {
|
||||
final bool isLabelClickable;
|
||||
@@ -57,136 +58,100 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
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)
|
||||
.pop(context.read<DocumentDetailsCubit>().state.document);
|
||||
return false;
|
||||
},
|
||||
child: DefaultTabController(
|
||||
length: tabLength,
|
||||
child: BlocListener<ConnectivityCubit, ConnectivityState>(
|
||||
listenWhen: (previous, current) =>
|
||||
!previous.isConnected && current.isConnected,
|
||||
listener: (context, state) {
|
||||
context.read<DocumentDetailsCubit>().loadMetaData();
|
||||
},
|
||||
child: Scaffold(
|
||||
extendBodyBehindAppBar: false,
|
||||
floatingActionButtonLocation:
|
||||
FloatingActionButtonLocation.endDocked,
|
||||
floatingActionButton: _buildEditButton(),
|
||||
bottomNavigationBar: _buildBottomAppBar(),
|
||||
body: NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||
SliverOverlapAbsorber(
|
||||
handle:
|
||||
NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
sliver: SliverAppBar(
|
||||
title: title != null ? Text(title) : null,
|
||||
leading: const BackButton(),
|
||||
pinned: true,
|
||||
forceElevated: innerBoxIsScrolled,
|
||||
collapsedHeight: kToolbarHeight,
|
||||
expandedHeight: 250.0,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
background: BlocBuilder<DocumentDetailsCubit,
|
||||
DocumentDetailsState>(
|
||||
builder: (context, state) {
|
||||
return Hero(
|
||||
tag: "thumb_${state.document.id}",
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
DocumentPreviewRoute($extra: state.document)
|
||||
.push(context);
|
||||
},
|
||||
child: Stack(
|
||||
alignment: Alignment.topCenter,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: DocumentPreview(
|
||||
enableHero: false,
|
||||
document: state.document,
|
||||
fit: BoxFit.cover,
|
||||
alignment: Alignment.topCenter,
|
||||
return AnnotatedRegion(
|
||||
value: buildOverlayStyle(
|
||||
Theme.of(context),
|
||||
systemNavigationBarColor: Theme.of(context).bottomAppBarTheme.color,
|
||||
),
|
||||
child: WillPopScope(
|
||||
onWillPop: () async {
|
||||
Navigator.of(context)
|
||||
.pop(context.read<DocumentDetailsCubit>().state.document);
|
||||
return false;
|
||||
},
|
||||
child: DefaultTabController(
|
||||
length: tabLength,
|
||||
child: BlocListener<ConnectivityCubit, ConnectivityState>(
|
||||
listenWhen: (previous, current) =>
|
||||
!previous.isConnected && current.isConnected,
|
||||
listener: (context, state) {
|
||||
context.read<DocumentDetailsCubit>().loadMetaData();
|
||||
},
|
||||
child: Scaffold(
|
||||
extendBodyBehindAppBar: false,
|
||||
floatingActionButtonLocation:
|
||||
FloatingActionButtonLocation.endDocked,
|
||||
floatingActionButton: _buildEditButton(),
|
||||
bottomNavigationBar: _buildBottomAppBar(),
|
||||
body: NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||
SliverOverlapAbsorber(
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
|
||||
context),
|
||||
sliver: SliverAppBar(
|
||||
title: Text(title),
|
||||
leading: const BackButton(),
|
||||
pinned: true,
|
||||
forceElevated: innerBoxIsScrolled,
|
||||
collapsedHeight: kToolbarHeight,
|
||||
expandedHeight: 250.0,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
background: BlocBuilder<DocumentDetailsCubit,
|
||||
DocumentDetailsState>(
|
||||
builder: (context, state) {
|
||||
return Hero(
|
||||
tag: "thumb_${state.document.id}",
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
DocumentPreviewRoute($extra: state.document)
|
||||
.push(context);
|
||||
},
|
||||
child: Stack(
|
||||
alignment: Alignment.topCenter,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: DocumentPreview(
|
||||
enableHero: false,
|
||||
document: state.document,
|
||||
fit: BoxFit.cover,
|
||||
alignment: Alignment.topCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
stops: [0.2, 0.4],
|
||||
colors: [
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.background
|
||||
.withOpacity(0.6),
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.background
|
||||
.withOpacity(0.3),
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
Positioned.fill(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
stops: [0.2, 0.4],
|
||||
colors: [
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.background
|
||||
.withOpacity(0.6),
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.background
|
||||
.withOpacity(0.3),
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
bottom: ColoredTabBar(
|
||||
tabBar: TabBar(
|
||||
isScrollable: true,
|
||||
tabs: [
|
||||
Tab(
|
||||
child: Text(
|
||||
S.of(context)!.overview,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
child: Text(
|
||||
S.of(context)!.content,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
child: Text(
|
||||
S.of(context)!.metaData,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
child: Text(
|
||||
S.of(context)!.similarDocuments,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (hasMultiUserSupport && false)
|
||||
bottom: ColoredTabBar(
|
||||
tabBar: TabBar(
|
||||
isScrollable: true,
|
||||
tabs: [
|
||||
Tab(
|
||||
child: Text(
|
||||
"Permissions",
|
||||
S.of(context)!.overview,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
@@ -194,80 +159,111 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
Tab(
|
||||
child: Text(
|
||||
S.of(context)!.content,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
child: Text(
|
||||
S.of(context)!.metaData,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
child: Text(
|
||||
S.of(context)!.similarDocuments,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (hasMultiUserSupport && false)
|
||||
Tab(
|
||||
child: Text(
|
||||
"Permissions",
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
body: BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>(
|
||||
builder: (context, state) {
|
||||
return BlocProvider(
|
||||
create: (context) => SimilarDocumentsCubit(
|
||||
context.read(),
|
||||
context.read(),
|
||||
context.read(),
|
||||
context.read(),
|
||||
documentId: state.document.id,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
horizontal: 16,
|
||||
],
|
||||
body: BlocBuilder<DocumentDetailsCubit, DocumentDetailsState>(
|
||||
builder: (context, state) {
|
||||
return BlocProvider(
|
||||
create: (context) => SimilarDocumentsCubit(
|
||||
context.read(),
|
||||
context.read(),
|
||||
context.read(),
|
||||
context.read(),
|
||||
documentId: state.document.id,
|
||||
),
|
||||
child: TabBarView(
|
||||
children: [
|
||||
CustomScrollView(
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView
|
||||
.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
DocumentOverviewWidget(
|
||||
document: state.document,
|
||||
itemSpacing: _itemSpacing,
|
||||
queryString: widget.titleAndContentQueryString,
|
||||
),
|
||||
],
|
||||
),
|
||||
CustomScrollView(
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView
|
||||
.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
DocumentContentWidget(
|
||||
isFullContentLoaded: state.isFullContentLoaded,
|
||||
document: state.document,
|
||||
queryString: widget.titleAndContentQueryString,
|
||||
),
|
||||
],
|
||||
),
|
||||
CustomScrollView(
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView
|
||||
.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
DocumentMetaDataWidget(
|
||||
document: state.document,
|
||||
itemSpacing: _itemSpacing,
|
||||
),
|
||||
],
|
||||
),
|
||||
CustomScrollView(
|
||||
controller: _pagingScrollController,
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView
|
||||
.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
SimilarDocumentsView(
|
||||
pagingScrollController: _pagingScrollController,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (hasMultiUserSupport && false)
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
horizontal: 16,
|
||||
),
|
||||
child: TabBarView(
|
||||
children: [
|
||||
CustomScrollView(
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView
|
||||
.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
DocumentOverviewWidget(
|
||||
document: state.document,
|
||||
itemSpacing: _itemSpacing,
|
||||
queryString:
|
||||
widget.titleAndContentQueryString,
|
||||
),
|
||||
],
|
||||
),
|
||||
CustomScrollView(
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView
|
||||
.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
DocumentContentWidget(
|
||||
isFullContentLoaded:
|
||||
state.isFullContentLoaded,
|
||||
document: state.document,
|
||||
queryString:
|
||||
widget.titleAndContentQueryString,
|
||||
),
|
||||
],
|
||||
),
|
||||
CustomScrollView(
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView
|
||||
.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
DocumentMetaDataWidget(
|
||||
document: state.document,
|
||||
itemSpacing: _itemSpacing,
|
||||
),
|
||||
],
|
||||
),
|
||||
CustomScrollView(
|
||||
controller: _pagingScrollController,
|
||||
slivers: [
|
||||
@@ -275,16 +271,32 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
|
||||
handle: NestedScrollView
|
||||
.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
DocumentPermissionsWidget(
|
||||
document: state.document,
|
||||
SimilarDocumentsView(
|
||||
pagingScrollController:
|
||||
_pagingScrollController,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
if (hasMultiUserSupport && false)
|
||||
CustomScrollView(
|
||||
controller: _pagingScrollController,
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView
|
||||
.sliverOverlapAbsorberHandleFor(
|
||||
context),
|
||||
),
|
||||
DocumentPermissionsWidget(
|
||||
document: state.document,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -29,11 +29,11 @@ class DocumentOverviewWidget extends StatelessWidget {
|
||||
final availableLabels = context.watch<LabelRepository>().state;
|
||||
return SliverList.list(
|
||||
children: [
|
||||
if (document.title != null)
|
||||
if (document.title.isNotEmpty)
|
||||
DetailsItem(
|
||||
label: S.of(context)!.title,
|
||||
content: HighlightedText(
|
||||
text: document.title!,
|
||||
text: document.title,
|
||||
highlights: queryString?.split(" ") ?? [],
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
@@ -9,7 +10,6 @@ import 'package:go_router/go_router.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/notifier/document_changed_notifier.dart';
|
||||
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart';
|
||||
import 'package:paperless_mobile/core/widgets/dialog_utils/pop_with_unsaved_changes.dart';
|
||||
import 'package:paperless_mobile/core/workarounds/colored_chip.dart';
|
||||
@@ -21,6 +21,7 @@ import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||
import 'package:paperless_mobile/routes/typed/branches/labels_route.dart';
|
||||
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
|
||||
import 'package:paperless_mobile/theme.dart';
|
||||
|
||||
class DocumentEditPage extends StatefulWidget {
|
||||
const DocumentEditPage({
|
||||
@@ -42,12 +43,6 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
|
||||
final _formKey = GlobalKey<FormBuilderState>();
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant DocumentEditPage oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
print("WIDGET CONFIGURATION CHANGED?!?!?");
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final currentUser = context.watch<LocalUserAccount>().paperlessUser;
|
||||
@@ -404,12 +399,6 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
Widget _buildTitleFormField(String? initialTitle) {
|
||||
return FormBuilderTextField(
|
||||
name: fkTitle,
|
||||
validator: (value) {
|
||||
if (value?.trim().isEmpty ?? true) {
|
||||
return S.of(context)!.thisFieldIsRequired;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
label: Text(S.of(context)!.title),
|
||||
),
|
||||
@@ -478,55 +467,3 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
).padded();
|
||||
}
|
||||
}
|
||||
|
||||
// class SampleWidget extends StatefulWidget {
|
||||
// const SampleWidget({super.key});
|
||||
|
||||
// @override
|
||||
// State<SampleWidget> createState() => _SampleWidgetState();
|
||||
// }
|
||||
|
||||
// class _SampleWidgetState extends State<SampleWidget> {
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return BlocBuilder<OptionsBloc, OptionsState>(
|
||||
// builder: (context, state) {
|
||||
// return OptionsFormField(
|
||||
// options: state.options,
|
||||
// onAddOption: (option) {
|
||||
// // This will call the repository and will cause a new state containing the new option to be emitted.
|
||||
// context.read<OptionsBloc>().addOption(option);
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
// class OptionsFormField extends StatefulWidget {
|
||||
// final List<Option> options;
|
||||
// final void Function(Option option) onAddOption;
|
||||
|
||||
// const OptionsFormField({
|
||||
// super.key,
|
||||
// required this.options,
|
||||
// required this.onAddOption,
|
||||
// });
|
||||
|
||||
// @override
|
||||
// State<OptionsFormField> createState() => _OptionsFormFieldState();
|
||||
// }
|
||||
|
||||
// class _OptionsFormFieldState extends State<OptionsFormField> {
|
||||
// final TextEditingController _controller;
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return TextFormField(
|
||||
// onTap: () async {
|
||||
// // User creates new option...
|
||||
// final Option option = await showOptionCreationForm();
|
||||
// widget.onAddOption(option);
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -20,7 +20,7 @@ class DeleteDocumentConfirmationDialog extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
document.title ?? document.originalFileName ?? '-',
|
||||
document.title.isEmpty ? '(no title)' : document.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
|
||||
@@ -111,7 +111,7 @@ class DocumentDetailedItem extends DocumentItem {
|
||||
],
|
||||
).paddedLTRB(8, 8, 8, 4),
|
||||
Text(
|
||||
document.title ?? '-',
|
||||
document.title.isEmpty ? '-' : 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.isEmpty ? '-' : document.title,
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
|
||||
@@ -58,7 +58,7 @@ class DocumentListItem extends DocumentItem {
|
||||
],
|
||||
),
|
||||
Text(
|
||||
document.title ?? '-',
|
||||
document.title.isEmpty ? '-' : document.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/routes/typed/branches/saved_views_route.dart';
|
||||
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
|
||||
|
||||
class SavedViewChip extends StatefulWidget {
|
||||
final SavedView view;
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.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/saved_views_route.dart';
|
||||
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
|
||||
|
||||
class SavedViewsWidget extends StatefulWidget {
|
||||
final void Function(SavedView view) onViewSelected;
|
||||
@@ -184,7 +185,8 @@ class _SavedViewsWidgetState extends State<SavedViewsWidget>
|
||||
child: ConnectivityAwareActionWrapper(
|
||||
child: TextButton.icon(
|
||||
onPressed: () {
|
||||
CreateSavedViewRoute(widget.filter).push(context);
|
||||
CreateSavedViewRoute($extra: widget.filter)
|
||||
.push(context);
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
label: Text(S.of(context)!.newView),
|
||||
|
||||
@@ -44,7 +44,7 @@ class BulkDeleteConfirmationDialog extends StatelessWidget {
|
||||
return ListTile(
|
||||
dense: true,
|
||||
title: Text(
|
||||
doc.title ?? doc.originalFileName ?? '-',
|
||||
doc.title.isEmpty ? '-' : doc.title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
|
||||
@@ -351,7 +351,7 @@ class _InboxItemState extends State<InboxItem> {
|
||||
|
||||
Text _buildTitle() {
|
||||
return Text(
|
||||
widget.document.title ?? '-',
|
||||
widget.document.title.isEmpty ? '-' : widget.document.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
|
||||
@@ -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/branches/saved_views_route.dart';
|
||||
import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
|
||||
|
||||
class LandingPage extends StatefulWidget {
|
||||
@@ -87,7 +88,11 @@ class _LandingPageState extends State<LandingPage> {
|
||||
Text(S.of(context)!.noSavedViewOnHomepageHint)
|
||||
.padded(),
|
||||
TextButton.icon(
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
const CreateSavedViewRoute(
|
||||
showOnDashboard: true,
|
||||
).push(context);
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
label: Text(S.of(context)!.newView),
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/hint_card.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
|
||||
@@ -12,9 +13,13 @@ const _fkShowInSidebar = 'show_in_sidebar';
|
||||
|
||||
class AddSavedViewPage extends StatefulWidget {
|
||||
final DocumentFilter? initialFilter;
|
||||
final bool? showOnDashboard;
|
||||
final bool? showInSidebar;
|
||||
const AddSavedViewPage({
|
||||
super.key,
|
||||
this.initialFilter,
|
||||
this.showOnDashboard,
|
||||
this.showInSidebar,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -59,7 +64,7 @@ class _AddSavedViewPageState extends State<AddSavedViewPage> {
|
||||
),
|
||||
FormBuilderField<bool>(
|
||||
name: _fkShowOnDashboard,
|
||||
initialValue: false,
|
||||
initialValue: widget.showOnDashboard ?? false,
|
||||
builder: (field) {
|
||||
return CheckboxListTile(
|
||||
value: field.value,
|
||||
@@ -70,7 +75,7 @@ class _AddSavedViewPageState extends State<AddSavedViewPage> {
|
||||
),
|
||||
FormBuilderField<bool>(
|
||||
name: _fkShowInSidebar,
|
||||
initialValue: false,
|
||||
initialValue: widget.showInSidebar ?? false,
|
||||
builder: (field) {
|
||||
return CheckboxListTile(
|
||||
value: field.value,
|
||||
@@ -82,6 +87,14 @@ class _AddSavedViewPageState extends State<AddSavedViewPage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
//TODO: INTL or remove
|
||||
HintCard(
|
||||
hintText:
|
||||
"Saved views can currently only be edited by changing the "
|
||||
"document filter. Therefore, go to the documents page, select "
|
||||
"this view, add the filters you want this view to have and "
|
||||
"save it by pressing 'Save changes' in the view menu.",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -316,7 +316,7 @@ class _GoRouterShellState extends State<GoRouterShell> {
|
||||
$loginRoute,
|
||||
$loggingOutRoute,
|
||||
$addAccountRoute,
|
||||
$providerShellRoute,
|
||||
$authenticatedRoute,
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -3,34 +3,28 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/view/edit_saved_view_page.dart';
|
||||
import 'package:paperless_mobile/routes/routes.dart';
|
||||
|
||||
part 'saved_views_route.g.dart';
|
||||
|
||||
@TypedGoRoute<SavedViewsRoute>(
|
||||
path: "/saved-views",
|
||||
routes: [
|
||||
TypedGoRoute<CreateSavedViewRoute>(
|
||||
path: "create",
|
||||
name: R.createSavedView,
|
||||
),
|
||||
TypedGoRoute<EditSavedViewRoute>(
|
||||
path: "edit",
|
||||
name: R.editSavedView,
|
||||
),
|
||||
],
|
||||
)
|
||||
class SavedViewsRoute extends GoRouteData {
|
||||
const SavedViewsRoute();
|
||||
}
|
||||
|
||||
class CreateSavedViewRoute extends GoRouteData {
|
||||
final DocumentFilter? $extra;
|
||||
const CreateSavedViewRoute(this.$extra);
|
||||
final bool? showOnDashboard;
|
||||
final bool? showInSidebar;
|
||||
const CreateSavedViewRoute({
|
||||
this.$extra = const DocumentFilter(),
|
||||
this.showOnDashboard,
|
||||
this.showInSidebar,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, GoRouterState state) {
|
||||
return AddSavedViewPage(initialFilter: $extra);
|
||||
return AddSavedViewPage(
|
||||
initialFilter: $extra,
|
||||
showInSidebar: showInSidebar,
|
||||
showOnDashboard: showOnDashboard,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ 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/scaffold_shell_route.dart';
|
||||
@@ -29,7 +30,7 @@ import 'package:provider/provider.dart';
|
||||
|
||||
part 'authenticated_route.g.dart';
|
||||
|
||||
@TypedShellRoute<ProviderShellRoute>(
|
||||
@TypedShellRoute<AuthenticatedRoute>(
|
||||
routes: [
|
||||
TypedGoRoute<SettingsRoute>(
|
||||
path: "/settings",
|
||||
@@ -39,6 +40,19 @@ part 'authenticated_route.g.dart';
|
||||
path: "/upload-queue",
|
||||
name: R.uploadQueue,
|
||||
),
|
||||
TypedGoRoute<SavedViewsRoute>(
|
||||
path: "/saved-views",
|
||||
routes: [
|
||||
TypedGoRoute<CreateSavedViewRoute>(
|
||||
path: "create",
|
||||
name: R.createSavedView,
|
||||
),
|
||||
TypedGoRoute<EditSavedViewRoute>(
|
||||
path: "edit",
|
||||
name: R.editSavedView,
|
||||
),
|
||||
],
|
||||
),
|
||||
TypedStatefulShellRoute<ScaffoldShellRoute>(
|
||||
branches: [
|
||||
TypedStatefulShellBranch<LandingBranch>(
|
||||
@@ -122,10 +136,10 @@ part 'authenticated_route.g.dart';
|
||||
),
|
||||
],
|
||||
)
|
||||
class ProviderShellRoute extends ShellRouteData {
|
||||
class AuthenticatedRoute extends ShellRouteData {
|
||||
static final GlobalKey<NavigatorState> $navigatorKey = outerShellNavigatorKey;
|
||||
|
||||
const ProviderShellRoute();
|
||||
const AuthenticatedRoute();
|
||||
|
||||
@override
|
||||
Widget builder(
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user