mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-09 18:07:50 -06:00
WIP - More decoupling of data layer from ui layer
This commit is contained in:
@@ -7,15 +7,15 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/view/pages/add_correspondent_page.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/view/pages/add_document_type_page.dart';
|
||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_correspondent_page.dart';
|
||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_document_type_page.dart';
|
||||
import 'package:paperless_mobile/features/edit_label/view/impl/add_storage_path_page.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/providers/labels_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/view/pages/add_storage_path_page.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
@@ -57,147 +57,165 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||
final values = _formKey.currentState!.value;
|
||||
var updatedDocument = widget.document.copyWith(
|
||||
title: values[fkTitle],
|
||||
created: values[fkCreatedDate],
|
||||
overwriteDocumentType: true,
|
||||
documentType: (values[fkDocumentType] as IdQueryParameter).id,
|
||||
overwriteCorrespondent: true,
|
||||
correspondent: (values[fkCorrespondent] as IdQueryParameter).id,
|
||||
overwriteStoragePath: true,
|
||||
storagePath: (values[fkStoragePath] as IdQueryParameter).id,
|
||||
overwriteTags: true,
|
||||
tags: (values[fkTags] as IdsTagsQuery).includedIds,
|
||||
);
|
||||
setState(() {
|
||||
_isSubmitLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
await widget.onEdit(updatedDocument);
|
||||
showSnackBar(context, S.of(context).documentUpdateSuccessMessage);
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
} finally {
|
||||
setState(() {
|
||||
_isSubmitLoading = false;
|
||||
});
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.save),
|
||||
label: Text(S.of(context).genericActionSaveLabel),
|
||||
),
|
||||
appBar: AppBar(
|
||||
title: Text(S.of(context).documentEditPageTitle),
|
||||
bottom: _isSubmitLoading
|
||||
? const PreferredSize(
|
||||
preferredSize: Size.fromHeight(4),
|
||||
child: LinearProgressIndicator(),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
extendBody: true,
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||
top: 8,
|
||||
left: 8,
|
||||
right: 8,
|
||||
return LabelsBlocProvider(
|
||||
child: Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: _onSubmit,
|
||||
icon: const Icon(Icons.save),
|
||||
label: Text(S.of(context).genericActionSaveLabel),
|
||||
),
|
||||
child: FormBuilder(
|
||||
key: _formKey,
|
||||
child: ListView(children: [
|
||||
_buildTitleFormField().padded(),
|
||||
_buildCreatedAtFormField().padded(),
|
||||
BlocBuilder<DocumentTypeCubit, LabelState<DocumentType>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<DocumentType, DocumentTypeQuery>(
|
||||
notAssignedSelectable: false,
|
||||
formBuilderState: _formKey.currentState,
|
||||
labelCreationWidgetBuilder: (currentInput) =>
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<DocumentTypeCubit>(context),
|
||||
child: AddDocumentTypePage(
|
||||
initialName: currentInput,
|
||||
),
|
||||
),
|
||||
label: S.of(context).documentDocumentTypePropertyLabel,
|
||||
initialValue:
|
||||
DocumentTypeQuery.fromId(widget.document.documentType),
|
||||
state: state.labels,
|
||||
name: fkDocumentType,
|
||||
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
||||
queryParameterNotAssignedBuilder:
|
||||
DocumentTypeQuery.notAssigned,
|
||||
prefixIcon: const Icon(Icons.description_outlined),
|
||||
);
|
||||
},
|
||||
).padded(),
|
||||
BlocBuilder<CorrespondentCubit, LabelState<Correspondent>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<Correspondent, CorrespondentQuery>(
|
||||
notAssignedSelectable: false,
|
||||
formBuilderState: _formKey.currentState,
|
||||
labelCreationWidgetBuilder: (initialValue) =>
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<CorrespondentCubit>(context),
|
||||
child: AddCorrespondentPage(initalValue: initialValue),
|
||||
),
|
||||
label: S.of(context).documentCorrespondentPropertyLabel,
|
||||
state: state.labels,
|
||||
initialValue:
|
||||
CorrespondentQuery.fromId(widget.document.correspondent),
|
||||
name: fkCorrespondent,
|
||||
queryParameterIdBuilder: CorrespondentQuery.fromId,
|
||||
queryParameterNotAssignedBuilder:
|
||||
CorrespondentQuery.notAssigned,
|
||||
prefixIcon: const Icon(Icons.person_outlined),
|
||||
);
|
||||
},
|
||||
).padded(),
|
||||
BlocBuilder<StoragePathCubit, LabelState<StoragePath>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<StoragePath, StoragePathQuery>(
|
||||
notAssignedSelectable: false,
|
||||
formBuilderState: _formKey.currentState,
|
||||
labelCreationWidgetBuilder: (initialValue) =>
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<StoragePathCubit>(context),
|
||||
child: AddStoragePathPage(initalValue: initialValue),
|
||||
),
|
||||
label: S.of(context).documentStoragePathPropertyLabel,
|
||||
state: state.labels,
|
||||
initialValue:
|
||||
StoragePathQuery.fromId(widget.document.storagePath),
|
||||
name: fkStoragePath,
|
||||
queryParameterIdBuilder: StoragePathQuery.fromId,
|
||||
queryParameterNotAssignedBuilder:
|
||||
StoragePathQuery.notAssigned,
|
||||
prefixIcon: const Icon(Icons.folder_outlined),
|
||||
);
|
||||
},
|
||||
).padded(),
|
||||
TagFormField(
|
||||
initialValue: IdsTagsQuery.included(widget.document.tags),
|
||||
notAssignedSelectable: false,
|
||||
anyAssignedSelectable: false,
|
||||
excludeAllowed: false,
|
||||
name: fkTags,
|
||||
).padded(),
|
||||
]),
|
||||
appBar: AppBar(
|
||||
title: Text(S.of(context).documentEditPageTitle),
|
||||
bottom: _isSubmitLoading
|
||||
? const PreferredSize(
|
||||
preferredSize: Size.fromHeight(4),
|
||||
child: LinearProgressIndicator(),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
extendBody: true,
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||
top: 8,
|
||||
left: 8,
|
||||
right: 8,
|
||||
),
|
||||
child: FormBuilder(
|
||||
key: _formKey,
|
||||
child: ListView(children: [
|
||||
_buildTitleFormField().padded(),
|
||||
_buildCreatedAtFormField().padded(),
|
||||
_buildDocumentTypeFormField().padded(),
|
||||
_buildCorrespondentFormField().padded(),
|
||||
_buildStoragePathFormField().padded(),
|
||||
TagFormField(
|
||||
initialValue: IdsTagsQuery.included(widget.document.tags),
|
||||
notAssignedSelectable: false,
|
||||
anyAssignedSelectable: false,
|
||||
excludeAllowed: false,
|
||||
name: fkTags,
|
||||
).padded(),
|
||||
]),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>
|
||||
_buildStoragePathFormField() {
|
||||
return BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<StoragePath, StoragePathQuery>(
|
||||
notAssignedSelectable: false,
|
||||
formBuilderState: _formKey.currentState,
|
||||
labelCreationWidgetBuilder: (initialValue) =>
|
||||
RepositoryProvider.value(
|
||||
value: RepositoryProvider.of<LabelRepository<StoragePath>>(context),
|
||||
child: AddStoragePathPage(initalValue: initialValue),
|
||||
),
|
||||
label: S.of(context).documentStoragePathPropertyLabel,
|
||||
state: state.labels,
|
||||
initialValue: StoragePathQuery.fromId(widget.document.storagePath),
|
||||
name: fkStoragePath,
|
||||
queryParameterIdBuilder: StoragePathQuery.fromId,
|
||||
queryParameterNotAssignedBuilder: StoragePathQuery.notAssigned,
|
||||
prefixIcon: const Icon(Icons.folder_outlined),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>
|
||||
_buildCorrespondentFormField() {
|
||||
return BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<Correspondent, CorrespondentQuery>(
|
||||
notAssignedSelectable: false,
|
||||
formBuilderState: _formKey.currentState,
|
||||
labelCreationWidgetBuilder: (initialValue) =>
|
||||
RepositoryProvider.value(
|
||||
value: RepositoryProvider.of<LabelRepository<Correspondent>>(
|
||||
context,
|
||||
),
|
||||
child: AddCorrespondentPage(initialName: initialValue),
|
||||
),
|
||||
label: S.of(context).documentCorrespondentPropertyLabel,
|
||||
state: state.labels,
|
||||
initialValue:
|
||||
CorrespondentQuery.fromId(widget.document.correspondent),
|
||||
name: fkCorrespondent,
|
||||
queryParameterIdBuilder: CorrespondentQuery.fromId,
|
||||
queryParameterNotAssignedBuilder: CorrespondentQuery.notAssigned,
|
||||
prefixIcon: const Icon(Icons.person_outlined),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>
|
||||
_buildDocumentTypeFormField() {
|
||||
return BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<DocumentType, DocumentTypeQuery>(
|
||||
notAssignedSelectable: false,
|
||||
formBuilderState: _formKey.currentState,
|
||||
labelCreationWidgetBuilder: (currentInput) =>
|
||||
RepositoryProvider.value(
|
||||
value: RepositoryProvider.of<LabelRepository<DocumentType>>(
|
||||
context,
|
||||
),
|
||||
child: AddDocumentTypePage(
|
||||
initialName: currentInput,
|
||||
),
|
||||
),
|
||||
label: S.of(context).documentDocumentTypePropertyLabel,
|
||||
initialValue: DocumentTypeQuery.fromId(widget.document.documentType),
|
||||
state: state.labels,
|
||||
name: fkDocumentType,
|
||||
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
||||
queryParameterNotAssignedBuilder: DocumentTypeQuery.notAssigned,
|
||||
prefixIcon: const Icon(Icons.description_outlined),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onSubmit() async {
|
||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||
final values = _formKey.currentState!.value;
|
||||
var updatedDocument = widget.document.copyWith(
|
||||
title: values[fkTitle],
|
||||
created: values[fkCreatedDate],
|
||||
overwriteDocumentType: true,
|
||||
documentType: (values[fkDocumentType] as IdQueryParameter).id,
|
||||
overwriteCorrespondent: true,
|
||||
correspondent: (values[fkCorrespondent] as IdQueryParameter).id,
|
||||
overwriteStoragePath: true,
|
||||
storagePath: (values[fkStoragePath] as IdQueryParameter).id,
|
||||
overwriteTags: true,
|
||||
tags: (values[fkTags] as IdsTagsQuery).includedIds,
|
||||
);
|
||||
setState(() {
|
||||
_isSubmitLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
await widget.onEdit(updatedDocument);
|
||||
showSnackBar(context, S.of(context).documentUpdateSuccessMessage);
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
} finally {
|
||||
setState(() {
|
||||
_isSubmitLoading = false;
|
||||
});
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildTitleFormField() {
|
||||
return FormBuilderTextField(
|
||||
name: fkTitle,
|
||||
|
||||
@@ -3,11 +3,12 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
|
||||
import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.dart';
|
||||
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/document_details/bloc/document_details_cubit.dart';
|
||||
import 'package:paperless_mobile/features/document_details/view/pages/document_details_page.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/documents_empty_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/grid/document_grid.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list.dart';
|
||||
@@ -15,11 +16,10 @@ import 'package:paperless_mobile/features/documents/view/widgets/search/document
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/documents_page_app_bar.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/sort_documents_button.dart';
|
||||
import 'package:paperless_mobile/features/home/view/widget/info_drawer.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/bloc/tags_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/providers/labels_bloc_provider.dart';
|
||||
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_state.dart';
|
||||
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
|
||||
import 'package:paperless_mobile/features/settings/model/view_type.dart';
|
||||
@@ -132,9 +132,20 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
),
|
||||
body: _buildBody(connectivityState),
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
panelBuilder: (scrollController) => DocumentFilterPanel(
|
||||
panelController: _filterPanelController,
|
||||
scrollController: scrollController,
|
||||
panelBuilder: (scrollController) =>
|
||||
BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
builder: (context, state) {
|
||||
return LabelsBlocProvider(
|
||||
child: DocumentFilterPanel(
|
||||
panelController: _filterPanelController,
|
||||
scrollController: scrollController,
|
||||
initialFilter: state.filter,
|
||||
onFilterChanged: (filter) =>
|
||||
BlocProvider.of<DocumentsCubit>(context)
|
||||
.updateFilter(filter: filter),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -192,21 +203,46 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
onRefresh: _onRefresh,
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
DocumentsPageAppBar(
|
||||
actions: [
|
||||
const SortDocumentsButton(),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
settings.preferredViewType == ViewType.grid
|
||||
? Icons.list
|
||||
: Icons.grid_view,
|
||||
),
|
||||
onPressed: () =>
|
||||
BlocProvider.of<ApplicationSettingsCubit>(context)
|
||||
.setViewType(
|
||||
settings.preferredViewType.toggle()),
|
||||
BlocProvider(
|
||||
create: (context) => SavedViewCubit(
|
||||
RepositoryProvider.of<SavedViewRepository>(context)),
|
||||
child: BlocListener<SavedViewCubit, SavedViewState>(
|
||||
listener: (context, state) {
|
||||
final documentsCubit =
|
||||
BlocProvider.of<DocumentsCubit>(context);
|
||||
try {
|
||||
if (state.selectedSavedViewId == null) {
|
||||
documentsCubit.updateFilter();
|
||||
} else {
|
||||
final newFilter = state
|
||||
.value[state.selectedSavedViewId]
|
||||
?.toDocumentFilter();
|
||||
if (newFilter != null) {
|
||||
documentsCubit.updateFilter(filter: newFilter);
|
||||
}
|
||||
}
|
||||
} on PaperlessServerException catch (error, stackTrace) {
|
||||
showErrorMessage(context, error, stackTrace);
|
||||
}
|
||||
},
|
||||
child: DocumentsPageAppBar(
|
||||
actions: [
|
||||
const SortDocumentsButton(),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
settings.preferredViewType == ViewType.grid
|
||||
? Icons.list
|
||||
: Icons.grid_view,
|
||||
),
|
||||
onPressed: () =>
|
||||
BlocProvider.of<ApplicationSettingsCubit>(
|
||||
context)
|
||||
.setViewType(
|
||||
settings.preferredViewType.toggle()),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child,
|
||||
SliverToBoxAdapter(
|
||||
@@ -233,29 +269,11 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
||||
MaterialPageRoute<DocumentModel?> _buildDetailsPageRoute(
|
||||
DocumentModel document) {
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<DocumentsCubit>(context),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<CorrespondentCubit>(context),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<DocumentTypeCubit>(context),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<TagCubit>(context),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value: BlocProvider.of<StoragePathCubit>(context),
|
||||
),
|
||||
BlocProvider.value(
|
||||
value:
|
||||
DocumentDetailsCubit(getIt<PaperlessDocumentsApi>(), document),
|
||||
),
|
||||
],
|
||||
child: const DocumentDetailsPage(),
|
||||
builder: (_) => BlocProvider.value(
|
||||
value: DocumentDetailsCubit(getIt<PaperlessDocumentsApi>(), document),
|
||||
child: const LabelRepositoriesProvider(
|
||||
child: DocumentDetailsPage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/empty_state.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/bloc/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class DocumentsEmptyState extends StatelessWidget {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/grid/document_grid_item.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.dart';
|
||||
import 'package:paperless_mobile/core/widgets/documents_list_loading_widget.dart';
|
||||
import 'package:paperless_mobile/core/widgets/offline_widget.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/list/document_list_item.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
|
||||
@@ -34,21 +36,23 @@ class DocumentListView extends StatelessWidget {
|
||||
builderDelegate: PagedChildBuilderDelegate(
|
||||
animateTransitions: true,
|
||||
itemBuilder: (context, document, index) {
|
||||
return DocumentListItem(
|
||||
isLabelClickable: isLabelClickable,
|
||||
document: document,
|
||||
onTap: onTap,
|
||||
isSelected: state.selection.contains(document),
|
||||
onSelected: onSelected,
|
||||
isAtLeastOneSelected: state.selection.isNotEmpty,
|
||||
isTagSelectedPredicate: (int tagId) {
|
||||
return state.filter.tags is IdsTagsQuery
|
||||
? (state.filter.tags as IdsTagsQuery)
|
||||
.includedIds
|
||||
.contains(tagId)
|
||||
: false;
|
||||
},
|
||||
onTagSelected: onTagSelected,
|
||||
return LabelRepositoriesProvider(
|
||||
child: DocumentListItem(
|
||||
isLabelClickable: isLabelClickable,
|
||||
document: document,
|
||||
onTap: onTap,
|
||||
isSelected: state.selection.contains(document),
|
||||
onSelected: onSelected,
|
||||
isAtLeastOneSelected: state.selection.isNotEmpty,
|
||||
isTagSelectedPredicate: (int tagId) {
|
||||
return state.filter.tags is IdsTagsQuery
|
||||
? (state.filter.tags as IdsTagsQuery)
|
||||
.includedIds
|
||||
.contains(tagId)
|
||||
: false;
|
||||
},
|
||||
onTagSelected: onTagSelected,
|
||||
),
|
||||
);
|
||||
},
|
||||
noItemsFoundIndicatorBuilder: (context) => hasInternetConnection
|
||||
|
||||
@@ -4,15 +4,12 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/search/query_type_form_field.dart';
|
||||
import 'package:paperless_mobile/features/labels/correspondent/bloc/correspondents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/document_type/bloc/document_type_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/bloc/label_state.dart';
|
||||
import 'package:paperless_mobile/features/labels/storage_path/bloc/storage_path_cubit.dart';
|
||||
import 'package:paperless_mobile/features/labels/tags/view/widgets/tags_form_field.dart';
|
||||
import 'package:paperless_mobile/features/labels/view/widgets/label_form_field.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/bloc/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
@@ -24,10 +21,15 @@ class DocumentFilterPanel extends StatefulWidget {
|
||||
final PanelController panelController;
|
||||
final ScrollController scrollController;
|
||||
|
||||
final DocumentFilter initialFilter;
|
||||
|
||||
final void Function(DocumentFilter filter) onFilterChanged;
|
||||
const DocumentFilterPanel({
|
||||
Key? key,
|
||||
required this.panelController,
|
||||
required this.scrollController,
|
||||
required this.onFilterChanged,
|
||||
required this.initialFilter,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -63,93 +65,84 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
),
|
||||
child: BlocConsumer<DocumentsCubit, DocumentsState>(
|
||||
listener: (context, state) {
|
||||
// Set initial values, otherwise they would not automatically update.
|
||||
_patchFromFilter(state.filter);
|
||||
},
|
||||
builder: (context, state) {
|
||||
return FormBuilder(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
child: FormBuilder(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
children: [
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
_buildDragLine(),
|
||||
Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: TextButton.icon(
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: Text(
|
||||
S.of(context).documentsFilterPageResetFilterLabel),
|
||||
onPressed: () => _resetFilter(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).documentsFilterPageTitle,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: _onApplyFilter,
|
||||
child: Text(
|
||||
S.of(context).documentsFilterPageApplyFilterLabel),
|
||||
),
|
||||
],
|
||||
).padded(),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
),
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(16.0),
|
||||
topRight: Radius.circular(16.0),
|
||||
),
|
||||
child: ListView(
|
||||
controller: widget.scrollController,
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
S.of(context).documentsFilterPageSearchLabel),
|
||||
).padded(const EdgeInsets.only(left: 8.0)),
|
||||
_buildQueryFormField(state),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
S.of(context).documentsFilterPageAdvancedLabel),
|
||||
).padded(const EdgeInsets.only(left: 8.0, top: 8.0)),
|
||||
_buildCreatedDateRangePickerFormField(state).padded(),
|
||||
_buildAddedDateRangePickerFormField(state).padded(),
|
||||
_buildCorrespondentFormField(state).padded(),
|
||||
_buildDocumentTypeFormField(state).padded(),
|
||||
_buildStoragePathFormField(state).padded(),
|
||||
TagFormField(
|
||||
name: DocumentModel.tagsKey,
|
||||
initialValue: state.filter.tags,
|
||||
allowCreation: false,
|
||||
).padded(),
|
||||
// Required in order for the storage path field to be visible when typing
|
||||
const SizedBox(
|
||||
height: 150,
|
||||
),
|
||||
],
|
||||
).padded(),
|
||||
_buildDragLine(),
|
||||
Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: TextButton.icon(
|
||||
icon: const Icon(Icons.refresh),
|
||||
label:
|
||||
Text(S.of(context).documentsFilterPageResetFilterLabel),
|
||||
onPressed: () => _resetFilter(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).documentsFilterPageTitle,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: _onApplyFilter,
|
||||
child:
|
||||
Text(S.of(context).documentsFilterPageApplyFilterLabel),
|
||||
),
|
||||
],
|
||||
).padded(),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
),
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(16.0),
|
||||
topRight: Radius.circular(16.0),
|
||||
),
|
||||
child: ListView(
|
||||
controller: widget.scrollController,
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(S.of(context).documentsFilterPageSearchLabel),
|
||||
).padded(const EdgeInsets.only(left: 8.0)),
|
||||
_buildQueryFormField(),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child:
|
||||
Text(S.of(context).documentsFilterPageAdvancedLabel),
|
||||
).padded(const EdgeInsets.only(left: 8.0, top: 8.0)),
|
||||
_buildCreatedDateRangePickerFormField().padded(),
|
||||
_buildAddedDateRangePickerFormField().padded(),
|
||||
_buildCorrespondentFormField().padded(),
|
||||
_buildDocumentTypeFormField().padded(),
|
||||
_buildStoragePathFormField().padded(),
|
||||
TagFormField(
|
||||
name: DocumentModel.tagsKey,
|
||||
initialValue: widget.initialFilter.tags,
|
||||
allowCreation: false,
|
||||
).padded(),
|
||||
// Required in order for the storage path field to be visible when typing
|
||||
const SizedBox(
|
||||
height: 150,
|
||||
),
|
||||
],
|
||||
).padded(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -163,15 +156,16 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildDocumentTypeFormField(DocumentsState docState) {
|
||||
return BlocBuilder<DocumentTypeCubit, LabelState<DocumentType>>(
|
||||
//TODO: Check if the blocs can be found in the context, otherwise just provide repository and create new bloc inside LabelFormField!
|
||||
Widget _buildDocumentTypeFormField() {
|
||||
return BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<DocumentType, DocumentTypeQuery>(
|
||||
formBuilderState: _formKey.currentState,
|
||||
name: fkDocumentType,
|
||||
state: state.labels,
|
||||
label: S.of(context).documentDocumentTypePropertyLabel,
|
||||
initialValue: docState.filter.documentType,
|
||||
initialValue: widget.initialFilter.documentType,
|
||||
queryParameterIdBuilder: DocumentTypeQuery.fromId,
|
||||
queryParameterNotAssignedBuilder: DocumentTypeQuery.notAssigned,
|
||||
prefixIcon: const Icon(Icons.description_outlined),
|
||||
@@ -180,15 +174,15 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCorrespondentFormField(DocumentsState docState) {
|
||||
return BlocBuilder<CorrespondentCubit, LabelState<Correspondent>>(
|
||||
Widget _buildCorrespondentFormField() {
|
||||
return BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<Correspondent, CorrespondentQuery>(
|
||||
formBuilderState: _formKey.currentState,
|
||||
name: fkCorrespondent,
|
||||
state: state.labels,
|
||||
label: S.of(context).documentCorrespondentPropertyLabel,
|
||||
initialValue: docState.filter.correspondent,
|
||||
initialValue: widget.initialFilter.correspondent,
|
||||
queryParameterIdBuilder: CorrespondentQuery.fromId,
|
||||
queryParameterNotAssignedBuilder: CorrespondentQuery.notAssigned,
|
||||
prefixIcon: const Icon(Icons.person_outline),
|
||||
@@ -197,15 +191,15 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStoragePathFormField(DocumentsState docState) {
|
||||
return BlocBuilder<StoragePathCubit, LabelState<StoragePath>>(
|
||||
Widget _buildStoragePathFormField() {
|
||||
return BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>(
|
||||
builder: (context, state) {
|
||||
return LabelFormField<StoragePath, StoragePathQuery>(
|
||||
formBuilderState: _formKey.currentState,
|
||||
name: fkStoragePath,
|
||||
state: state.labels,
|
||||
label: S.of(context).documentStoragePathPropertyLabel,
|
||||
initialValue: docState.filter.storagePath,
|
||||
initialValue: widget.initialFilter.storagePath,
|
||||
queryParameterIdBuilder: StoragePathQuery.fromId,
|
||||
queryParameterNotAssignedBuilder: StoragePathQuery.notAssigned,
|
||||
prefixIcon: const Icon(Icons.folder_outlined),
|
||||
@@ -214,7 +208,7 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildQueryFormField(DocumentsState state) {
|
||||
Widget _buildQueryFormField() {
|
||||
final queryType =
|
||||
_formKey.currentState?.getRawValue(QueryTypeFormField.fkQueryType) ??
|
||||
QueryType.titleAndContent;
|
||||
@@ -239,16 +233,15 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
prefixIcon: const Icon(Icons.search_outlined),
|
||||
labelText: label,
|
||||
suffixIcon: QueryTypeFormField(
|
||||
initialValue: state.filter.queryType,
|
||||
initialValue: widget.initialFilter.queryType,
|
||||
afterSelected: (queryType) => setState(() {}),
|
||||
),
|
||||
),
|
||||
initialValue: state.filter.queryText,
|
||||
initialValue: widget.initialFilter.queryText,
|
||||
).padded();
|
||||
}
|
||||
|
||||
Widget _buildDateRangePickerHelper(
|
||||
DocumentsState state, String formFieldKey) {
|
||||
Widget _buildDateRangePickerHelper(String formFieldKey) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
@@ -328,13 +321,13 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCreatedDateRangePickerFormField(DocumentsState state) {
|
||||
Widget _buildCreatedDateRangePickerFormField() {
|
||||
return Column(
|
||||
children: [
|
||||
FormBuilderDateRangePicker(
|
||||
initialValue: _dateTimeRangeOfNullable(
|
||||
state.filter.createdDateAfter,
|
||||
state.filter.createdDateBefore,
|
||||
widget.initialFilter.createdDateAfter,
|
||||
widget.initialFilter.createdDateBefore,
|
||||
),
|
||||
// Workaround for theme data not being correctly passed to daterangepicker, see
|
||||
// https://github.com/flutter/flutter/issues/87580
|
||||
@@ -371,18 +364,18 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4.0),
|
||||
_buildDateRangePickerHelper(state, fkCreatedAt),
|
||||
_buildDateRangePickerHelper(fkCreatedAt),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAddedDateRangePickerFormField(DocumentsState state) {
|
||||
Widget _buildAddedDateRangePickerFormField() {
|
||||
return Column(
|
||||
children: [
|
||||
FormBuilderDateRangePicker(
|
||||
initialValue: _dateTimeRangeOfNullable(
|
||||
state.filter.addedDateAfter,
|
||||
state.filter.addedDateBefore,
|
||||
widget.initialFilter.addedDateAfter,
|
||||
widget.initialFilter.addedDateBefore,
|
||||
),
|
||||
// Workaround for theme data not being correctly passed to daterangepicker, see
|
||||
// https://github.com/flutter/flutter/issues/87580
|
||||
@@ -419,7 +412,7 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4.0),
|
||||
_buildDateRangePickerHelper(state, fkAddedAt),
|
||||
_buildDateRangePickerHelper(fkAddedAt),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class SortFieldSelectionBottomSheet extends StatefulWidget {
|
||||
const SortFieldSelectionBottomSheet({super.key});
|
||||
final SortOrder initialSortOrder;
|
||||
final SortField initialSortField;
|
||||
|
||||
final Future Function(SortField field, SortOrder order) onSubmit;
|
||||
|
||||
const SortFieldSelectionBottomSheet({
|
||||
super.key,
|
||||
required this.initialSortOrder,
|
||||
required this.initialSortField,
|
||||
required this.onSubmit,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SortFieldSelectionBottomSheet> createState() =>
|
||||
@@ -17,81 +23,60 @@ class SortFieldSelectionBottomSheet extends StatefulWidget {
|
||||
|
||||
class _SortFieldSelectionBottomSheetState
|
||||
extends State<SortFieldSelectionBottomSheet> {
|
||||
SortField? _selectedFieldLoading;
|
||||
SortOrder? _selectedOrderLoading;
|
||||
late SortField _currentSortField;
|
||||
late SortOrder _currentSortOrder;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_currentSortField = widget.initialSortField;
|
||||
_currentSortOrder = widget.initialSortOrder;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ClipRRect(
|
||||
child: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
bloc: getIt<DocumentsCubit>(),
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).documentsPageOrderByLabel,
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
textAlign: TextAlign.start,
|
||||
).padded(
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 16)),
|
||||
Column(
|
||||
children: SortField.values
|
||||
.map(
|
||||
(e) => _buildSortOption(
|
||||
e,
|
||||
state.filter.sortOrder,
|
||||
state.filter.sortField == e,
|
||||
_selectedFieldLoading == e,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
),
|
||||
TextButton(
|
||||
child: Text(S.of(context).documentsFilterPageApplyFilterLabel),
|
||||
onPressed: () => widget.onSubmit(
|
||||
_currentSortField,
|
||||
_currentSortOrder,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: SortField.values.map(_buildSortOption).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSortOption(
|
||||
SortField field,
|
||||
SortOrder order,
|
||||
bool isCurrentlySelected,
|
||||
bool isNextSelected,
|
||||
) {
|
||||
return ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
title: Text(
|
||||
_localizedSortField(field),
|
||||
),
|
||||
trailing: isNextSelected
|
||||
? (_buildOrderIcon(_selectedOrderLoading!))
|
||||
: (_selectedOrderLoading == null && isCurrentlySelected
|
||||
? _buildOrderIcon(order)
|
||||
: null),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_selectedFieldLoading = field;
|
||||
_selectedOrderLoading =
|
||||
isCurrentlySelected ? order.toggle() : SortOrder.descending;
|
||||
});
|
||||
BlocProvider.of<DocumentsCubit>(context)
|
||||
.updateCurrentFilter((filter) => filter.copyWith(
|
||||
sortOrder: isCurrentlySelected
|
||||
? order.toggle()
|
||||
: SortOrder.descending,
|
||||
sortField: field,
|
||||
))
|
||||
.whenComplete(() {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_selectedFieldLoading = null;
|
||||
_selectedOrderLoading = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
trailing: _currentSortField == field
|
||||
? _buildOrderIcon(_currentSortOrder)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class BulkDeleteConfirmationDialog extends StatelessWidget {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/selection/bulk_delete_confirmation_dialog.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart';
|
||||
import 'package:paperless_mobile/features/saved_view/view/saved_view_selection_widget.dart';
|
||||
import 'package:paperless_mobile/generated/l10n.dart';
|
||||
import 'package:paperless_mobile/util.dart';
|
||||
@@ -79,7 +80,6 @@ class _DocumentsPageAppBarState extends State<DocumentsPageAppBar> {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
//TODO: replace with sorting stuff...
|
||||
SavedViewSelectionWidget(height: 48, enabled: enabled),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:paperless_mobile/di_initializer.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_cubit.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/search/sort_field_selection_bottom_sheet.dart';
|
||||
|
||||
@@ -33,11 +32,19 @@ class _SortDocumentsButtonState extends State<SortDocumentsButton> {
|
||||
topRight: Radius.circular(16),
|
||||
),
|
||||
),
|
||||
builder: (context) => BlocProvider.value(
|
||||
value: getIt<DocumentsCubit>(),
|
||||
child: const FractionallySizedBox(
|
||||
heightFactor: .6,
|
||||
child: SortFieldSelectionBottomSheet(),
|
||||
builder: (context) => FractionallySizedBox(
|
||||
heightFactor: .6,
|
||||
child: BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
builder: (context, state) {
|
||||
return SortFieldSelectionBottomSheet(
|
||||
initialSortField: state.filter.sortField,
|
||||
initialSortOrder: state.filter.sortOrder,
|
||||
onSubmit: (field, order) =>
|
||||
BlocProvider.of<DocumentsCubit>(context).updateCurrentFilter(
|
||||
(filter) => filter.copyWith(sortField: field, sortOrder: order),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user