Files
AcWeb/ac_web/lib/Widgets/LatestReportColumn.dart
T

178 lines
4.9 KiB
Dart

import 'package:ac_web/Api/AcApi.dart';
import 'package:ac_web/Api/Dtos/EReportType.dart';
import 'package:ac_web/Api/Dtos/ReportDto.dart';
import 'package:ac_web/Pages/ReportDetails.dart';
import 'package:ac_web/Widgets/AutoRefreshState.dart';
import 'package:ac_web/Widgets/LatestReportEntry.dart';
import 'package:ac_web/Widgets/LatestReportHeader.dart';
import 'package:ac_web/Widgets/ReportTypeIcons.dart';
import 'package:flutter/material.dart';
class LatestReportColumn extends StatefulWidget{
const LatestReportColumn({super.key, this.title = "Latest Reports", this.filterType});
/// Headline drawn above the first day header.
final String title;
/// When set, only reports of this type are shown.
final EReportType? filterType;
@override
State<LatestReportColumn> createState() => _LatestReportColumnState();
}
class _LatestReportColumnState extends State<LatestReportColumn>
with AutoRefreshState<LatestReportColumn> {
late Future<List<ReportDto>> mReportsFuture;
@override
void initState() {
super.initState();
mReportsFuture = AcApi.instance.reports.getReports();
}
/// Automatic reload. The reports that are already on screen stay visible
/// while the request runs, the future is only replaced once it succeeded.
@override
Future<void> RefreshData() async {
final List<ReportDto> reports = await AcApi.instance.reports.getReports();
if (!mounted) {
return;
}
setState(() {
mReportsFuture = Future<List<ReportDto>>.value(reports);
});
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List<ReportDto>>(
future: mReportsFuture,
builder: (context, snapshot) {
// Not `connectionState`: a refresh puts the builder back into the
// waiting state although the previous reports are still available, and
// those should stay on screen instead of flashing a spinner.
if (!snapshot.hasData && !snapshot.hasError) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError && !snapshot.hasData) {
return Center(child: Text('Could not load reports: ${snapshot.error}'));
}
// The list of reports fetched from the ReportsController.
List<ReportDto> reports = snapshot.data ?? <ReportDto>[];
if (widget.filterType != null) {
reports = reports
.where((report) => report.type == widget.filterType)
.toList();
}
return _buildList(reports);
},
);
}
Widget _buildList(List<ReportDto> reports) {
List<Widget> widgets = <Widget>[];
if (reports.isEmpty) {
return ListView(
padding: EdgeInsetsGeometry.all(8),
children: <Widget>[
LatestReportHeader(text: widget.title, bigHeader: true,),
const SizedBox(height: 16),
const Text("No reports available."),
],
);
}
// Title Entry
widgets.add(LatestReportHeader(text: widget.title, bigHeader: true,));
DateTime? lastDay;
// Iterate through the reports and create Report entries accordingly
for(int i = 0; i < reports.length; i++){
// Add a day header above the first entry of every day
final DateTime day = _dayOf(reports[i].timestamp);
if(lastDay == null || day != lastDay){
widgets.add(LatestReportHeader(text: _dayLabel(day)));
lastDay = day;
}
final ReportDto report = reports[i];
final IconData icon = reports[i].type.icon;
widgets.add(LatestReportEntry(
text: reports[i].title,
icon: icon,
author: reports[i].author,
timestamp: reports[i].timestamp,
onTap: () => _openReport(report),
));
}
return ListView(
padding: EdgeInsetsGeometry.all(8),
children: widgets,
);
}
/// Opens a new page that shows all details of the given report.
void _openReport(ReportDto report) {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (context) => ReportDetails(report: report),
),
);
}
/// Strips the time part, so that two timestamps of the same day are equal.
DateTime _dayOf(DateTime timestamp) {
final DateTime local = timestamp.toLocal();
return DateTime(local.year, local.month, local.day);
}
/// "Today", "Yesterday" or e.g. "15. August" for all other days.
String _dayLabel(DateTime day) {
final DateTime today = _dayOf(DateTime.now());
final DateTime yesterday = DateTime(today.year, today.month, today.day - 1);
if (day == today) {
return "Today";
}
if (day == yesterday) {
return "Yesterday";
}
return "${day.day}. ${_monthNames[day.month - 1]}";
}
static const List<String> _monthNames = <String>[
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
}