diff --git a/lib/data_objects/tetrio.dart b/lib/data_objects/tetrio.dart index c3d22dc..fba0986 100644 --- a/lib/data_objects/tetrio.dart +++ b/lib/data_objects/tetrio.dart @@ -78,6 +78,7 @@ enum Stats { area, eTR, acceTR, + acceTRabs, opener, plonk, infDS, @@ -107,6 +108,7 @@ const Map chartsShortTitles = { Stats.area: "Area", Stats.eTR: "eTR", Stats.acceTR: "±eTR", + Stats.acceTRabs: "+eTR absolute", Stats.opener: "Opener", Stats.plonk: "Plonk", Stats.infDS: "Inf. DS", @@ -383,6 +385,8 @@ class TetrioPlayer { return tlSeason1.estTr?.esttr; case Stats.acceTR: return tlSeason1.esttracc; + case Stats.acceTRabs: + return tlSeason1.esttracc?.abs(); case Stats.opener: return tlSeason1.playstyle?.opener; case Stats.plonk: @@ -2051,6 +2055,8 @@ class TetrioPlayerFromLeaderboard { return estTr.esttr; case Stats.acceTR: return esttracc; + case Stats.acceTRabs: + return esttracc.abs(); case Stats.opener: return playstyle.opener; case Stats.plonk: diff --git a/lib/utils/numers_formats.dart b/lib/utils/numers_formats.dart index 52e2c2a..b437749 100644 --- a/lib/utils/numers_formats.dart +++ b/lib/utils/numers_formats.dart @@ -3,5 +3,6 @@ import 'package:tetra_stats/gen/strings.g.dart'; final NumberFormat comparef = NumberFormat("+#,###.###;-#,###.###")..maximumFractionDigits = 3; final NumberFormat intf = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0); +final NumberFormat f4 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 4); final NumberFormat f3 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3); final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2); \ No newline at end of file diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index ea38a06..8358dcf 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -16,6 +16,7 @@ import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/services/tetrio_crud.dart'; import 'package:tetra_stats/main.dart' show prefs; import 'package:tetra_stats/services/crud_exceptions.dart'; +import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/utils/text_shadow.dart'; import 'package:tetra_stats/views/ranks_averages_view.dart' show RankAveragesView; import 'package:tetra_stats/views/tl_leaderboard_view.dart' show TLLeaderboardView; @@ -29,22 +30,12 @@ import 'package:window_manager/window_manager.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:go_router/go_router.dart'; -Future me = Future.delayed(const Duration(seconds: 60), () => [null, null, null, null, null, null]); // I love lists shut up -TetrioPlayersLeaderboard? everyone; -PlayerLeaderboardPosition? meAmongEveryone; -TetraLeagueAlpha? rankAverages; -String _searchFor = "6098518e3d5155e6ec429cdc"; // who we looking for -String _titleNickname = "dan63047"; final TetrioService teto = TetrioService(); // thing, that manadge our local DB -/// Each dropdown menu item contains list of dots for the graph -var chartsData = >>[]; int _chartsIndex = 0; List _historyShortTitles = ["TR", "Glicko", "RD", "APM", "PPS", "VS", "APP", "DS/S", "DS/P", "APP + DS/P", "VS/APM", "Cheese", "GbE", "wAPP", "Area", "eTR", "±eTR", "Opener", "Plonk", "Inf. DS", "Stride"]; late ScrollController _scrollController; final NumberFormat _timeInSec = NumberFormat("#,###.###s.", LocaleSettings.currentLocale.languageCode); final NumberFormat secs = NumberFormat("00.###", LocaleSettings.currentLocale.languageCode); -final NumberFormat _f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2); -final NumberFormat _f4 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 4); final DateFormat _dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms(); @@ -55,8 +46,6 @@ class MainView extends StatefulWidget { /// if [player] username or id provided, it loads his stats. Also it hides menu drawer and three dots menu. const MainView({super.key, this.player}); - String get title => "Tetra Stats: $_titleNickname"; - @override State createState() => _MainState(); } @@ -85,12 +74,22 @@ String readableIntDifference(int a, int b){ } class _MainState extends State with TickerProviderStateMixin { + Future me = Future.delayed(const Duration(seconds: 60), () => [null, null, null, null, null, null]); // I love lists shut up + TetrioPlayersLeaderboard? everyone; + PlayerLeaderboardPosition? meAmongEveryone; + TetraLeagueAlpha? rankAverages; + String _searchFor = "6098518e3d5155e6ec429cdc"; // who we looking for + String _titleNickname = "dan63047"; + /// Each dropdown menu item contains list of dots for the graph + var chartsData = >>[]; final bodyGlobalKey = GlobalKey(); bool _showSearchBar = false; late TabController _tabController; late TabController _wideScreenTabController; late bool fixedScroll; + String get title => "Tetra Stats: $_titleNickname"; + @override void initState() { initDB(); @@ -153,7 +152,7 @@ class _MainState extends State with TickerProviderStateMixin { // Change view title and window title if avaliable setState((){_titleNickname = me.username;}); - if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) await windowManager.setTitle(widget.title); + if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) await windowManager.setTitle(title); // Requesting Tetra League (alpha), records, news and top TR of player late List requests; @@ -245,9 +244,9 @@ class _MainState extends State with TickerProviderStateMixin { if (uniqueTL.isEmpty) uniqueTL.add(element.tlSeason1); } // Also i need previous Tetra League State for comparison if avaliable - compareWith = uniqueTL.length >= 2 ? uniqueTL.toList().elementAtOrNull(uniqueTL.length - 2) : null; - - chartsData = >>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid + if (uniqueTL.length >= 2){ + compareWith = uniqueTL.toList().elementAtOrNull(uniqueTL.length - 2); + chartsData = >>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.rating)], child: Text(t.statCellNum.tr)), DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.glicko!)], child: const Text("Glicko")), DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.rd!)], child: const Text("Rating Deviation")), @@ -270,6 +269,10 @@ class _MainState extends State with TickerProviderStateMixin { DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.playstyle!.infds)], child: const Text("Inf. DS")), DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.playstyle!.stride)], child: const Text("Stride")), ]; + }else{ + compareWith = null; + chartsData = []; + } return [me, records, states, tlMatches, compareWith, isTracking, news, topTR]; } @@ -286,7 +289,7 @@ class _MainState extends State with TickerProviderStateMixin { drawer: widget.player == null ? NavDrawer(changePlayer) : null, // Side menu hidden if player provided drawerEdgeDragWidth: MediaQuery.of(context).size.width * 0.2, // 20% of left side of the screen used of Drawer gesture appBar: AppBar( - title: _showSearchBar ? SearchBox(onSubmit: changePlayer, bigScreen: MediaQuery.of(context).size.width > 768) : Text(widget.title, style: const TextStyle(shadows: textShadow)), + title: _showSearchBar ? SearchBox(onSubmit: changePlayer, bigScreen: MediaQuery.of(context).size.width > 768) : Text(title, style: const TextStyle(shadows: textShadow)), backgroundColor: Colors.black, actions: widget.player == null ? [ // search bar and PopupMenuButton hidden if player provided TODO: Subject to change _showSearchBar @@ -425,6 +428,7 @@ class _MainState extends State with TickerProviderStateMixin { topTR: snapshot.data![7], bot: snapshot.data![0].role == "bot", guest: snapshot.data![0].role == "anon", + averages: rankAverages, lbPositions: meAmongEveryone ), ), @@ -433,7 +437,7 @@ class _MainState extends State with TickerProviderStateMixin { child: _TLRecords(userID: snapshot.data![0].userId, data: snapshot.data![3]) ), ],), - _History(states: snapshot.data![2], update: _justUpdate), + _History(chartsData: chartsData, update: _justUpdate), Row(children: [ Container( width: MediaQuery.of(context).size.width/2, @@ -455,10 +459,11 @@ class _MainState extends State with TickerProviderStateMixin { topTR: snapshot.data![7], bot: snapshot.data![0].role == "bot", guest: snapshot.data![0].role == "anon", + averages: rankAverages, lbPositions: meAmongEveryone ), _TLRecords(userID: snapshot.data![0].userId, data: snapshot.data![3]), - _History(states: snapshot.data![2], update: _justUpdate), + _History(chartsData: chartsData, update: _justUpdate), _RecordThingy(record: snapshot.data![1]['sprint'], rank: snapshot.data![0].tlSeason1.percentileRank), _RecordThingy(record: snapshot.data![1]['blitz'], rank: snapshot.data![0].tlSeason1.percentileRank), _OtherThingy(zen: snapshot.data![1]['zen'], bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![6],) @@ -680,17 +685,17 @@ class _TLRecords extends StatelessWidget { } class _History extends StatelessWidget{ - final List states; + final List>> chartsData; final Function update; /// Widget, that can show history of some stat of the player on the graph. /// Requires player [states], which is list of states and function [update], which rebuild widgets - const _History({required this.states, required this.update}); + const _History({required this.chartsData, required this.update}); @override Widget build(BuildContext context) { bool bigScreen = MediaQuery.of(context).size.width > 768; - return states.isNotEmpty ? + return chartsData.isNotEmpty ? Column( children: [ DropdownButton( @@ -701,7 +706,7 @@ class _History extends StatelessWidget{ update(); } ), - if(chartsData[_chartsIndex].value!.length > 1) _HistoryChartThigy(data: chartsData[_chartsIndex].value!, yAxisTitle: _historyShortTitles[_chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? _f2 : NumberFormat.compact(),) + if(chartsData[_chartsIndex].value!.length > 1) _HistoryChartThigy(data: chartsData[_chartsIndex].value!, yAxisTitle: _historyShortTitles[_chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(),) else Center(child: Text(t.notEnoughData, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28))) ], ) @@ -955,7 +960,7 @@ class _HistoryChartThigyState extends State<_HistoryChartThigy> { hoveredPointId = -1; // not hovering over any point } else { hoveredPointId = touchResponse!.lineBarSpots!.first.spotIndex; - headerTooltip = "${_f4.format(touchResponse.lineBarSpots!.first.y)} ${widget.yAxisTitle}"; + headerTooltip = "${f4.format(touchResponse.lineBarSpots!.first.y)} ${widget.yAxisTitle}"; footerTooltip = _dateFormat.format(DateTime.fromMillisecondsSinceEpoch(touchResponse.lineBarSpots!.first.x.floor())); } }); diff --git a/lib/widgets/tl_thingy.dart b/lib/widgets/tl_thingy.dart index 0fb9104..ab786ce 100644 --- a/lib/widgets/tl_thingy.dart +++ b/lib/widgets/tl_thingy.dart @@ -169,13 +169,13 @@ class _TLThingyState extends State { crossAxisAlignment: WrapCrossAlignment.start, clipBehavior: Clip.hardEdge, children: [ - if (currentTl.apm != null) StatCellNum(playerStat: currentTl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.apm, higherIsBetter: true, oldPlayerStat: oldTl?.apm, pos: widget.lbPositions?.apm, averageStat: rankAverages?.apm), - if (currentTl.pps != null) StatCellNum(playerStat: currentTl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.pps, higherIsBetter: true, oldPlayerStat: oldTl?.pps, pos: widget.lbPositions?.pps, averageStat: rankAverages?.pps, smallDecimal: false), - if (currentTl.vs != null) StatCellNum(playerStat: currentTl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.vs, higherIsBetter: true, oldPlayerStat: oldTl?.vs, pos: widget.lbPositions?.vs, averageStat: rankAverages?.vs), + if (currentTl.apm != null) StatCellNum(playerStat: currentTl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.apm, higherIsBetter: true, oldPlayerStat: oldTl?.apm, pos: widget.lbPositions?.apm, averageStat: widget.averages?.apm), + if (currentTl.pps != null) StatCellNum(playerStat: currentTl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.pps, higherIsBetter: true, oldPlayerStat: oldTl?.pps, pos: widget.lbPositions?.pps, averageStat: widget.averages?.pps, smallDecimal: false), + if (currentTl.vs != null) StatCellNum(playerStat: currentTl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.vs, higherIsBetter: true, oldPlayerStat: oldTl?.vs, pos: widget.lbPositions?.vs, averageStat: widget.averages?.vs), if (currentTl.standingLocal > 0) StatCellNum(playerStat: currentTl.standingLocal, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.lbpc, higherIsBetter: false, oldPlayerStat: oldTl?.standingLocal), StatCellNum(playerStat: currentTl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.gamesPlayed, higherIsBetter: true, oldPlayerStat: oldTl?.gamesPlayed, pos: widget.lbPositions?.gamesPlayed), StatCellNum(playerStat: currentTl.gamesWon, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.gamesWonTL, higherIsBetter: true, oldPlayerStat: oldTl?.gamesWon, pos: widget.lbPositions?.gamesWon), - StatCellNum(playerStat: currentTl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.winrate, higherIsBetter: true, oldPlayerStat: oldTl != null ? oldTl!.winrate*100 : null, pos: widget.lbPositions?.winrate, averageStat: rankAverages != null ? rankAverages!.winrate * 100 : null), + StatCellNum(playerStat: currentTl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.winrate, higherIsBetter: true, oldPlayerStat: oldTl != null ? oldTl!.winrate*100 : null, pos: widget.lbPositions?.winrate, averageStat: widget.averages != null ? widget.averages!.winrate * 100 : null), ], ), ), @@ -202,7 +202,7 @@ class _TLThingyState extends State { Text(t.statCellNum.appDescription), Text("${t.exactValue}: ${currentTl.nerdStats!.app}") ], oldPlayerStat: oldTl?.nerdStats?.app, pos: widget.lbPositions?.app, - averageStat: rankAverages?.nerdStats?.app), + averageStat: widget.averages?.nerdStats?.app), GaugetNum(playerStat: currentTl.nerdStats!.vsapm, playerStatLabel: "VS / APM", higherIsBetter: true, minimum: 1.8, maximum: 2.4, ranges: [ GaugeRange(startValue: 1.8, endValue: 2.0, color: Colors.green), GaugeRange(startValue: 2.0, endValue: 2.2, color: Colors.blue), @@ -211,7 +211,7 @@ class _TLThingyState extends State { Text(t.statCellNum.vsapmDescription), Text("${t.exactValue}: ${currentTl.nerdStats!.vsapm}") ], oldPlayerStat: oldTl?.nerdStats?.vsapm, pos: widget.lbPositions?.vsapm, - averageStat: rankAverages?.nerdStats?.vsapm) + averageStat: widget.averages?.nerdStats?.vsapm) ]), ), Wrap( @@ -223,7 +223,7 @@ class _TLThingyState extends State { children: [ StatCellNum(playerStat: currentTl.nerdStats!.dss, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dss, pos: widget.lbPositions?.dss, - averageStat: rankAverages?.nerdStats?.dss, smallDecimal: false, + averageStat: widget.averages?.nerdStats?.dss, smallDecimal: false, alertWidgets: [Text(t.statCellNum.dssDescription), Text("${t.formula}: (VS / 100) - (APM / 60)"), Text("${t.exactValue}: ${currentTl.nerdStats!.dss}"),], @@ -232,7 +232,7 @@ class _TLThingyState extends State { oldPlayerStat: oldTl?.nerdStats?.dss,), StatCellNum(playerStat: currentTl.nerdStats!.dsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dsp, pos: widget.lbPositions?.dsp, - averageStat: rankAverages?.nerdStats?.dsp, smallDecimal: false, + averageStat: widget.averages?.nerdStats?.dsp, smallDecimal: false, alertWidgets: [Text(t.statCellNum.dspDescription), Text("${t.formula}: DS/S / PPS"), Text("${t.exactValue}: ${currentTl.nerdStats!.dsp}"),], @@ -241,7 +241,7 @@ class _TLThingyState extends State { oldPlayerStat: oldTl?.nerdStats?.dsp,), StatCellNum(playerStat: currentTl.nerdStats!.appdsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.appdsp, pos: widget.lbPositions?.appdsp, - averageStat: rankAverages?.nerdStats?.appdsp, smallDecimal: false, + averageStat: widget.averages?.nerdStats?.appdsp, smallDecimal: false, alertWidgets: [Text(t.statCellNum.appdspDescription), Text("${t.formula}: APP + DS/P"), Text("${t.exactValue}: ${currentTl.nerdStats!.appdsp}"),], @@ -259,7 +259,7 @@ class _TLThingyState extends State { oldPlayerStat: oldTl?.nerdStats?.cheese,), StatCellNum(playerStat: currentTl.nerdStats!.gbe, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.gbe, pos: widget.lbPositions?.gbe, - averageStat: rankAverages?.nerdStats?.gbe, smallDecimal: false, + averageStat: widget.averages?.nerdStats?.gbe, smallDecimal: false, alertWidgets: [Text(t.statCellNum.gbeDescription), Text("${t.formula}: APP * DS/P * 2"), Text("${t.exactValue}: ${currentTl.nerdStats!.gbe}"),], @@ -268,7 +268,7 @@ class _TLThingyState extends State { oldPlayerStat: oldTl?.nerdStats?.gbe,), StatCellNum(playerStat: currentTl.nerdStats!.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.nyaapp, pos: widget.lbPositions?.nyaapp, - averageStat: rankAverages?.nerdStats?.nyaapp, smallDecimal: false, + averageStat: widget.averages?.nerdStats?.nyaapp, smallDecimal: false, alertWidgets: [Text(t.statCellNum.nyaappDescription), Text("${t.formula}: APP - 5 * tan(radians((Cheese Index / -30) + 1))"), Text("${t.exactValue}: ${currentTl.nerdStats!.nyaapp}"),], @@ -277,7 +277,7 @@ class _TLThingyState extends State { oldPlayerStat: oldTl?.nerdStats?.nyaapp,), StatCellNum(playerStat: currentTl.nerdStats!.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: t.statCellNum.area, pos: widget.lbPositions?.area, - averageStat: rankAverages?.nerdStats?.area, + averageStat: widget.averages?.nerdStats?.area, alertWidgets: [Text(t.statCellNum.areaDescription), Text("${t.formula}: APM * 1 + PPS * 45 + VS * 0.444 + APP * 185 + DS/S * 175 + DS/P * 450 + Garbage Effi * 315"), Text("${t.exactValue}: ${currentTl.nerdStats!.area}"),], diff --git a/lib/widgets/user_thingy.dart b/lib/widgets/user_thingy.dart index 03708a4..2c59eab 100644 --- a/lib/widgets/user_thingy.dart +++ b/lib/widgets/user_thingy.dart @@ -271,7 +271,7 @@ class UserThingy extends StatelessWidget { playerStatLabel: t.statCellNum.hoursPlayed, isScreenBig: bigScreen, alertTitle: t.exactGametime, - alertWidgets: [Text(player.gameTime.toString(), style: const TextStyle(fontFamily: "Eurostile Round Extended"),)], + alertWidgets: [Text(player.gameTime.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 24),)], higherIsBetter: true,), if (player.gamesPlayed >= 0) StatCellNum(