diff --git a/README.md b/README.md index 2b5a373..5a85a5a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ - ~~Stats Calculator~~ - ~~Ability to compare player with himself in past~~ - ~~Tetra League matches history~~ *dev build are here* -- Tetra League historic charts for tracked players (maybe even same sh*t for 40l and blitz well see) +- ~~Tetra League historic charts for tracked players~~ (bit mess idk) - Better UI with delta and hints for stats *that will be v0.2.0* - Ability to compare player with APM-PPS-VS stats - Ability to fetch Tetra League leaderboard diff --git a/lib/data_objects/tetrio.dart b/lib/data_objects/tetrio.dart index aad9899..e9eacf9 100644 --- a/lib/data_objects/tetrio.dart +++ b/lib/data_objects/tetrio.dart @@ -614,11 +614,11 @@ class EndContextMulti { naturalOrder = json['naturalorder']; wins = json['wins']; points = json['points']['primary']; - secondary = json['points']['secondary']; - tertiary = json['points']['tertiary']; + secondary = json['points']['secondary'].toDouble(); + tertiary = json['points']['tertiary'].toDouble(); secondaryTracking = json['points']['secondaryAvgTracking'].cast(); tertiaryTracking = json['points']['tertiaryAvgTracking'].cast(); - extra = json['points']['extra']['vs']; + extra = json['points']['extra']['vs'].toDouble(); extraTracking = json['points']['extraAvgTracking']['aggregatestats___vsscore'].cast(); nerdStats = NerdStats(secondary, tertiary, extra); estTr = EstTr(secondary, tertiary, extra, noTrRd, nerdStats.app, nerdStats.dss, nerdStats.dsp, nerdStats.gbe); diff --git a/lib/services/tetrio_crud.dart b/lib/services/tetrio_crud.dart index 3def9b3..69c9e3c 100644 --- a/lib/services/tetrio_crud.dart +++ b/lib/services/tetrio_crud.dart @@ -136,7 +136,7 @@ class TetrioService extends DB { : []; var zen = TetrioZen.fromJson(jsonRecords['data']['zen']); Map map = {"user": userID.toLowerCase().trim(), "sprint": sprint, "blitz": blitz, "zen": zen}; - developer.log("fetchRecords: $userID stream retrieved and cached", name: "services/tetrio_crud"); + developer.log("fetchRecords: $userID records retrieved and cached", name: "services/tetrio_crud"); _recordsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = map; return map; } else { diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index b87ed21..4bdb67e 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:fl_chart/fl_chart.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter/services.dart'; import 'package:tetra_stats/data_objects/tetrio.dart'; @@ -20,6 +21,7 @@ const allowedHeightForPlayerBioInPixels = 30.0; const givenTextHeightByScreenPercentage = 0.3; final NumberFormat timeInSec = NumberFormat("#,###.###s."); final NumberFormat f2 = NumberFormat.decimalPatternDigits(decimalDigits: 2); +final DateFormat dateFormat = DateFormat.yMMMd().add_Hms(); class MainView extends StatefulWidget { const MainView({Key? key}) : super(key: key); @@ -106,10 +108,14 @@ class _MainState extends State with SingleTickerProviderStateMixin { Future fetch(String nickOrID) async { TetrioPlayer me = await teto.fetchPlayer(nickOrID); setState((){_titleNickname = me.username;}); - bool isTracking = await teto.isPlayerTracking(nickOrID); - if (isTracking) teto.storeState(me); + bool isTracking = await teto.isPlayerTracking(me.userId); + List states = []; + if (isTracking){ + teto.storeState(me); + states.addAll(await teto.getPlayer(me.userId)); + } Map records = await teto.fetchRecords(me.userId); - return [me, records, isTracking]; + return [me, records, states, isTracking]; } void _justUpdate() { @@ -233,7 +239,7 @@ class _MainState extends State with SingleTickerProviderStateMixin { tl: snapshot.data![0].tlSeason1, userID: snapshot.data![0].userId), _TLRecords(userID: snapshot.data![0].userId), - Text("kekwa"), + _TLHistory(states: snapshot.data![2]), _RecordThingy( record: (snapshot.data![1]['sprint'].isNotEmpty) ? snapshot.data![1]['sprint'][0] @@ -415,7 +421,7 @@ class _TLRecords extends StatelessWidget { ), );}, )] - : [const Text("No records",style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28))], + : [const Center(child: Text("No records", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))], ); } } @@ -423,6 +429,77 @@ class _TLRecords extends StatelessWidget { } } +class _TLHistory extends StatelessWidget{ + final List states; + const _TLHistory({super.key, required this.states}); + + @override + Widget build(BuildContext context) { + bool bigScreen = MediaQuery.of(context).size.width > 768; + List trData = [for (var state in states) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.rating)]; + List apmData = [for (var state in states) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.apm!)]; + List ppsData = [for (var state in states) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.pps!)]; + List vsData = [for (var state in states) FlSpot(state.state.millisecondsSinceEpoch.toDouble(), state.tlSeason1.vs!)]; + return ListView(physics: const ClampingScrollPhysics(), + children: states.isNotEmpty ? [ + Column( + children: [ + _HistoryChartThigy(data: trData, title: "Tetra Rating", yAxisTitle: "TR", bigScreen: bigScreen), + _HistoryChartThigy(data: apmData, title: "Attack Per Minute", yAxisTitle: "APM", bigScreen: bigScreen), + _HistoryChartThigy(data: ppsData, title: "Pieces Per Second", yAxisTitle: "PPS", bigScreen: bigScreen), + _HistoryChartThigy(data: vsData, title: "Versus Score", yAxisTitle: "VS", bigScreen: bigScreen), + ], + ), + ] : [const Center(child: Text("No history saved", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))]); + } +} + +class _HistoryChartThigy extends StatelessWidget{ + final List data; + final String title; + final String yAxisTitle; + final bool bigScreen; + const _HistoryChartThigy({super.key, required this.data, required this.title, required this.yAxisTitle, required this.bigScreen}); + + @override + Widget build(BuildContext context) { + return AspectRatio( + aspectRatio: bigScreen ? 1.9 : 1.1, + child: Stack( + children: [ + Row(mainAxisAlignment: MainAxisAlignment.center, children: [Text(title, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28))]), + Padding( padding: bigScreen ? const EdgeInsets.fromLTRB(40, 80, 40, 48) : const EdgeInsets.fromLTRB(0, 80, 0, 48) , + child: LineChart( + LineChartData( + lineBarsData: [LineChartBarData(spots: data)], + borderData: FlBorderData(show: false), + titlesData: FlTitlesData(topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)), + rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)), + bottomTitles: AxisTitles(sideTitles: SideTitles(showTitles: true, reservedSize: 30, getTitlesWidget: (double value, TitleMeta meta){ + return SideTitleWidget( + axisSide: meta.axisSide, + angle: 0.3, + child: Text(DateFormat(DateFormat.YEAR_ABBR_MONTH_DAY).format(DateTime.fromMillisecondsSinceEpoch(value.floor()))), + ); + })), + leftTitles: AxisTitles(sideTitles: SideTitles(showTitles: true, reservedSize: 80, getTitlesWidget: (double value, TitleMeta meta){ + return SideTitleWidget( + axisSide: meta.axisSide, + child: Text(f2.format(value)), + ); + }))), + lineTouchData: LineTouchData(touchTooltipData: LineTouchTooltipData(getTooltipItems: (touchedSpots) { + return [for (var v in touchedSpots) LineTooltipItem("${f2.format(v.y)} $yAxisTitle \n", TextStyle(), children: [TextSpan(text: "${dateFormat.format(DateTime.fromMillisecondsSinceEpoch(v.x.floor()))}")])]; + },)) + ) + ), + ), + ], + ) + ); + } +} + class _RecordThingy extends StatelessWidget { final RecordSingle? record; const _RecordThingy({Key? key, required this.record}) : super(key: key); @@ -736,10 +813,7 @@ class _RecordThingy extends StatelessWidget { ), ] : [ - Text("No record", - style: TextStyle( - fontFamily: "Eurostile Round Extended", - fontSize: bigScreen ? 42 : 28)) + const Text("No record", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)) ], ); }); diff --git a/lib/widgets/tl_thingy.dart b/lib/widgets/tl_thingy.dart index 6d2dd9b..907151a 100644 --- a/lib/widgets/tl_thingy.dart +++ b/lib/widgets/tl_thingy.dart @@ -307,7 +307,7 @@ class TLThingy extends StatelessWidget { ) ] : [ - Text("That user never played Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + const Text("That user never played Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)), ], ); }, diff --git a/pubspec.yaml b/pubspec.yaml index f950e60..2514616 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ publish_to: 'none' # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 0.1.0+3 +version: 0.2.0+4 environment: sdk: '>=2.19.6 <3.0.0'