You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
4.9 KiB
182 lines
4.9 KiB
import 'dart:math'; |
|
|
|
import 'package:flutter/material.dart'; |
|
import 'package:neon/blocs.dart'; |
|
import 'package:neon/theme.dart'; |
|
import 'package:neon/utils.dart'; |
|
import 'package:neon/widgets.dart'; |
|
import 'package:neon_dashboard/l10n/localizations.dart'; |
|
import 'package:neon_dashboard/src/blocs/dashboard.dart'; |
|
import 'package:neon_dashboard/src/widgets/dry_intrinsic_height.dart'; |
|
import 'package:neon_dashboard/src/widgets/widget.dart'; |
|
import 'package:neon_dashboard/src/widgets/widget_button.dart'; |
|
import 'package:neon_dashboard/src/widgets/widget_item.dart'; |
|
import 'package:nextcloud/dashboard.dart' as dashboard; |
|
|
|
/// Displays the whole dashboard page layout. |
|
class DashboardMainPage extends StatelessWidget { |
|
/// Creates a new dashboard main page. |
|
const DashboardMainPage({ |
|
super.key, |
|
}); |
|
|
|
@override |
|
Widget build(final BuildContext context) { |
|
final bloc = NeonProvider.of<DashboardBloc>(context); |
|
|
|
return ResultBuilder.behaviorSubject( |
|
subject: bloc.widgets, |
|
builder: (final context, final snapshot) { |
|
Widget? child; |
|
if (snapshot.hasData) { |
|
var minHeight = 504.0; |
|
|
|
final children = <Widget>[]; |
|
for (final widget in snapshot.requireData.entries) { |
|
final items = buildWidgetItems( |
|
context: context, |
|
widget: widget.key, |
|
items: widget.value, |
|
).toList(); |
|
|
|
final height = items.map((final i) => i.height!).reduce((final a, final b) => a + b); |
|
minHeight = max(minHeight, height); |
|
|
|
children.add( |
|
DashboardWidget( |
|
widget: widget.key, |
|
children: items, |
|
), |
|
); |
|
} |
|
|
|
child = Wrap( |
|
alignment: WrapAlignment.center, |
|
spacing: 8, |
|
runSpacing: 8, |
|
children: children |
|
.map( |
|
(final widget) => SizedBox( |
|
width: 320, |
|
height: minHeight + 24, |
|
child: widget, |
|
), |
|
) |
|
.toList(), |
|
); |
|
} |
|
|
|
return Center( |
|
child: NeonListView.custom( |
|
scrollKey: 'dashboard', |
|
isLoading: snapshot.isLoading, |
|
error: snapshot.error, |
|
onRefresh: bloc.refresh, |
|
sliver: SliverFillRemaining( |
|
hasScrollBody: false, |
|
child: Center( |
|
child: child, |
|
), |
|
), |
|
), |
|
); |
|
}, |
|
); |
|
} |
|
|
|
/// Builds the list of messages, [items] and buttons for a [widget]. |
|
@visibleForTesting |
|
static Iterable<SizedBox> buildWidgetItems({ |
|
required final BuildContext context, |
|
required final dashboard.Widget widget, |
|
required final dashboard.WidgetItems? items, |
|
}) sync* { |
|
yield SizedBox( |
|
height: 64, |
|
child: DryIntrinsicHeight( |
|
child: ListTile( |
|
title: Text( |
|
widget.title, |
|
style: const TextStyle( |
|
fontWeight: FontWeight.bold, |
|
), |
|
), |
|
leading: SizedBox.square( |
|
dimension: largeIconSize, |
|
child: NeonUrlImage( |
|
url: widget.iconUrl, |
|
svgColorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn), |
|
size: const Size.square(largeIconSize), |
|
), |
|
), |
|
), |
|
), |
|
); |
|
|
|
yield const SizedBox( |
|
height: 20, |
|
); |
|
|
|
final halfEmptyContentMessage = _buildMessage(items?.halfEmptyContentMessage); |
|
final emptyContentMessage = _buildMessage(items?.emptyContentMessage); |
|
if (halfEmptyContentMessage != null) { |
|
yield halfEmptyContentMessage; |
|
} |
|
if (emptyContentMessage != null) { |
|
yield emptyContentMessage; |
|
} |
|
if (halfEmptyContentMessage == null && emptyContentMessage == null && (items?.items.isEmpty ?? true)) { |
|
yield _buildMessage(DashboardLocalizations.of(context).noEntries)!; |
|
} |
|
|
|
if (items?.items != null) { |
|
for (final item in items!.items) { |
|
yield SizedBox( |
|
height: 64, |
|
child: DryIntrinsicHeight( |
|
child: DashboardWidgetItem( |
|
item: item, |
|
roundIcon: widget.itemIconsRound, |
|
), |
|
), |
|
); |
|
} |
|
} |
|
|
|
yield const SizedBox( |
|
height: 20, |
|
); |
|
|
|
if (widget.buttons != null) { |
|
for (final button in widget.buttons!) { |
|
yield SizedBox( |
|
height: 32, |
|
child: DashboardWidgetButton( |
|
button: button, |
|
), |
|
); |
|
} |
|
} |
|
} |
|
|
|
static SizedBox? _buildMessage(final String? message) { |
|
if (message == null || message.isEmpty) { |
|
return null; |
|
} |
|
|
|
return SizedBox( |
|
height: 60, |
|
child: Center( |
|
child: Column( |
|
children: [ |
|
const Icon( |
|
Icons.check, |
|
size: largeIconSize, |
|
), |
|
Text(message), |
|
], |
|
), |
|
), |
|
); |
|
} |
|
}
|
|
|