mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-09 16:07:57 -06:00
Further migrations to route based navigation, improved saved view logic
This commit is contained in:
@@ -56,21 +56,6 @@ Future<void> pushSavedViewDetailsRoute(
|
||||
);
|
||||
}
|
||||
|
||||
Future<SavedView?> pushAddSavedViewRoute(BuildContext context,
|
||||
{required DocumentFilter filter}) {
|
||||
return Navigator.of(context).push<SavedView?>(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => AddSavedViewPage(
|
||||
currentFilter: filter,
|
||||
correspondents: context.read<LabelRepository>().state.correspondents,
|
||||
documentTypes: context.read<LabelRepository>().state.documentTypes,
|
||||
storagePaths: context.read<LabelRepository>().state.storagePaths,
|
||||
tags: context.read<LabelRepository>().state.tags,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> pushBulkEditCorrespondentRoute(
|
||||
BuildContext context, {
|
||||
required List<DocumentModel> selection,
|
||||
|
||||
@@ -35,6 +35,18 @@ class SavedViewRepository
|
||||
return created;
|
||||
}
|
||||
|
||||
Future<SavedView> update(SavedView object) async {
|
||||
await _initialized.future;
|
||||
final updated = await _api.update(object);
|
||||
final updatedState = {...state.savedViews}..update(
|
||||
updated.id!,
|
||||
(_) => updated,
|
||||
ifAbsent: () => updated,
|
||||
);
|
||||
emit(SavedViewRepositoryState.loaded(savedViews: updatedState));
|
||||
return updated;
|
||||
}
|
||||
|
||||
Future<int> delete(SavedView view) async {
|
||||
await _initialized.future;
|
||||
await _api.delete(view);
|
||||
|
||||
@@ -54,7 +54,7 @@ String translateError(BuildContext context, ErrorCode code) {
|
||||
ErrorCode.suggestionsQueryError => S.of(context)!.couldNotLoadSuggestions,
|
||||
ErrorCode.acknowledgeTasksError => S.of(context)!.couldNotAcknowledgeTasks,
|
||||
ErrorCode.correspondentDeleteFailed =>
|
||||
"Could not delete correspondent, please try again.",
|
||||
"Could not delete correspondent, please try again.", //TODO: INTL
|
||||
ErrorCode.documentTypeDeleteFailed =>
|
||||
"Could not delete document type, please try again.",
|
||||
ErrorCode.tagDeleteFailed => "Could not delete tag, please try again.",
|
||||
@@ -73,5 +73,6 @@ String translateError(BuildContext context, ErrorCode code) {
|
||||
ErrorCode.uiSettingsLoadFailed => "Could not load UI settings",
|
||||
ErrorCode.loadTasksError => "Could not load tasks.",
|
||||
ErrorCode.userNotFound => "User could not be found.",
|
||||
ErrorCode.updateSavedViewError => "Could not update saved view.",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hive/hive.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/local_user_account.dart';
|
||||
import 'package:paperless_mobile/features/document_search/view/document_search_bar.dart';
|
||||
import 'package:paperless_mobile/features/home/view/model/api_version.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/manage_accounts_page.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/global_settings_builder.dart';
|
||||
import 'package:paperless_mobile/features/settings/view/widgets/user_avatar.dart';
|
||||
|
||||
@@ -5,15 +5,13 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
|
||||
import 'package:paperless_mobile/core/delegate/customizable_sliver_persistent_header_delegate.dart';
|
||||
import 'package:paperless_mobile/core/navigation/push_routes.dart';
|
||||
import 'package:paperless_mobile/core/widgets/material/colored_tab_bar.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
|
||||
import 'package:paperless_mobile/features/document_search/view/sliver_search_bar.dart';
|
||||
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/adaptive_documents_view.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/saved_views/saved_view_changed_dialog.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/saved_views/saved_views_widget.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/search/document_filter_panel.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/document_selection_sliver_app_bar.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/view_type_selection_widget.dart';
|
||||
@@ -24,6 +22,7 @@ import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
|
||||
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:sliver_tools/sliver_tools.dart';
|
||||
|
||||
class DocumentFilterIntent {
|
||||
final DocumentFilter? filter;
|
||||
@@ -46,12 +45,29 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
final SliverOverlapAbsorberHandle searchBarHandle =
|
||||
SliverOverlapAbsorberHandle();
|
||||
final SliverOverlapAbsorberHandle tabBarHandle =
|
||||
final SliverOverlapAbsorberHandle savedViewsHandle =
|
||||
SliverOverlapAbsorberHandle();
|
||||
late final TabController _tabController;
|
||||
|
||||
int _currentTab = 0;
|
||||
|
||||
bool get hasSelectedViewChanged {
|
||||
final cubit = context.watch<DocumentsCubit>();
|
||||
final savedViewCubit = context.watch<SavedViewCubit>();
|
||||
final activeView = savedViewCubit.state.maybeMap(
|
||||
loaded: (state) {
|
||||
if (cubit.state.filter.selectedView != null) {
|
||||
return state.savedViews[cubit.state.filter.selectedView!];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
orElse: () => null,
|
||||
);
|
||||
final viewHasChanged = activeView != null &&
|
||||
activeView.toDocumentFilter() != cubit.state.filter;
|
||||
return viewHasChanged;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -139,7 +155,7 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
onPressed: () {
|
||||
context.read<DocumentsCubit>().updateFilter();
|
||||
_onResetFilter();
|
||||
},
|
||||
child: Icon(
|
||||
Icons.refresh,
|
||||
@@ -195,107 +211,148 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
}
|
||||
return true;
|
||||
},
|
||||
child: NestedScrollView(
|
||||
floatHeaderSlivers: true,
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||
SliverOverlapAbsorber(
|
||||
handle: searchBarHandle,
|
||||
sliver: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
builder: (context, state) {
|
||||
if (state.selection.isEmpty) {
|
||||
return SliverSearchBar(
|
||||
floating: true,
|
||||
titleText: S.of(context)!.documents,
|
||||
);
|
||||
} else {
|
||||
return DocumentSelectionSliverAppBar(
|
||||
state: state,
|
||||
);
|
||||
child: Stack(
|
||||
children: [
|
||||
NestedScrollView(
|
||||
floatHeaderSlivers: true,
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||
SliverOverlapAbsorber(
|
||||
handle: searchBarHandle,
|
||||
sliver: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
builder: (context, state) {
|
||||
if (state.selection.isEmpty) {
|
||||
return SliverSearchBar(
|
||||
floating: true,
|
||||
titleText: S.of(context)!.documents,
|
||||
);
|
||||
} else {
|
||||
return DocumentSelectionSliverAppBar(
|
||||
state: state,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
SliverOverlapAbsorber(
|
||||
handle: savedViewsHandle,
|
||||
sliver: SliverPinnedHeader(
|
||||
child: _buildViewActions(),
|
||||
),
|
||||
),
|
||||
// SliverOverlapAbsorber(
|
||||
// handle: tabBarHandle,
|
||||
// sliver: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
// builder: (context, state) {
|
||||
// if (state.selection.isNotEmpty) {
|
||||
// return const SliverToBoxAdapter(
|
||||
// child: SizedBox.shrink(),
|
||||
// );
|
||||
// }
|
||||
// return SliverPersistentHeader(
|
||||
// pinned: true,
|
||||
// delegate:
|
||||
// CustomizableSliverPersistentHeaderDelegate(
|
||||
// minExtent: kTextTabBarHeight,
|
||||
// maxExtent: kTextTabBarHeight,
|
||||
// child: ColoredTabBar(
|
||||
// tabBar: TabBar(
|
||||
// controller: _tabController,
|
||||
// tabs: [
|
||||
// Tab(text: S.of(context)!.documents),
|
||||
// if (context
|
||||
// .watch<LocalUserAccount>()
|
||||
// .paperlessUser
|
||||
// .canViewSavedViews)
|
||||
// Tab(text: S.of(context)!.views),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
body: NotificationListener<ScrollNotification>(
|
||||
onNotification: (notification) {
|
||||
final metrics = notification.metrics;
|
||||
if (metrics.maxScrollExtent == 0) {
|
||||
return true;
|
||||
}
|
||||
final desiredTab =
|
||||
(metrics.pixels / metrics.maxScrollExtent)
|
||||
.round();
|
||||
if (metrics.axis == Axis.horizontal &&
|
||||
_currentTab != desiredTab) {
|
||||
setState(() => _currentTab = desiredTab);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
physics: context
|
||||
.watch<DocumentsCubit>()
|
||||
.state
|
||||
.selection
|
||||
.isNotEmpty
|
||||
? const NeverScrollableScrollPhysics()
|
||||
: null,
|
||||
children: [
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return _buildDocumentsTab(
|
||||
connectivityState,
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
if (context
|
||||
.watch<LocalUserAccount>()
|
||||
.paperlessUser
|
||||
.canViewSavedViews)
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return _buildSavedViewsTab(
|
||||
connectivityState,
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverOverlapAbsorber(
|
||||
handle: tabBarHandle,
|
||||
sliver: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
builder: (context, state) {
|
||||
if (state.selection.isNotEmpty) {
|
||||
return const SliverToBoxAdapter(
|
||||
child: SizedBox.shrink(),
|
||||
);
|
||||
}
|
||||
return SliverPersistentHeader(
|
||||
pinned: true,
|
||||
delegate:
|
||||
CustomizableSliverPersistentHeaderDelegate(
|
||||
minExtent: kTextTabBarHeight,
|
||||
maxExtent: kTextTabBarHeight,
|
||||
child: ColoredTabBar(
|
||||
tabBar: TabBar(
|
||||
controller: _tabController,
|
||||
tabs: [
|
||||
Tab(text: S.of(context)!.documents),
|
||||
if (context
|
||||
.watch<LocalUserAccount>()
|
||||
.paperlessUser
|
||||
.canViewSavedViews)
|
||||
Tab(text: S.of(context)!.views),
|
||||
],
|
||||
AnimatedOpacity(
|
||||
opacity: hasSelectedViewChanged ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(bottom: 24),
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceVariant
|
||||
.withOpacity(0.9),
|
||||
child: InkWell(
|
||||
customBorder: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
onTap: () {},
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(16, 8, 16, 8),
|
||||
child: Text(
|
||||
"Update selected view",
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
body: NotificationListener<ScrollNotification>(
|
||||
onNotification: (notification) {
|
||||
final metrics = notification.metrics;
|
||||
if (metrics.maxScrollExtent == 0) {
|
||||
return true;
|
||||
}
|
||||
final desiredTab =
|
||||
(metrics.pixels / metrics.maxScrollExtent).round();
|
||||
if (metrics.axis == Axis.horizontal &&
|
||||
_currentTab != desiredTab) {
|
||||
setState(() => _currentTab = desiredTab);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
physics: context
|
||||
.watch<DocumentsCubit>()
|
||||
.state
|
||||
.selection
|
||||
.isNotEmpty
|
||||
? const NeverScrollableScrollPhysics()
|
||||
: null,
|
||||
children: [
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return _buildDocumentsTab(
|
||||
connectivityState,
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
if (context
|
||||
.watch<LocalUserAccount>()
|
||||
.paperlessUser
|
||||
.canViewSavedViews)
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return _buildSavedViewsTab(
|
||||
connectivityState,
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -315,12 +372,12 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
notificationPredicate: (_) => connectivityState.isConnected,
|
||||
child: CustomScrollView(
|
||||
key: const PageStorageKey<String>("savedViews"),
|
||||
slivers: <Widget>[
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: searchBarHandle,
|
||||
),
|
||||
SliverOverlapInjector(
|
||||
handle: tabBarHandle,
|
||||
handle: savedViewsHandle,
|
||||
),
|
||||
const SavedViewList(),
|
||||
],
|
||||
@@ -370,15 +427,40 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
key: const PageStorageKey<String>("documents"),
|
||||
slivers: <Widget>[
|
||||
SliverOverlapInjector(handle: searchBarHandle),
|
||||
SliverOverlapInjector(handle: tabBarHandle),
|
||||
_buildViewActions(),
|
||||
SliverOverlapInjector(handle: savedViewsHandle),
|
||||
BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.filter != current.filter,
|
||||
builder: (context, state) {
|
||||
return SliverToBoxAdapter(
|
||||
child: SavedViewsWidget(
|
||||
onViewSelected: (view) {
|
||||
final cubit = context.read<DocumentsCubit>();
|
||||
if (state.filter.selectedView == view.id) {
|
||||
_onResetFilter();
|
||||
} else {
|
||||
cubit.updateFilter(
|
||||
filter: view.toDocumentFilter(),
|
||||
);
|
||||
}
|
||||
},
|
||||
onUpdateView: (view) async {
|
||||
await context.read<SavedViewCubit>().update(view);
|
||||
showSnackBar(context,
|
||||
"Saved view successfully updated."); //TODO: INTL
|
||||
},
|
||||
filter: state.filter,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
builder: (context, state) {
|
||||
if (state.hasLoaded && state.documents.isEmpty) {
|
||||
return SliverToBoxAdapter(
|
||||
child: DocumentsEmptyState(
|
||||
state: state,
|
||||
onReset: context.read<DocumentsCubit>().resetFilter,
|
||||
onReset: _onResetFilter,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -413,10 +495,12 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
}
|
||||
|
||||
Widget _buildViewActions() {
|
||||
return SliverToBoxAdapter(
|
||||
child: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
builder: (context, state) {
|
||||
return Row(
|
||||
return BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
builder: (context, state) {
|
||||
return Container(
|
||||
padding: EdgeInsets.all(4),
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SortDocumentsButton(
|
||||
@@ -427,21 +511,22 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
onChanged: context.read<DocumentsCubit>().setViewType,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
).paddedSymmetrically(horizontal: 8, vertical: 4),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _onCreateSavedView(DocumentFilter filter) async {
|
||||
final newView = await pushAddSavedViewRoute(context, filter: filter);
|
||||
if (newView != null) {
|
||||
try {
|
||||
await context.read<SavedViewCubit>().add(newView);
|
||||
} on PaperlessApiException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
//TODO: Implement
|
||||
// final newView = await pushAddSavedViewRoute(context, filter: filter);
|
||||
// if (newView != null) {
|
||||
// try {
|
||||
// await context.read<SavedViewCubit>().add(newView);
|
||||
// } on PaperlessApiException catch (error, stackTrace) {
|
||||
// showErrorMessage(context, error, stackTrace);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void _openDocumentFilter() async {
|
||||
@@ -485,7 +570,7 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
if (filterIntent != null) {
|
||||
try {
|
||||
if (filterIntent.shouldReset) {
|
||||
await context.read<DocumentsCubit>().resetFilter();
|
||||
await _onResetFilter();
|
||||
} else {
|
||||
await context
|
||||
.read<DocumentsCubit>()
|
||||
@@ -651,4 +736,40 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onResetFilter() async {
|
||||
final cubit = context.read<DocumentsCubit>();
|
||||
final savedViewCubit = context.read<SavedViewCubit>();
|
||||
final activeView = savedViewCubit.state.maybeMap(
|
||||
loaded: (state) {
|
||||
if (cubit.state.filter.selectedView != null) {
|
||||
return state.savedViews[cubit.state.filter.selectedView!];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
orElse: () => null,
|
||||
);
|
||||
final viewHasChanged = activeView != null &&
|
||||
activeView.toDocumentFilter() != cubit.state.filter;
|
||||
if (viewHasChanged) {
|
||||
final discardChanges = await showDialog(
|
||||
context: context,
|
||||
builder: (context) => SavedViewChangedDialog(),
|
||||
);
|
||||
if (discardChanges == true) {
|
||||
cubit.resetFilter();
|
||||
// Reset
|
||||
} else if (discardChanges == false) {
|
||||
final newView = activeView.copyWith(
|
||||
filterRules: FilterRule.fromFilter(cubit.state.filter),
|
||||
);
|
||||
final savedViewCubit2 = context.read<SavedViewCubit>();
|
||||
|
||||
await savedViewCubit2.update(newView);
|
||||
showSnackBar(context, "Saved view successfully updated.");
|
||||
}
|
||||
} else {
|
||||
cubit.resetFilter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_cancel_button.dart';
|
||||
import 'package:paperless_mobile/core/widgets/dialog_utils/dialog_confirm_button.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
|
||||
class SavedViewChangedDialog extends StatelessWidget {
|
||||
const SavedViewChangedDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text("Discard changes?"), //TODO: INTL
|
||||
content: Text(
|
||||
"Some filters of the currently active view have changed. By resetting the filter, these changes will be lost. Do you still wish to continue?", //TODO: INTL
|
||||
),
|
||||
actionsOverflowButtonSpacing: 8,
|
||||
actions: [
|
||||
const DialogCancelButton(),
|
||||
TextButton(
|
||||
child: Text(S.of(context)!.saveChanges),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, false);
|
||||
},
|
||||
),
|
||||
DialogConfirmButton(
|
||||
label: S.of(context)!.resetFilter,
|
||||
style: DialogConfirmButtonStyle.danger,
|
||||
returnValue: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
|
||||
class SavedViewChip extends StatelessWidget {
|
||||
final SavedView view;
|
||||
final void Function(SavedView view) onViewSelected;
|
||||
final void Function(SavedView vie) onUpdateView;
|
||||
final bool selected;
|
||||
final bool hasChanged;
|
||||
|
||||
const SavedViewChip({
|
||||
super.key,
|
||||
required this.view,
|
||||
required this.onViewSelected,
|
||||
required this.selected,
|
||||
required this.hasChanged,
|
||||
required this.onUpdateView,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Badge(
|
||||
smallSize: 12,
|
||||
alignment: const AlignmentDirectional(1.1, -1.2),
|
||||
backgroundColor: Colors.red,
|
||||
isLabelVisible: hasChanged,
|
||||
child: FilterChip(
|
||||
avatar: Icon(
|
||||
Icons.saved_search,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
showCheckmark: false,
|
||||
selectedColor: Theme.of(context).colorScheme.primaryContainer,
|
||||
selected: selected,
|
||||
label: Text(view.name),
|
||||
onSelected: (_) {
|
||||
onViewSelected(view);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/saved_views/saved_view_chip.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||
|
||||
class SavedViewsWidget extends StatelessWidget {
|
||||
final void Function(SavedView view) onViewSelected;
|
||||
final void Function(SavedView view) onUpdateView;
|
||||
final DocumentFilter filter;
|
||||
const SavedViewsWidget({
|
||||
super.key,
|
||||
required this.onViewSelected,
|
||||
required this.filter,
|
||||
required this.onUpdateView,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12,
|
||||
left: 16,
|
||||
right: 16,
|
||||
),
|
||||
height: 50,
|
||||
child: BlocBuilder<SavedViewCubit, SavedViewState>(
|
||||
builder: (context, state) {
|
||||
return state.maybeWhen(
|
||||
loaded: (savedViews) {
|
||||
if (savedViews.isEmpty) {
|
||||
return Text("No saved views");
|
||||
}
|
||||
return ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) {
|
||||
final view = savedViews.values.elementAt(index);
|
||||
return SavedViewChip(
|
||||
view: view,
|
||||
onUpdateView: onUpdateView,
|
||||
onViewSelected: onViewSelected,
|
||||
selected: filter.selectedView != null &&
|
||||
view.id == filter.selectedView,
|
||||
hasChanged: filter.selectedView == view.id &&
|
||||
filter != view.toDocumentFilter(),
|
||||
);
|
||||
},
|
||||
itemCount: savedViews.length,
|
||||
);
|
||||
},
|
||||
error: () => Text("Error loading saved views"),
|
||||
orElse: () => Placeholder(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,12 @@ class DocumentFilterForm extends StatefulWidget {
|
||||
static const fkAddedAt = DocumentModel.addedKey;
|
||||
|
||||
static DocumentFilter assembleFilter(
|
||||
GlobalKey<FormBuilderState> formKey, DocumentFilter initialFilter) {
|
||||
GlobalKey<FormBuilderState> formKey,
|
||||
DocumentFilter initialFilter,
|
||||
) {
|
||||
formKey.currentState?.save();
|
||||
final v = formKey.currentState!.value;
|
||||
return DocumentFilter(
|
||||
return initialFilter.copyWith(
|
||||
correspondent:
|
||||
v[DocumentFilterForm.fkCorrespondent] as IdQueryParameter? ??
|
||||
DocumentFilter.initial.correspondent,
|
||||
@@ -36,11 +38,7 @@ class DocumentFilterForm extends StatefulWidget {
|
||||
DocumentFilter.initial.query,
|
||||
created: (v[DocumentFilterForm.fkCreatedAt] as DateRangeQuery),
|
||||
added: (v[DocumentFilterForm.fkAddedAt] as DateRangeQuery),
|
||||
asnQuery: initialFilter.asnQuery,
|
||||
page: 1,
|
||||
pageSize: initialFilter.pageSize,
|
||||
sortField: initialFilter.sortField,
|
||||
sortOrder: initialFilter.sortOrder,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/app_drawer/view/app_drawer.dart';
|
||||
import 'package:paperless_mobile/features/inbox/cubit/inbox_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
import 'package:paperless_mobile/helpers/message_helpers.dart';
|
||||
|
||||
const _landingPage = 0;
|
||||
const _documentsIndex = 1;
|
||||
@@ -30,134 +29,123 @@ class ScaffoldWithNavigationBar extends StatefulWidget {
|
||||
class ScaffoldWithNavigationBarState extends State<ScaffoldWithNavigationBar> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final disabledColor = Theme.of(context).disabledColor;
|
||||
final primaryColor = Theme.of(context).colorScheme.primary;
|
||||
|
||||
return Scaffold(
|
||||
drawer: const AppDrawer(),
|
||||
bottomNavigationBar: NavigationBar(
|
||||
selectedIndex: widget.navigationShell.currentIndex,
|
||||
onDestinationSelected: (index) {
|
||||
switch (index) {
|
||||
case _landingPage:
|
||||
widget.navigationShell.goBranch(index);
|
||||
break;
|
||||
case _documentsIndex:
|
||||
if (widget.authenticatedUser.canViewDocuments) {
|
||||
widget.navigationShell.goBranch(index);
|
||||
} else {
|
||||
showSnackBar(context, S.of(context)!.missingPermissions);
|
||||
}
|
||||
break;
|
||||
case _scannerIndex:
|
||||
if (widget.authenticatedUser.canCreateDocuments) {
|
||||
widget.navigationShell.goBranch(index);
|
||||
} else {
|
||||
showSnackBar(context, S.of(context)!.missingPermissions);
|
||||
}
|
||||
break;
|
||||
case _labelsIndex:
|
||||
if (widget.authenticatedUser.canViewAnyLabel) {
|
||||
widget.navigationShell.goBranch(index);
|
||||
} else {
|
||||
showSnackBar(context, S.of(context)!.missingPermissions);
|
||||
}
|
||||
break;
|
||||
case _inboxIndex:
|
||||
if (widget.authenticatedUser.canViewDocuments &&
|
||||
widget.authenticatedUser.canViewTags) {
|
||||
widget.navigationShell.goBranch(index);
|
||||
} else {
|
||||
showSnackBar(context, S.of(context)!.missingPermissions);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
widget.navigationShell.goBranch(
|
||||
index,
|
||||
initialLocation: index == widget.navigationShell.currentIndex,
|
||||
);
|
||||
},
|
||||
destinations: [
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.home_outlined),
|
||||
icon: const Icon(Icons.home_outlined),
|
||||
selectedIcon: Icon(
|
||||
Icons.home,
|
||||
color: primaryColor,
|
||||
),
|
||||
label: "Home", //TODO: INTL
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(
|
||||
Icons.description_outlined,
|
||||
color: !widget.authenticatedUser.canViewDocuments
|
||||
? disabledColor
|
||||
: null,
|
||||
_toggleDestination(
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.description_outlined),
|
||||
selectedIcon: Icon(
|
||||
Icons.description,
|
||||
color: primaryColor,
|
||||
),
|
||||
label: S.of(context)!.documents,
|
||||
),
|
||||
selectedIcon: Icon(
|
||||
Icons.description,
|
||||
color: primaryColor,
|
||||
),
|
||||
label: S.of(context)!.documents,
|
||||
disableWhen: !widget.authenticatedUser.canViewDocuments,
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(
|
||||
Icons.document_scanner_outlined,
|
||||
color: !widget.authenticatedUser.canCreateDocuments
|
||||
? disabledColor
|
||||
: null,
|
||||
_toggleDestination(
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.document_scanner_outlined),
|
||||
selectedIcon: Icon(
|
||||
Icons.document_scanner,
|
||||
color: primaryColor,
|
||||
),
|
||||
label: S.of(context)!.scanner,
|
||||
),
|
||||
selectedIcon: Icon(
|
||||
Icons.document_scanner,
|
||||
color: primaryColor,
|
||||
),
|
||||
label: S.of(context)!.scanner,
|
||||
disableWhen: !widget.authenticatedUser.canCreateDocuments,
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(
|
||||
Icons.sell_outlined,
|
||||
color: !widget.authenticatedUser.canViewAnyLabel
|
||||
? disabledColor
|
||||
: null,
|
||||
_toggleDestination(
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.sell_outlined),
|
||||
selectedIcon: Icon(
|
||||
Icons.sell,
|
||||
color: primaryColor,
|
||||
),
|
||||
label: S.of(context)!.labels,
|
||||
),
|
||||
selectedIcon: Icon(
|
||||
Icons.sell,
|
||||
color: primaryColor,
|
||||
),
|
||||
label: S.of(context)!.labels,
|
||||
disableWhen: !widget.authenticatedUser.canViewAnyLabel,
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Builder(builder: (context) {
|
||||
if (!(widget.authenticatedUser.canViewDocuments &&
|
||||
widget.authenticatedUser.canViewTags)) {
|
||||
return Icon(
|
||||
Icons.inbox_outlined,
|
||||
color: disabledColor,
|
||||
);
|
||||
}
|
||||
return BlocBuilder<InboxCubit, InboxState>(
|
||||
builder: (context, state) {
|
||||
return Badge.count(
|
||||
isLabelVisible: state.itemsInInboxCount > 0,
|
||||
count: state.itemsInInboxCount,
|
||||
child: const Icon(Icons.inbox_outlined),
|
||||
_toggleDestination(
|
||||
NavigationDestination(
|
||||
icon: Builder(
|
||||
builder: (context) {
|
||||
return BlocBuilder<InboxCubit, InboxState>(
|
||||
builder: (context, state) {
|
||||
return Badge.count(
|
||||
isLabelVisible: state.itemsInInboxCount > 0,
|
||||
count: state.itemsInInboxCount,
|
||||
child: const Icon(Icons.inbox_outlined),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
selectedIcon: BlocBuilder<InboxCubit, InboxState>(
|
||||
builder: (context, state) {
|
||||
return Badge.count(
|
||||
isLabelVisible: state.itemsInInboxCount > 0,
|
||||
count: state.itemsInInboxCount,
|
||||
child: Icon(
|
||||
Icons.inbox,
|
||||
color: primaryColor,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
selectedIcon: BlocBuilder<InboxCubit, InboxState>(
|
||||
builder: (context, state) {
|
||||
return Badge.count(
|
||||
isLabelVisible: state.itemsInInboxCount > 0 &&
|
||||
widget.authenticatedUser.canViewInbox,
|
||||
count: state.itemsInInboxCount,
|
||||
child: Icon(
|
||||
Icons.inbox,
|
||||
color: primaryColor,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
label: S.of(context)!.inbox,
|
||||
),
|
||||
label: S.of(context)!.inbox,
|
||||
disableWhen: !widget.authenticatedUser.canViewInbox,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: widget.navigationShell,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _toggleDestination(
|
||||
Widget destination, {
|
||||
required bool disableWhen,
|
||||
}) {
|
||||
final disabledColor = Theme.of(context).disabledColor;
|
||||
|
||||
final disabledTheme = Theme.of(context).navigationBarTheme.copyWith(
|
||||
labelTextStyle: MaterialStatePropertyAll(
|
||||
Theme.of(context)
|
||||
.textTheme
|
||||
.labelSmall
|
||||
?.copyWith(color: disabledColor),
|
||||
),
|
||||
iconTheme: MaterialStatePropertyAll(
|
||||
Theme.of(context).iconTheme.copyWith(color: disabledColor),
|
||||
),
|
||||
);
|
||||
if (disableWhen) {
|
||||
return AbsorbPointer(
|
||||
child: Theme(
|
||||
data: Theme.of(context).copyWith(navigationBarTheme: disabledTheme),
|
||||
child: destination,
|
||||
),
|
||||
);
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,44 +51,58 @@ class _LandingPageState extends State<LandingPage> {
|
||||
).padded(24),
|
||||
),
|
||||
SliverToBoxAdapter(child: _buildStatisticsCard(context)),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 0, 8),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: Text(
|
||||
"Saved Views",
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
if (currentUser.canViewSavedViews) ...[
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 0, 8),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: Text(
|
||||
"Saved Views",
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
BlocBuilder<SavedViewCubit, SavedViewState>(
|
||||
builder: (context, state) {
|
||||
return state.maybeWhen(
|
||||
loaded: (savedViews) {
|
||||
final dashboardViews = savedViews.values
|
||||
.where((element) => element.showOnDashboard)
|
||||
.toList();
|
||||
if (dashboardViews.isEmpty) {
|
||||
return const SliverToBoxAdapter(
|
||||
child: Text("No views"),
|
||||
);
|
||||
}
|
||||
return SliverList.builder(
|
||||
itemBuilder: (context, index) {
|
||||
return SavedViewPreview(
|
||||
savedView: dashboardViews.elementAt(index),
|
||||
BlocBuilder<SavedViewCubit, SavedViewState>(
|
||||
builder: (context, state) {
|
||||
return state.maybeWhen(
|
||||
loaded: (savedViews) {
|
||||
final dashboardViews = savedViews.values
|
||||
.where((element) => element.showOnDashboard)
|
||||
.toList();
|
||||
if (dashboardViews.isEmpty) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"There are no saved views to show on your dashboard.", //TODO: INTL
|
||||
),
|
||||
TextButton.icon(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add),
|
||||
label: Text("Add new view"),
|
||||
)
|
||||
],
|
||||
).paddedOnly(left: 16),
|
||||
);
|
||||
},
|
||||
itemCount: dashboardViews.length,
|
||||
);
|
||||
},
|
||||
orElse: () => const SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
}
|
||||
return SliverList.builder(
|
||||
itemBuilder: (context, index) {
|
||||
return SavedViewPreview(
|
||||
savedView: dashboardViews.elementAt(index),
|
||||
);
|
||||
},
|
||||
itemCount: dashboardViews.length,
|
||||
);
|
||||
},
|
||||
orElse: () => const SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -121,6 +135,7 @@ class _LandingPageState extends State<LandingPage> {
|
||||
}
|
||||
final stats = snapshot.data!;
|
||||
return ExpansionCard(
|
||||
initiallyExpanded: false,
|
||||
title: Text(
|
||||
"Statistics", //TODO: INTL
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
|
||||
@@ -68,7 +68,7 @@ mixin DocumentPagingBlocMixin<State extends DocumentPagingState>
|
||||
/// Convenience method which allows to directly use [DocumentFilter.copyWith] on the current filter.
|
||||
///
|
||||
Future<void> updateCurrentFilter(
|
||||
final DocumentFilter Function(DocumentFilter) transformFn,
|
||||
final DocumentFilter Function(DocumentFilter filter) transformFn,
|
||||
) async =>
|
||||
updateFilter(filter: transformFn(state.filter));
|
||||
|
||||
|
||||
@@ -35,6 +35,10 @@ class SavedViewCubit extends Cubit<SavedViewState> {
|
||||
return _savedViewRepository.delete(view);
|
||||
}
|
||||
|
||||
Future<SavedView> update(SavedView view) async {
|
||||
return await _savedViewRepository.update(view);
|
||||
}
|
||||
|
||||
Future<void> reload() async {
|
||||
final views = await _savedViewRepository.findAll();
|
||||
final values = {for (var element in views) element.id!: element};
|
||||
|
||||
@@ -1,25 +1,15 @@
|
||||
import 'package:flutter/material.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/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/search/document_filter_form.dart';
|
||||
import 'package:paperless_mobile/generated/l10n/app_localizations.dart';
|
||||
|
||||
class AddSavedViewPage extends StatefulWidget {
|
||||
final DocumentFilter currentFilter;
|
||||
final Map<int, Correspondent> correspondents;
|
||||
final Map<int, DocumentType> documentTypes;
|
||||
final Map<int, Tag> tags;
|
||||
final Map<int, StoragePath> storagePaths;
|
||||
final DocumentFilter? initialFilter;
|
||||
const AddSavedViewPage({
|
||||
super.key,
|
||||
required this.currentFilter,
|
||||
required this.correspondents,
|
||||
required this.documentTypes,
|
||||
required this.tags,
|
||||
required this.storagePaths,
|
||||
this.initialFilter,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -81,21 +71,6 @@ class _AddSavedViewPageState extends State<AddSavedViewPage> {
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
Text(
|
||||
"Review filter",
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
).padded(),
|
||||
Flexible(
|
||||
child: DocumentFilterForm(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
formKey: _filterFormKey,
|
||||
initialFilter: widget.currentFilter,
|
||||
correspondents: widget.correspondents,
|
||||
documentTypes: widget.documentTypes,
|
||||
storagePaths: widget.storagePaths,
|
||||
tags: widget.tags,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -104,19 +79,19 @@ class _AddSavedViewPageState extends State<AddSavedViewPage> {
|
||||
|
||||
void _onCreate(BuildContext context) {
|
||||
if (_savedViewFormKey.currentState?.saveAndValidate() ?? false) {
|
||||
context.pop(
|
||||
SavedView.fromDocumentFilter(
|
||||
DocumentFilterForm.assembleFilter(
|
||||
_filterFormKey,
|
||||
widget.currentFilter,
|
||||
),
|
||||
name: _savedViewFormKey.currentState?.value[fkName] as String,
|
||||
showOnDashboard:
|
||||
_savedViewFormKey.currentState?.value[fkShowOnDashboard] as bool,
|
||||
showInSidebar:
|
||||
_savedViewFormKey.currentState?.value[fkShowInSidebar] as bool,
|
||||
),
|
||||
);
|
||||
// context.pop(
|
||||
// SavedView.fromDocumentFilter(
|
||||
// DocumentFilterForm.assembleFilter(
|
||||
// _filterFormKey,
|
||||
// widget.currentFilter,
|
||||
// ),
|
||||
// name: _savedViewFormKey.currentState?.value[fkName] as String,
|
||||
// showOnDashboard:
|
||||
// _savedViewFormKey.currentState?.value[fkShowOnDashboard] as bool,
|
||||
// showInSidebar:
|
||||
// _savedViewFormKey.currentState?.value[fkShowInSidebar] as bool,
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/items/document_list_item.dart';
|
||||
import 'package:paperless_mobile/features/landing/view/widgets/expansion_card.dart';
|
||||
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:provider/provider.dart';
|
||||
|
||||
@@ -30,27 +32,26 @@ class SavedViewPreview extends StatelessWidget {
|
||||
loaded: (documents) {
|
||||
return Column(
|
||||
children: [
|
||||
for (final document in documents)
|
||||
DocumentListItem(
|
||||
document: document,
|
||||
isLabelClickable: false,
|
||||
isSelected: false,
|
||||
isSelectionActive: false,
|
||||
onTap: (document) {
|
||||
DocumentDetailsRoute($extra: document)
|
||||
.push(context);
|
||||
},
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
TextButton(
|
||||
child: Text("Show"),
|
||||
onPressed: documents.length >= 5 ? () {} : null,
|
||||
if (documents.isEmpty)
|
||||
Text("This view is empty.").padded()
|
||||
else
|
||||
for (final document in documents)
|
||||
DocumentListItem(
|
||||
document: document,
|
||||
isLabelClickable: false,
|
||||
isSelected: false,
|
||||
isSelectionActive: false,
|
||||
onTap: (document) {
|
||||
DocumentDetailsRoute($extra: document)
|
||||
.push(context);
|
||||
},
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
icon: Icon(Icons.open_in_new),
|
||||
label: Text("Show in documents"),
|
||||
icon: const Icon(Icons.open_in_new),
|
||||
label: Text("Show all"), //TODO: INTL
|
||||
onPressed: () {
|
||||
context.read<DocumentsCubit>().updateFilter(
|
||||
filter: savedView.toDocumentFilter(),
|
||||
|
||||
@@ -192,11 +192,6 @@ class GoRouterShell extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _GoRouterShellState extends State<GoRouterShell> {
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
25
lib/routes/typed/branches/saved_views_route.dart
Normal file
25
lib/routes/typed/branches/saved_views_route.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'package:flutter/src/widgets/framework.dart';
|
||||
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';
|
||||
|
||||
@TypedGoRoute(path: "/saved-views", routes: [])
|
||||
class SavedViewsRoute extends GoRouteData {
|
||||
const SavedViewsRoute();
|
||||
}
|
||||
|
||||
class CreateSavedViewRoute extends GoRouteData {
|
||||
final DocumentFilter? $extra;
|
||||
const CreateSavedViewRoute(this.$extra);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, GoRouterState state) {
|
||||
return AddSavedViewPage(
|
||||
initialFilter: $extra,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EditSavedViewRoute extends GoRouteData {
|
||||
const EditSavedViewRoute();
|
||||
}
|
||||
Reference in New Issue
Block a user