Daily sign history

This commit is contained in:
LittleSheep 2024-09-08 00:23:59 +08:00
parent 4e8f2ddef3
commit 0a04c72468
5 changed files with 368 additions and 54 deletions

View File

@ -2,9 +2,30 @@ import 'package:get/get.dart';
import 'package:solian/exceptions/request.dart'; import 'package:solian/exceptions/request.dart';
import 'package:solian/exceptions/unauthorized.dart'; import 'package:solian/exceptions/unauthorized.dart';
import 'package:solian/models/daily_sign.dart'; import 'package:solian/models/daily_sign.dart';
import 'package:solian/models/pagination.dart';
import 'package:solian/providers/auth.dart'; import 'package:solian/providers/auth.dart';
class DailySignProvider extends GetxController { class DailySignProvider extends GetxController {
Future<List<DailySignRecord>> listLastRecord(int take) async {
final AuthProvider auth = Get.find();
if (auth.isAuthorized.isFalse) throw const UnauthorizedException();
final client = auth.configureClient('id');
final resp = await client.get('/daily?take=$take');
if (resp.statusCode != 200 && resp.statusCode != 404) {
throw RequestException(resp);
} else if (resp.statusCode == 404) {
return List.empty();
}
final result = PaginationResult.fromJson(resp.body);
return List.from(
result.data?.map((x) => DailySignRecord.fromJson(x)) ?? [],
);
}
Future<DailySignRecord?> getToday() async { Future<DailySignRecord?> getToday() async {
final AuthProvider auth = Get.find(); final AuthProvider auth = Get.find();
if (auth.isAuthorized.isFalse) throw const UnauthorizedException(); if (auth.isAuthorized.isFalse) throw const UnauthorizedException();

View File

@ -24,6 +24,7 @@ import 'package:solian/providers/websocket.dart';
import 'package:solian/router.dart'; import 'package:solian/router.dart';
import 'package:solian/screens/account/notification.dart'; import 'package:solian/screens/account/notification.dart';
import 'package:solian/widgets/chat/chat_event.dart'; import 'package:solian/widgets/chat/chat_event.dart';
import 'package:solian/widgets/daily_sign/history_chart.dart';
import 'package:solian/widgets/posts/post_list.dart'; import 'package:solian/widgets/posts/post_list.dart';
class DashboardScreen extends StatefulWidget { class DashboardScreen extends StatefulWidget {
@ -80,10 +81,14 @@ class _DashboardScreenState extends State<DashboardScreen> {
bool _signingDaily = true; bool _signingDaily = true;
DailySignRecord? _signRecord; DailySignRecord? _signRecord;
List<DailySignRecord>? _signRecordHistory;
Future<void> _pullDaily() async { Future<void> _pullDaily() async {
try { try {
_signRecord = await _dailySign.getToday(); _signRecord = await _dailySign.getToday();
_dailySign.listLastRecord(30).then((value) {
setState(() => _signRecordHistory = value);
});
} catch (e) { } catch (e) {
context.showErrorDialog(e); context.showErrorDialog(e);
} }
@ -137,7 +142,9 @@ class _DashboardScreenState extends State<DashboardScreen> {
], ],
).paddingOnly(top: 8, left: 18, right: 18, bottom: 12), ).paddingOnly(top: 8, left: 18, right: 18, bottom: 12),
Card( Card(
child: ListTile( child: Column(
children: [
ListTile(
leading: AnimatedSwitcher( leading: AnimatedSwitcher(
switchInCurve: Curves.fastOutSlowIn, switchInCurve: Curves.fastOutSlowIn,
switchOutCurve: Curves.fastOutSlowIn, switchOutCurve: Curves.fastOutSlowIn,
@ -165,7 +172,8 @@ class _DashboardScreenState extends State<DashboardScreen> {
) )
: Text( : Text(
_signRecord!.symbol, _signRecord!.symbol,
style: GoogleFonts.notoSerifHk(fontSize: 20, height: 1), style: GoogleFonts.notoSerifHk(
fontSize: 20, height: 1),
).paddingSymmetric(horizontal: 9), ).paddingSymmetric(horizontal: 9),
).paddingOnly(left: 4), ).paddingOnly(left: 4),
title: _signRecord == null title: _signRecord == null
@ -190,8 +198,23 @@ class _DashboardScreenState extends State<DashboardScreen> {
icon: const Icon(Icons.local_fire_department), icon: const Icon(Icons.local_fire_department),
onPressed: _signingDaily ? null : _signDaily, onPressed: _signingDaily ? null : _signDaily,
) )
: const SizedBox.shrink(), : IconButton(
tooltip: '查看运势历史',
icon: const Icon(Icons.history),
onPressed: () {
showDialog(
context: context,
useRootNavigator: true,
builder: (context) =>
DailySignHistoryChartDialog(
data: _signRecordHistory,
), ),
);
},
),
),
),
],
), ),
).paddingSymmetric(horizontal: 8), ).paddingSymmetric(horizontal: 8),
const Divider(thickness: 0.3).paddingSymmetric(vertical: 8), const Divider(thickness: 0.3).paddingSymmetric(vertical: 8),

View File

@ -0,0 +1,253 @@
import 'dart:math';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:solian/models/daily_sign.dart';
class DailySignHistoryChartDialog extends StatelessWidget {
final List<DailySignRecord>? data;
const DailySignHistoryChartDialog({super.key, required this.data});
static List<String> signSymbols = ['大凶', '', '中平', '', '大吉'];
DateTime? get _firstRecordDate => data?.map((x) => x.createdAt).reduce(
(a, b) => DateTime.fromMillisecondsSinceEpoch(
min(a.millisecondsSinceEpoch, b.millisecondsSinceEpoch)));
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('运势历史'),
Text(
'${DateFormat('yyyy/MM/dd').format(_firstRecordDate!)} - ${DateFormat('yyyy/MM/dd').format(DateTime.now())}',
style: TextStyle(
fontSize: 13,
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
),
),
],
),
content: data == null
? SizedBox(
height: 180,
width: max(640, MediaQuery.of(context).size.width),
child: const Center(
child: CircularProgressIndicator(),
),
)
: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('近期运势', style: Theme.of(context).textTheme.titleMedium)
.paddingOnly(bottom: 18),
SizedBox(
height: 180,
width: max(640, MediaQuery.of(context).size.width),
child: LineChart(
LineChartData(
lineBarsData: [
LineChartBarData(
isCurved: true,
isStrokeCapRound: true,
isStrokeJoinRound: true,
color: Theme.of(context).colorScheme.primary,
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
colors: List.filled(
data!.length,
Theme.of(context)
.colorScheme
.primary
.withOpacity(0.3),
).toList(),
),
),
spots: data!
.map(
(x) => FlSpot(
x.createdAt
.copyWith(
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
)
.millisecondsSinceEpoch
.toDouble(),
x.resultTier.toDouble(),
),
)
.toList(),
)
],
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
getTooltipItems: (spots) => spots
.map((spot) => LineTooltipItem(
'${signSymbols[spot.y.toInt()]}\n${DateFormat('MM/dd').format(DateTime.fromMillisecondsSinceEpoch(spot.x.toInt()))}',
TextStyle(
color:
Theme.of(context).colorScheme.onSurface,
),
))
.toList(),
getTooltipColor: (_) =>
Theme.of(context).colorScheme.surfaceContainerHigh,
)),
titlesData: FlTitlesData(
topTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
rightTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 40,
interval: 1,
getTitlesWidget: (value, _) => Align(
alignment: Alignment.centerRight,
child: Text(
signSymbols[value.toInt()],
textAlign: TextAlign.right,
).paddingOnly(right: 8),
),
),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 28,
interval: 86400000,
getTitlesWidget: (value, _) => Text(
DateFormat('MM/dd').format(
DateTime.fromMillisecondsSinceEpoch(
value.toInt(),
),
),
textAlign: TextAlign.center,
).paddingOnly(top: 8),
),
),
),
gridData: const FlGridData(show: false),
borderData: FlBorderData(show: false),
),
),
).marginOnly(right: 24, bottom: 8, top: 8),
const Gap(16),
Text('功德趋势', style: Theme.of(context).textTheme.titleMedium)
.paddingOnly(bottom: 18),
SizedBox(
height: 180,
width: max(640, MediaQuery.of(context).size.width),
child: LineChart(
LineChartData(
lineBarsData: [
LineChartBarData(
isCurved: true,
isStrokeCapRound: true,
isStrokeJoinRound: true,
color: Theme.of(context).colorScheme.primary,
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
colors: List.filled(
data!.length,
Theme.of(context)
.colorScheme
.primary
.withOpacity(0.3),
).toList(),
),
),
spots: data!
.map(
(x) => FlSpot(
x.createdAt
.copyWith(
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
)
.millisecondsSinceEpoch
.toDouble(),
x.resultExperience.toDouble(),
),
)
.toList(),
)
],
lineTouchData: LineTouchData(
touchTooltipData: LineTouchTooltipData(
getTooltipItems: (spots) => spots
.map((spot) => LineTooltipItem(
'+${spot.y.toStringAsFixed(0)} EXP\n${DateFormat('MM/dd').format(DateTime.fromMillisecondsSinceEpoch(spot.x.toInt()))}',
TextStyle(
color:
Theme.of(context).colorScheme.onSurface,
),
))
.toList(),
getTooltipColor: (_) =>
Theme.of(context).colorScheme.surfaceContainerHigh,
)),
titlesData: FlTitlesData(
topTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
rightTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 40,
getTitlesWidget: (value, _) => Align(
alignment: Alignment.centerRight,
child: Text(
value.toStringAsFixed(0),
textAlign: TextAlign.right,
).paddingOnly(right: 8),
),
),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 28,
interval: 86400000,
getTitlesWidget: (value, _) => Text(
DateFormat('MM/dd').format(
DateTime.fromMillisecondsSinceEpoch(
value.toInt(),
),
),
textAlign: TextAlign.center,
).paddingOnly(top: 8),
),
),
),
gridData: const FlGridData(show: false),
borderData: FlBorderData(show: false),
),
),
).marginOnly(right: 24, bottom: 8, top: 8),
],
),
);
}
}

View File

@ -390,6 +390,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.9" version: "2.3.9"
equatable:
dependency: transitive
description:
name: equatable
sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2
url: "https://pub.dev"
source: hosted
version: "2.0.5"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -582,6 +590,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
fl_chart:
dependency: "direct main"
description:
name: fl_chart
sha256: "94307bef3a324a0d329d3ab77b2f0c6e5ed739185ffc029ed28c0f9b019ea7ef"
url: "https://pub.dev"
source: hosted
version: "0.69.0"
floor: floor:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -77,6 +77,7 @@ dependencies:
freezed_annotation: ^2.4.4 freezed_annotation: ^2.4.4
json_annotation: ^4.9.0 json_annotation: ^4.9.0
gap: ^3.0.1 gap: ^3.0.1
fl_chart: ^0.69.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: