mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-10 14:07:59 -06:00
Cleaned up code, implemented message queue to notify subscribers of document updates.
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||
import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/core/notifier/document_changed_notifier.dart';
|
||||
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||
import 'package:paperless_mobile/features/documents/bloc/documents_state.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/paged_documents_mixin.dart';
|
||||
|
||||
@@ -17,14 +17,21 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
||||
final DocumentChangedNotifier notifier;
|
||||
|
||||
DocumentsCubit(this.api, this.notifier) : super(const DocumentsState()) {
|
||||
reload();
|
||||
notifier.subscribe(
|
||||
this,
|
||||
onUpdated: replace,
|
||||
onDeleted: remove,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> bulkRemove(List<DocumentModel> documents) async {
|
||||
log("[DocumentsCubit] bulkRemove");
|
||||
Future<void> bulkDelete(List<DocumentModel> documents) async {
|
||||
debugPrint("[DocumentsCubit] bulkRemove");
|
||||
await api.bulkAction(
|
||||
BulkDeleteAction(documents.map((doc) => doc.id)),
|
||||
);
|
||||
for (final deletedDoc in documents) {
|
||||
notifier.notifyDeleted(deletedDoc);
|
||||
}
|
||||
await reload();
|
||||
}
|
||||
|
||||
@@ -33,7 +40,7 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
||||
Iterable<int> addTags = const [],
|
||||
Iterable<int> removeTags = const [],
|
||||
}) async {
|
||||
log("[DocumentsCubit] bulkEditTags");
|
||||
debugPrint("[DocumentsCubit] bulkEditTags");
|
||||
await api.bulkAction(BulkModifyTagsAction(
|
||||
documents.map((doc) => doc.id),
|
||||
addTags: addTags,
|
||||
@@ -43,7 +50,7 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
||||
}
|
||||
|
||||
void toggleDocumentSelection(DocumentModel model) {
|
||||
log("[DocumentsCubit] toggleSelection");
|
||||
debugPrint("[DocumentsCubit] toggleSelection");
|
||||
if (state.selectedIds.contains(model.id)) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
@@ -58,12 +65,12 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
||||
}
|
||||
|
||||
void resetSelection() {
|
||||
log("[DocumentsCubit] resetSelection");
|
||||
debugPrint("[DocumentsCubit] resetSelection");
|
||||
emit(state.copyWith(selection: []));
|
||||
}
|
||||
|
||||
void reset() {
|
||||
log("[DocumentsCubit] reset");
|
||||
debugPrint("[DocumentsCubit] reset");
|
||||
emit(const DocumentsState());
|
||||
}
|
||||
|
||||
@@ -81,4 +88,10 @@ class DocumentsCubit extends HydratedCubit<DocumentsState>
|
||||
Map<String, dynamic>? toJson(DocumentsState state) {
|
||||
return state.toJson();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
notifier.unsubscribe(this);
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:paperless_api/paperless_api.dart';
|
||||
import 'package:paperless_mobile/features/paged_document_view/model/paged_documents_state.dart';
|
||||
|
||||
class DocumentsState extends PagedDocumentsState {
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: true, includeToJson: false)
|
||||
final List<DocumentModel> selection;
|
||||
|
||||
const DocumentsState({
|
||||
@@ -34,11 +34,8 @@ class DocumentsState extends PagedDocumentsState {
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
hasLoaded,
|
||||
filter,
|
||||
value,
|
||||
selection,
|
||||
isLoading,
|
||||
...super.props,
|
||||
];
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
|
||||
@@ -160,8 +160,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
notAssignedSelectable: false,
|
||||
formBuilderState: _formKey.currentState,
|
||||
labelCreationWidgetBuilder: (initialValue) => RepositoryProvider(
|
||||
create: (context) => context.read<
|
||||
LabelRepository<StoragePath, StoragePathRepositoryState>>(),
|
||||
create: (context) => context.read<LabelRepository<StoragePath>>(),
|
||||
child: AddStoragePathPage(initalValue: initialValue),
|
||||
),
|
||||
textFieldLabel: S.of(context).documentStoragePathPropertyLabel,
|
||||
@@ -182,8 +181,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
notAssignedSelectable: false,
|
||||
formBuilderState: _formKey.currentState,
|
||||
labelCreationWidgetBuilder: (initialValue) => RepositoryProvider(
|
||||
create: (context) => context.read<
|
||||
LabelRepository<Correspondent, CorrespondentRepositoryState>>(),
|
||||
create: (context) => context.read<LabelRepository<Correspondent>>(),
|
||||
child: AddCorrespondentPage(initialName: initialValue),
|
||||
),
|
||||
textFieldLabel: S.of(context).documentCorrespondentPropertyLabel,
|
||||
@@ -215,8 +213,7 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
|
||||
notAssignedSelectable: false,
|
||||
formBuilderState: _formKey.currentState,
|
||||
labelCreationWidgetBuilder: (currentInput) => RepositoryProvider(
|
||||
create: (context) => context.read<
|
||||
LabelRepository<DocumentType, DocumentTypeRepositoryState>>(),
|
||||
create: (context) => context.read<LabelRepository<DocumentType>>(),
|
||||
child: AddDocumentTypePage(
|
||||
initialName: currentInput,
|
||||
),
|
||||
|
||||
@@ -249,7 +249,7 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return RefreshIndicator(
|
||||
edgeOffset: kToolbarHeight,
|
||||
edgeOffset: kToolbarHeight + kTextTabBarHeight,
|
||||
onRefresh: _onReloadDocuments,
|
||||
notificationPredicate: (_) =>
|
||||
connectivityState.isConnected,
|
||||
@@ -263,13 +263,14 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
),
|
||||
_buildViewActions(),
|
||||
BlocBuilder<DocumentsCubit, DocumentsState>(
|
||||
buildWhen: (previous, current) =>
|
||||
!const ListEquality().equals(
|
||||
previous.documents,
|
||||
current.documents,
|
||||
) ||
|
||||
previous.selectedIds !=
|
||||
current.selectedIds,
|
||||
// Not required anymore since saved views are now handled separately
|
||||
// buildWhen: (previous, current) =>
|
||||
// !const ListEquality().equals(
|
||||
// previous.documents,
|
||||
// current.documents,
|
||||
// ) ||
|
||||
// previous.selectedIds !=
|
||||
// current.selectedIds,
|
||||
builder: (context, state) {
|
||||
if (state.hasLoaded &&
|
||||
state.documents.isEmpty) {
|
||||
@@ -323,7 +324,7 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return RefreshIndicator(
|
||||
edgeOffset: kToolbarHeight,
|
||||
edgeOffset: kToolbarHeight + kTextTabBarHeight,
|
||||
onRefresh: _onReloadSavedViews,
|
||||
notificationPredicate: (_) =>
|
||||
connectivityState.isConnected,
|
||||
@@ -390,7 +391,7 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
try {
|
||||
await context
|
||||
.read<DocumentsCubit>()
|
||||
.bulkRemove(documentsState.selection);
|
||||
.bulkDelete(documentsState.selection);
|
||||
showSnackBar(
|
||||
context,
|
||||
S.of(context).documentsPageBulkDeleteSuccessfulText,
|
||||
@@ -467,20 +468,14 @@ class _DocumentsPageState extends State<DocumentsPage>
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _openDetails(DocumentModel document) async {
|
||||
final updatedModel = await Navigator.pushNamed(
|
||||
void _openDetails(DocumentModel document) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
DocumentDetailsRoute.routeName,
|
||||
arguments: DocumentDetailsRouteArguments(
|
||||
document: document,
|
||||
),
|
||||
) as DocumentModel?;
|
||||
// final updatedModel = await Navigator.of(context).push<DocumentModel?>(
|
||||
// _buildDetailsPageRoute(document),
|
||||
// );
|
||||
if (updatedModel != document) {
|
||||
context.read<DocumentsCubit>().reload();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void _addTagToFilter(int tagId) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
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/bloc/documents_state.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';
|
||||
@@ -23,6 +23,7 @@ abstract class AdaptiveDocumentsView extends StatelessWidget {
|
||||
final void Function(int? id)? onDocumentTypeSelected;
|
||||
final void Function(int? id)? onStoragePathSelected;
|
||||
|
||||
bool get showLoadingPlaceholder => (!hasLoaded && isLoading);
|
||||
const AdaptiveDocumentsView({
|
||||
super.key,
|
||||
this.selectedDocumentIds = const [],
|
||||
@@ -56,6 +57,7 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
||||
super.onTap,
|
||||
super.selectedDocumentIds,
|
||||
super.viewType,
|
||||
super.enableHeroAnimation,
|
||||
required super.isLoading,
|
||||
required super.hasLoaded,
|
||||
});
|
||||
@@ -71,8 +73,8 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
||||
}
|
||||
|
||||
Widget _buildListView() {
|
||||
if (!hasLoaded && isLoading) {
|
||||
return const DocumentsListLoadingWidget();
|
||||
if (showLoadingPlaceholder) {
|
||||
return DocumentsListLoadingWidget.sliver();
|
||||
}
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
@@ -91,6 +93,7 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
||||
onCorrespondentSelected: onCorrespondentSelected,
|
||||
onDocumentTypeSelected: onDocumentTypeSelected,
|
||||
onStoragePathSelected: onStoragePathSelected,
|
||||
enableHeroAnimation: enableHeroAnimation,
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -99,8 +102,8 @@ class SliverAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
||||
}
|
||||
|
||||
Widget _buildGridView() {
|
||||
if (!hasLoaded && isLoading) {
|
||||
return const DocumentsListLoadingWidget();
|
||||
if (showLoadingPlaceholder) {
|
||||
return DocumentGridLoadingWidget.sliver();
|
||||
}
|
||||
return SliverGrid.builder(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
@@ -162,10 +165,8 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
||||
}
|
||||
|
||||
Widget _buildListView() {
|
||||
if (!hasLoaded && isLoading) {
|
||||
return const CustomScrollView(slivers: [
|
||||
DocumentsListLoadingWidget(),
|
||||
]);
|
||||
if (showLoadingPlaceholder) {
|
||||
return DocumentsListLoadingWidget();
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
@@ -194,12 +195,8 @@ class DefaultAdaptiveDocumentsView extends AdaptiveDocumentsView {
|
||||
}
|
||||
|
||||
Widget _buildGridView() {
|
||||
if (!hasLoaded && isLoading) {
|
||||
return const CustomScrollView(
|
||||
slivers: [
|
||||
DocumentsListLoadingWidget(),
|
||||
],
|
||||
); //TODO: Build grid skeleton
|
||||
if (showLoadingPlaceholder) {
|
||||
return DocumentGridLoadingWidget();
|
||||
}
|
||||
return GridView.builder(
|
||||
controller: scrollController,
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:paperless_mobile/core/widgets/shimmer_placeholder.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/document_preview.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/placeholder/document_item_placeholder.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/placeholder/tags_placeholder.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/placeholder/text_placeholder.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
|
||||
class DocumentGridLoadingWidget extends StatelessWidget
|
||||
with DocumentItemPlaceholder {
|
||||
final bool _isSliver;
|
||||
@override
|
||||
final Random random = Random(1257195195);
|
||||
DocumentGridLoadingWidget({super.key}) : _isSliver = false;
|
||||
|
||||
DocumentGridLoadingWidget.sliver({super.key}) : _isSliver = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const delegate = SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 4,
|
||||
crossAxisSpacing: 4,
|
||||
childAspectRatio: 1 / 2,
|
||||
);
|
||||
if (_isSliver) {
|
||||
return SliverGrid.builder(
|
||||
gridDelegate: delegate,
|
||||
itemBuilder: (context, index) => _buildPlaceholderGridItem(context),
|
||||
);
|
||||
}
|
||||
return GridView.builder(
|
||||
gridDelegate: delegate,
|
||||
itemBuilder: (context, index) => _buildPlaceholderGridItem(context),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPlaceholderGridItem(BuildContext context) {
|
||||
final values = nextValues;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Card(
|
||||
elevation: 1.0,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ShimmerPlaceholder(
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Container(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ShimmerPlaceholder(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextPlaceholder(
|
||||
length: values.correspondentLength,
|
||||
fontSize: 16,
|
||||
).padded(1),
|
||||
TextPlaceholder(
|
||||
length: values.titleLength,
|
||||
fontSize: 16,
|
||||
),
|
||||
if (values.tagCount > 0) ...[
|
||||
const Spacer(),
|
||||
TagsPlaceholder(
|
||||
count: values.tagCount,
|
||||
dense: true,
|
||||
),
|
||||
],
|
||||
const Spacer(),
|
||||
TextPlaceholder(
|
||||
length: 100,
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.bodySmall!.fontSize!,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:paperless_mobile/core/widgets/shimmer_placeholder.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/placeholder/document_item_placeholder.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/placeholder/tags_placeholder.dart';
|
||||
import 'package:paperless_mobile/features/documents/view/widgets/placeholder/text_placeholder.dart';
|
||||
|
||||
class DocumentsListLoadingWidget extends StatelessWidget {
|
||||
static const _tags = [" ", " ", " "];
|
||||
static const _titleLengths = <double>[double.infinity, 150.0, 200.0];
|
||||
static const _correspondentLengths = <double>[200.0, 300.0, 150.0];
|
||||
static const _fontSize = 16.0;
|
||||
class DocumentsListLoadingWidget extends StatelessWidget
|
||||
with DocumentItemPlaceholder {
|
||||
final bool _isSliver;
|
||||
DocumentsListLoadingWidget({super.key}) : _isSliver = false;
|
||||
|
||||
const DocumentsListLoadingWidget({super.key
|
||||
});
|
||||
DocumentsListLoadingWidget.sliver({super.key}) : _isSliver = true;
|
||||
|
||||
@override
|
||||
final Random random = Random(1209571050);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _random = Random();
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return _buildFakeListItem(context, _random);
|
||||
},
|
||||
),
|
||||
);
|
||||
if (_isSliver) {
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) => _buildFakeListItem(context),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return ListView.builder(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildFakeListItem(context),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildFakeListItem(BuildContext context, Random random) {
|
||||
final tagCount = random.nextInt(_tags.length + 1);
|
||||
final correspondentLength =
|
||||
_correspondentLengths[random.nextInt(_correspondentLengths.length - 1)];
|
||||
final titleLength = _titleLengths[random.nextInt(_titleLengths.length - 1)];
|
||||
return Shimmer.fromColors(
|
||||
baseColor: Theme.of(context).brightness == Brightness.light
|
||||
? Colors.grey[300]!
|
||||
: Colors.grey[900]!,
|
||||
highlightColor: Theme.of(context).brightness == Brightness.light
|
||||
? Colors.grey[100]!
|
||||
: Colors.grey[600]!,
|
||||
Widget _buildFakeListItem(BuildContext context) {
|
||||
const fontSize = 14.0;
|
||||
final values = nextValues;
|
||||
return ShimmerPlaceholder(
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.all(8),
|
||||
dense: true,
|
||||
@@ -45,15 +45,17 @@ class DocumentsListLoadingWidget extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
color: Colors.white,
|
||||
height: 50,
|
||||
height: double.infinity,
|
||||
width: 35,
|
||||
),
|
||||
),
|
||||
title: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2.0),
|
||||
width: correspondentLength,
|
||||
height: _fontSize,
|
||||
color: Colors.white,
|
||||
title: Row(
|
||||
children: [
|
||||
TextPlaceholder(
|
||||
length: values.correspondentLength,
|
||||
fontSize: fontSize,
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2.0),
|
||||
@@ -61,21 +63,16 @@ class DocumentsListLoadingWidget extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2.0),
|
||||
height: _fontSize,
|
||||
width: titleLength,
|
||||
color: Colors.white,
|
||||
TextPlaceholder(
|
||||
length: values.titleLength,
|
||||
fontSize: fontSize,
|
||||
),
|
||||
if (values.tagCount > 0)
|
||||
TagsPlaceholder(count: values.tagCount, dense: true),
|
||||
TextPlaceholder(
|
||||
length: 100,
|
||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize!,
|
||||
),
|
||||
Wrap(
|
||||
spacing: 2.0,
|
||||
children: List.generate(
|
||||
tagCount,
|
||||
(index) => InputChip(
|
||||
label: Text(_tags[random.nextInt(_tags.length)]),
|
||||
),
|
||||
),
|
||||
).paddedOnly(top: 4),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -56,7 +56,7 @@ class DocumentListItem extends DocumentItem {
|
||||
Text(
|
||||
document.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: document.tags.isEmpty ? 2 : 1,
|
||||
maxLines: 1,
|
||||
),
|
||||
AbsorbPointer(
|
||||
absorbing: isSelectionActive,
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import 'dart:math';
|
||||
|
||||
mixin DocumentItemPlaceholder {
|
||||
static const _tags = [" ", " ", " "];
|
||||
static const _titleLengths = <double>[double.infinity, 150.0, 200.0];
|
||||
static const _correspondentLengths = <double>[120.0, 80.0, 40.0];
|
||||
|
||||
Random get random;
|
||||
|
||||
RandomDocumentItemPlaceholderValues get nextValues {
|
||||
return RandomDocumentItemPlaceholderValues(
|
||||
tagCount: random.nextInt(_tags.length + 1),
|
||||
correspondentLength: _correspondentLengths[
|
||||
random.nextInt(_correspondentLengths.length - 1)],
|
||||
titleLength: _titleLengths[random.nextInt(_titleLengths.length - 1)],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RandomDocumentItemPlaceholderValues {
|
||||
final int tagCount;
|
||||
final double correspondentLength;
|
||||
final double titleLength;
|
||||
|
||||
RandomDocumentItemPlaceholderValues({
|
||||
required this.tagCount,
|
||||
required this.correspondentLength,
|
||||
required this.titleLength,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TagsPlaceholder extends StatelessWidget {
|
||||
static const _lengths = [24, 36, 16, 48];
|
||||
final int count;
|
||||
final bool dense;
|
||||
const TagsPlaceholder({
|
||||
super.key,
|
||||
required this.count,
|
||||
required this.dense,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 32,
|
||||
child: ListView.separated(
|
||||
itemCount: count,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) => FilterChip(
|
||||
labelPadding:
|
||||
dense ? const EdgeInsets.symmetric(horizontal: 2) : null,
|
||||
padding: dense ? const EdgeInsets.all(4) : null,
|
||||
visualDensity: const VisualDensity(vertical: -2),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
side: BorderSide.none,
|
||||
onSelected: (_) {},
|
||||
selected: false,
|
||||
label: Text(
|
||||
List.filled(_lengths[index], " ").join(),
|
||||
),
|
||||
),
|
||||
separatorBuilder: (context, _) => const SizedBox(width: 4),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||
|
||||
class TextPlaceholder extends StatelessWidget {
|
||||
final double length;
|
||||
final double fontSize;
|
||||
|
||||
const TextPlaceholder({
|
||||
super.key,
|
||||
required this.length,
|
||||
required this.fontSize,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
width: length,
|
||||
height: fontSize,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -44,16 +44,12 @@ class SortDocumentsButton extends StatelessWidget {
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (context) => LabelCubit<DocumentType>(
|
||||
context.read<
|
||||
LabelRepository<DocumentType,
|
||||
DocumentTypeRepositoryState>>(),
|
||||
context.read<LabelRepository<DocumentType>>(),
|
||||
),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => LabelCubit<Correspondent>(
|
||||
context.read<
|
||||
LabelRepository<Correspondent,
|
||||
CorrespondentRepositoryState>>(),
|
||||
context.read<LabelRepository<Correspondent>>(),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user