feat: Add detailed view type

This commit is contained in:
Anton Stubenbord
2023-02-12 18:48:24 +01:00
parent edd8b899c8
commit 681d551b56
5 changed files with 205 additions and 54 deletions

View File

@@ -18,6 +18,7 @@ import 'package:paperless_mobile/features/saved_view/cubit/saved_view_cubit.dart
import 'package:paperless_mobile/features/saved_view/view/add_saved_view_page.dart';
import 'package:paperless_mobile/features/saved_view/view/saved_view_list.dart';
import 'package:paperless_mobile/features/search_app_bar/view/search_app_bar.dart';
import 'package:paperless_mobile/features/settings/model/view_type.dart';
import 'package:paperless_mobile/features/tasks/cubit/task_status_cubit.dart';
import 'package:paperless_mobile/generated/l10n.dart';
import 'package:paperless_mobile/helpers/message_helpers.dart';
@@ -247,55 +248,62 @@ class _DocumentsPageState extends State<DocumentsPage>
onRefresh: _onReloadDocuments,
notificationPredicate: (_) =>
connectivityState.isConnected,
child: CustomScrollView(
key: const PageStorageKey<String>("documents"),
slivers: <Widget>[
SliverOverlapInjector(
handle: NestedScrollView
.sliverOverlapAbsorberHandleFor(
context),
),
_buildViewActions(),
child:
BlocBuilder<DocumentsCubit, DocumentsState>(
builder: (context, state) {
if (state.hasLoaded &&
state.documents.isEmpty) {
return SliverToBoxAdapter(
child: DocumentsEmptyState(
state: state,
onReset: () {
context
.read<DocumentsCubit>()
.resetFilter();
},
),
);
}
builder: (context, state) {
return CustomScrollView(
key: const PageStorageKey<String>(
"documents"),
slivers: <Widget>[
SliverOverlapInjector(
handle: NestedScrollView
.sliverOverlapAbsorberHandleFor(
context),
),
_buildViewActions(),
Builder(
builder: (context) {
if (state.hasLoaded &&
state.documents.isEmpty) {
return SliverToBoxAdapter(
child: DocumentsEmptyState(
state: state,
onReset: () {
context
.read<DocumentsCubit>()
.resetFilter();
},
),
);
}
return SliverAdaptiveDocumentsView(
viewType: state.viewType,
onTap: _openDetails,
onSelected: context
.read<DocumentsCubit>()
.toggleDocumentSelection,
hasInternetConnection:
connectivityState.isConnected,
onTagSelected: _addTagToFilter,
onCorrespondentSelected:
_addCorrespondentToFilter,
onDocumentTypeSelected:
_addDocumentTypeToFilter,
onStoragePathSelected:
_addStoragePathToFilter,
documents: state.documents,
hasLoaded: state.hasLoaded,
isLabelClickable: true,
isLoading: state.isLoading,
selectedDocumentIds: state.selectedIds,
);
},
),
],
return SliverAdaptiveDocumentsView(
viewType: state.viewType,
onTap: _openDetails,
onSelected: context
.read<DocumentsCubit>()
.toggleDocumentSelection,
hasInternetConnection:
connectivityState.isConnected,
onTagSelected: _addTagToFilter,
onCorrespondentSelected:
_addCorrespondentToFilter,
onDocumentTypeSelected:
_addDocumentTypeToFilter,
onStoragePathSelected:
_addStoragePathToFilter,
documents: state.documents,
hasLoaded: state.hasLoaded,
isLabelClickable: true,
isLoading: state.isLoading,
selectedDocumentIds:
state.selectedIds,
);
},
),
],
);
},
),
);
},

View File

@@ -3,6 +3,7 @@ import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/repository/provider/label_repositories_provider.dart';
import 'package:paperless_mobile/features/documents/view/widgets/document_grid_loading_widget.dart';
import 'package:paperless_mobile/features/documents/view/widgets/documents_list_loading_widget.dart';
import 'package:paperless_mobile/features/documents/view/widgets/items/document_detailed_item.dart';
import 'package:paperless_mobile/features/documents/view/widgets/items/document_grid_item.dart';
import 'package:paperless_mobile/features/documents/view/widgets/items/document_list_item.dart';
import 'package:paperless_mobile/features/settings/model/view_type.dart';
@@ -69,6 +70,8 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView {
return _buildGridView();
case ViewType.list:
return _buildListView();
case ViewType.detailed:
return _buildFullView();
}
}
@@ -101,6 +104,36 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView {
);
}
Widget _buildFullView() {
if (showLoadingPlaceholder) {
//TODO: Build detailed loading animation
return DocumentsListLoadingWidget.sliver();
}
return SliverList(
delegate: SliverChildBuilderDelegate(
childCount: documents.length,
(context, index) {
final document = documents.elementAt(index);
return LabelRepositoriesProvider(
child: DocumentDetailedItem(
isLabelClickable: isLabelClickable,
document: document,
onTap: onTap,
isSelected: selectedDocumentIds.contains(document.id),
onSelected: onSelected,
isSelectionActive: selectedDocumentIds.isNotEmpty,
onTagSelected: onTagSelected,
onCorrespondentSelected: onCorrespondentSelected,
onDocumentTypeSelected: onDocumentTypeSelected,
onStoragePathSelected: onStoragePathSelected,
enableHeroAnimation: enableHeroAnimation,
),
);
},
),
);
}
Widget _buildGridView() {
if (showLoadingPlaceholder) {
return DocumentGridLoadingWidget.sliver();
@@ -161,6 +194,8 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView {
return _buildGridView();
case ViewType.list:
return _buildListView();
case ViewType.detailed:
return _buildFullView();
}
}
@@ -194,6 +229,37 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView {
);
}
Widget _buildFullView() {
if (showLoadingPlaceholder) {
return DocumentsListLoadingWidget();
}
return ListView.builder(
physics: const PageScrollPhysics(),
controller: scrollController,
primary: false,
itemCount: documents.length,
itemBuilder: (context, index) {
final document = documents.elementAt(index);
return LabelRepositoriesProvider(
child: DocumentDetailedItem(
isLabelClickable: isLabelClickable,
document: document,
onTap: onTap,
isSelected: selectedDocumentIds.contains(document.id),
onSelected: onSelected,
isSelectionActive: selectedDocumentIds.isNotEmpty,
onTagSelected: onTagSelected,
onCorrespondentSelected: onCorrespondentSelected,
onDocumentTypeSelected: onDocumentTypeSelected,
onStoragePathSelected: onStoragePathSelected,
enableHeroAnimation: enableHeroAnimation,
),
);
},
);
}
Widget _buildGridView() {
if (showLoadingPlaceholder) {
return DocumentGridLoadingWidget();

View File

@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart';
import 'package:paperless_mobile/features/documents/view/widgets/items/document_item.dart';
class DocumentDetailedItem extends DocumentItem {
const DocumentDetailedItem({
super.key,
required super.document,
required super.isSelected,
required super.isSelectionActive,
required super.isLabelClickable,
required super.enableHeroAnimation,
super.onCorrespondentSelected,
super.onDocumentTypeSelected,
super.onSelected,
super.onStoragePathSelected,
super.onTagSelected,
super.onTap,
});
@override
Widget build(BuildContext context) {
return Card(
child: Column(
children: [
DocumentPreview(id: document.id),
],
),
);
}
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/features/settings/model/view_type.dart';
/// Meant to be used with blocbuilder.
@@ -14,13 +15,56 @@ class ViewTypeSelectionWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final next = viewType.toggle();
final icon = next == ViewType.grid ? Icons.grid_view_rounded : Icons.list;
return IconButton(
icon: Icon(icon),
onPressed: () {
late final IconData icon;
switch (viewType) {
case ViewType.grid:
icon = Icons.grid_view_rounded;
break;
case ViewType.list:
icon = Icons.list;
break;
case ViewType.detailed:
icon = Icons.article_outlined;
break;
}
return PopupMenuButton<ViewType>(
child: Icon(icon),
itemBuilder: (context) => [
_buildViewTypeOption(
ViewType.list,
'List',
Icons.list,
),
_buildViewTypeOption(
ViewType.grid,
'Grid',
Icons.grid_view_rounded,
),
_buildViewTypeOption(
ViewType.detailed,
'Detailed',
Icons.article_outlined,
),
],
onSelected: (next) {
onChanged(next);
},
);
}
PopupMenuItem<ViewType> _buildViewTypeOption(
ViewType type,
String label,
IconData icon,
) {
return PopupMenuItem(
value: type,
child: ListTile(
selected: type == viewType,
trailing: type == viewType ? const Icon(Icons.done) : null,
title: Text(label),
leading: Icon(icon),
),
);
}
}