✨ Daily sign history
This commit is contained in:
parent
4e8f2ddef3
commit
0a04c72468
@ -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();
|
||||||
|
@ -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),
|
||||||
|
253
lib/widgets/daily_sign/history_chart.dart
Normal file
253
lib/widgets/daily_sign/history_chart.dart
Normal 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),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
16
pubspec.lock
16
pubspec.lock
@ -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:
|
||||||
|
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user