From f95ffb59aa3118ef13daf36d3a8254f9e5cc650f Mon Sep 17 00:00:00 2001 From: dan63047 Date: Thu, 7 Mar 2024 01:34:15 +0300 Subject: [PATCH] Experimental new feature --- lib/data_objects/tetrio.dart | 94 +++++++ lib/services/tetrio_crud.dart | 10 + lib/utils/numers_formats.dart | 7 + lib/views/calc_view.dart | 1 - lib/views/main_view.dart | 129 ++++----- lib/views/mathes_view.dart | 1 - lib/views/ranks_averages_view.dart | 3 +- lib/views/settings_view.dart | 18 +- lib/views/tl_leaderboard_view.dart | 2 +- lib/widgets/gauget_num.dart | 98 +++++++ lib/widgets/list_tile_trailing_stats.dart | 4 +- lib/widgets/stat_sell_num.dart | 25 +- lib/widgets/tl_thingy.dart | 302 ++++++++++------------ 13 files changed, 439 insertions(+), 255 deletions(-) create mode 100644 lib/utils/numers_formats.dart create mode 100644 lib/widgets/gauget_num.dart diff --git a/lib/data_objects/tetrio.dart b/lib/data_objects/tetrio.dart index cc7607e..c9fa2f3 100644 --- a/lib/data_objects/tetrio.dart +++ b/lib/data_objects/tetrio.dart @@ -1139,6 +1139,73 @@ class News { } } +class PlayerLeaderboardPosition{ + late LeaderboardPosition apm; + late LeaderboardPosition pps; + late LeaderboardPosition vs; + late LeaderboardPosition gamesPlayed; + late LeaderboardPosition gamesWon; + late LeaderboardPosition winrate; + late LeaderboardPosition app; + late LeaderboardPosition vsapm; + late LeaderboardPosition dss; + late LeaderboardPosition dsp; + late LeaderboardPosition appdsp; + late LeaderboardPosition cheese; + late LeaderboardPosition gbe; + late LeaderboardPosition nyaapp; + late LeaderboardPosition area; + late LeaderboardPosition estTr; + late LeaderboardPosition accOfEst; + + PlayerLeaderboardPosition({ + required this.apm, + required this.pps, + required this.vs, + required this.gamesPlayed, + required this.gamesWon, + required this.winrate, + required this.app, + required this.vsapm, + required this.dss, + required this.dsp, + required this.appdsp, + required this.cheese, + required this.gbe, + required this.nyaapp, + required this.area, + required this.estTr, + required this.accOfEst + }); + + PlayerLeaderboardPosition.fromSearchResults(List results){ + apm = results[0]; + pps = results[1]; + vs = results[2]; + gamesPlayed = results[3]; + gamesWon = results[4]; + winrate = results[5]; + app = results[6]; + vsapm = results[7]; + dss = results[8]; + dsp = results[9]; + appdsp = results[10]; + cheese = results[11]; + gbe = results[12]; + nyaapp = results[13]; + area = results[14]; + estTr = results[15]; + accOfEst = results[16]; + } +} + +class LeaderboardPosition{ + int position; + double percentage; + + LeaderboardPosition(this.position, this.percentage); +} + class TetrioPlayersLeaderboard { late String type; late DateTime timestamp; @@ -1163,6 +1230,20 @@ class TetrioPlayersLeaderboard { return lb; } + List getStatRankingSequel(Stats stat){ + List lb = List.from(leaderboard); + lb.sort(((a, b) { + if (a.getStatByEnum(stat) > b.getStatByEnum(stat)){ + return -1; + }else if (a.getStatByEnum(stat) == b.getStatByEnum(stat)){ + return 0; + }else{ + return 1; + } + })); + return lb; + } + List getAverageOfRank(String rank){ // i tried to refactor it and that's was terrible if (rank.isNotEmpty && !rankCutoffs.keys.contains(rank)) throw Exception("Invalid rank"); List filtredLeaderboard = List.from(leaderboard); @@ -1753,6 +1834,19 @@ class TetrioPlayersLeaderboard { } } + PlayerLeaderboardPosition? getLeaderboardPosition(String userID) { + if (leaderboard.indexWhere((element) => element.userId == userID) == -1) return null; + List stats = [Stats.apm, Stats.pps, Stats.vs, Stats.gp, Stats.gw, Stats.wr, + Stats.app, Stats.vsapm, Stats.dss, Stats.dsp, Stats.appdsp, Stats.cheese, Stats.gbe, Stats.nyaapp, Stats.area, Stats.eTR, Stats.acceTR]; + List results = []; + for (Stats stat in stats) { + List sortedLeaderboard = getStatRanking(leaderboard, stat, reversed: false); + int position = sortedLeaderboard.indexWhere((element) => element.userId == userID) + 1; + results.add(LeaderboardPosition(position, position / sortedLeaderboard.length)); + } + return PlayerLeaderboardPosition.fromSearchResults(results); + } + Map> get averages => { 'x': getAverageOfRank("x"), 'u': getAverageOfRank("u"), diff --git a/lib/services/tetrio_crud.dart b/lib/services/tetrio_crud.dart index 073a7fa..a99c8c7 100644 --- a/lib/services/tetrio_crud.dart +++ b/lib/services/tetrio_crud.dart @@ -74,6 +74,7 @@ class TetrioService extends DB { final Map> _recordsCache = {}; final Map _replaysCache = {}; // the only one is different: {"replayID": [replayString, replayBytes]} final Map _leaderboardsCache = {}; + final Map _lbPositions = {}; final Map> _newsCache = {}; final Map> _topTRcache = {}; final Map _tlStreamsCache = {}; @@ -142,6 +143,14 @@ class TetrioService extends DB { db.insert(tetrioTLReplayStatsTable, {idCol: replay.id, "data": jsonEncode(replay.toJson())}); } + void cacheLeaderboardPositions(String userID, PlayerLeaderboardPosition positions){ + _lbPositions[userID] = positions; + } + + PlayerLeaderboardPosition? getCachedLeaderboardPositions(String userID){ + return _lbPositions[userID]; + } + /// Downloads replay from inoue (szy API). Requiers [replayID]. If request have /// different from 200 statusCode, it will throw an excepction. Returns list, that contains same replay /// as string and as binary. @@ -504,6 +513,7 @@ class TetrioService extends DB { switch (response.statusCode) { case 200: + _lbPositions.clear(); var rawJson = jsonDecode(response.body); if (rawJson['success']) { // if api confirmed that everything ok TetrioPlayersLeaderboard leaderboard = TetrioPlayersLeaderboard.fromJson(rawJson['data']['users'], "league", DateTime.fromMillisecondsSinceEpoch(rawJson['cache']['cached_at'])); diff --git a/lib/utils/numers_formats.dart b/lib/utils/numers_formats.dart new file mode 100644 index 0000000..52e2c2a --- /dev/null +++ b/lib/utils/numers_formats.dart @@ -0,0 +1,7 @@ +import 'package:intl/intl.dart'; +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 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/calc_view.dart b/lib/views/calc_view.dart index d942746..5c9334e 100644 --- a/lib/views/calc_view.dart +++ b/lib/views/calc_view.dart @@ -13,7 +13,6 @@ double? vs; NerdStats? nerdStats; EstTr? estTr; Playstyle? playstyle; -final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2); late String oldWindowTitle; class CalcView extends StatefulWidget { diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 3c630e8..740a8a7 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -31,6 +31,7 @@ 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; String _searchFor = "6098518e3d5155e6ec429cdc"; // who we looking for String _titleNickname = "dan63047"; final TetrioService teto = TetrioService(); // thing, that manadge our local DB @@ -168,8 +169,14 @@ class _MainState extends State with TickerProviderStateMixin { news = requests[2] as List; topTR = requests.elementAtOrNull(3) as double?; // No TR - no Top TR - // Get tetra League leaderboard if needed - // if(prefs.getBool("loadLeaderboard") == true) everyone = await teto.fetchTLLeaderboard(); + meAmongEveryone = teto.getCachedLeaderboardPositions(me.userId); + if (meAmongEveryone == null && prefs.getBool("showPositions") == true){ + // Get tetra League leaderboard + everyone = teto.getCachedLeaderboard(); + everyone ??= await teto.fetchTLLeaderboard(); + meAmongEveryone = await compute(everyone!.getLeaderboardPosition, me.userId); + if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!); + } // Making list of Tetra League matches List tlMatches = []; @@ -394,7 +401,15 @@ class _MainState extends State with TickerProviderStateMixin { body: TabBarView( controller: _tabController, children: [ - TLThingy(tl: snapshot.data![0].tlSeason1, userID: snapshot.data![0].userId, states: snapshot.data![2], topTR: snapshot.data![7], bot: snapshot.data![0].role == "bot", guest: snapshot.data![0].role == "anon"), + TLThingy( + tl: snapshot.data![0].tlSeason1, + userID: snapshot.data![0].userId, + states: snapshot.data![2], + topTR: snapshot.data![7], + bot: snapshot.data![0].role == "bot", + guest: snapshot.data![0].role == "anon", + lbPositions: meAmongEveryone + ), _TLRecords(userID: snapshot.data![0].userId, data: snapshot.data![3]), _History(states: snapshot.data![2], update: _justUpdate), _RecordThingy(record: snapshot.data![1]['sprint'], rank: snapshot.data![0].tlSeason1.percentileRank), @@ -958,71 +973,58 @@ class _RecordThingy extends StatelessWidget { else if (record!.stream.contains("blitz")) Text(t.blitz, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), // show main metric - Wrap( - direction: Axis.horizontal, - alignment: WrapAlignment.spaceAround, - crossAxisAlignment: WrapCrossAlignment.center, - clipBehavior: Clip.hardEdge, + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + textBaseline: TextBaseline.alphabetic, children: [ // Show grade based on closest rank average - if (record!.stream.contains("40l")) Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageSprint.key}.png", height: 96) - else if (record!.stream.contains("blitz")) Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageBlitz.key}.png", height: 96), + if (record!.stream.contains("40l")) Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageSprint.key}.png", height: 48) + else if (record!.stream.contains("blitz")) Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageBlitz.key}.png", height: 48), - // TODO: I'm not sure abour that element. Maybe, it could be done differenly - Column( - children: [ - // Show result - if (record!.stream.contains("40l")) Text(get40lTime(record!.endContext!.finalTime.inMicroseconds), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)) - else if (record!.stream.contains("blitz")) Text(NumberFormat.decimalPattern().format(record!.endContext!.score), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), - - // Show difference between rank average - if (record!.stream.contains("40l") && (rank != null && rank != "z")) Text( - "${readableTimeDifference(record!.endContext!.finalTime, sprintAverages[rank]!)} ${sprintBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average", - textAlign: TextAlign.center, - style: TextStyle( - color: sprintBetterThanRankAverage??false ? - Colors.greenAccent : - Colors.redAccent - ) - ) - else if (record!.stream.contains("40l") && (rank == null || rank == "z")) Text( - "${readableTimeDifference(record!.endContext!.finalTime, closestAverageSprint.value)} ${sprintBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageSprint.key!.toUpperCase()} rank average", - textAlign: TextAlign.center, - style: TextStyle( - color: sprintBetterThanClosestAverage ? - Colors.greenAccent : - Colors.redAccent - ) - ) - else if (record!.stream.contains("blitz") && (rank != null && rank != "z")) Text( - "${readableIntDifference(record!.endContext!.score, blitzAverages[rank]!)} ${blitzBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average", - textAlign: TextAlign.center, - style: TextStyle( - color: blitzBetterThanRankAverage??false ? - Colors.greenAccent : - Colors.redAccent - ) - ) - else if (record!.stream.contains("blitz") && (rank == null || rank == "z")) Text( - "${readableIntDifference(record!.endContext!.score, closestAverageBlitz.value)} ${blitzBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageBlitz.key!.toUpperCase()} rank average", - textAlign: TextAlign.center, - style: TextStyle( - color: blitzBetterThanClosestAverage ? - Colors.greenAccent : - Colors.redAccent - ) - ), - ], - ), + // Show result + if (record!.stream.contains("40l")) Text(get40lTime(record!.endContext!.finalTime.inMicroseconds), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)) + else if (record!.stream.contains("blitz")) Text(NumberFormat.decimalPattern().format(record!.endContext!.score), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), ], ), - // if (record!.stream.contains("40l")) Text(get40lTime(record!.endContext!.finalTime.inMicroseconds), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)) - // else if (record!.stream.contains("blitz")) Text(NumberFormat.decimalPattern().format(record!.endContext!.score), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), - // // Compare with averages - // if (record!.stream.contains("40l") && rank != null) RichText(text: TextSpan(text: "${readableTimeDifference(record!.endContext!.finalTime, sprintAverages[rank]!)} ${sprintBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average", style: TextStyle(fontFamily: "Eurostile Round", color: sprintBetterThanRankAverage??false ? Colors.green : Colors.red))) - // //Text("${record!.endContext!.finalTime - sprintAverages[rank]!}; ${sprintAverages[rank]}; ${get40lTime((record!.endContext!.finalTime - sprintAverages[rank]!).inMicroseconds)}") - // else if (record!.stream.contains("blitz")) Text("${closestAverageBlitz}; ${blitzAverages[rank]}"), + // Show difference between rank average + if (record!.stream.contains("40l") && (rank != null && rank != "z")) Text( + "${readableTimeDifference(record!.endContext!.finalTime, sprintAverages[rank]!)} ${sprintBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average", + textAlign: TextAlign.center, + style: TextStyle( + color: sprintBetterThanRankAverage??false ? + Colors.greenAccent : + Colors.redAccent + ) + ) + else if (record!.stream.contains("40l") && (rank == null || rank == "z")) Text( + "${readableTimeDifference(record!.endContext!.finalTime, closestAverageSprint.value)} ${sprintBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageSprint.key!.toUpperCase()} rank average", + textAlign: TextAlign.center, + style: TextStyle( + color: sprintBetterThanClosestAverage ? + Colors.greenAccent : + Colors.redAccent + ) + ) + else if (record!.stream.contains("blitz") && (rank != null && rank != "z")) Text( + "${readableIntDifference(record!.endContext!.score, blitzAverages[rank]!)} ${blitzBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average", + textAlign: TextAlign.center, + style: TextStyle( + color: blitzBetterThanRankAverage??false ? + Colors.greenAccent : + Colors.redAccent + ) + ) + else if (record!.stream.contains("blitz") && (rank == null || rank == "z")) Text( + "${readableIntDifference(record!.endContext!.score, closestAverageBlitz.value)} ${blitzBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageBlitz.key!.toUpperCase()} rank average", + textAlign: TextAlign.center, + style: TextStyle( + color: blitzBetterThanClosestAverage ? + Colors.greenAccent : + Colors.redAccent + ) + ), // Show rank if presented if (record!.rank != null) StatCellNum(playerStat: record!.rank!, playerStatLabel: "Leaderboard Placement", isScreenBig: bigScreen, higherIsBetter: false), @@ -1054,7 +1056,8 @@ class _RecordThingy extends StatelessWidget { // List of actions Padding(padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), - child: SizedBox(width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85, + child: Container(width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85, + constraints: BoxConstraints(maxWidth: 452), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( diff --git a/lib/views/mathes_view.dart b/lib/views/mathes_view.dart index 7656d40..0a1c3f5 100644 --- a/lib/views/mathes_view.dart +++ b/lib/views/mathes_view.dart @@ -8,7 +8,6 @@ import 'package:tetra_stats/views/tl_match_view.dart'; import 'package:window_manager/window_manager.dart'; final TetrioService teto = TetrioService(); -final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2); late String oldWindowTitle; class MatchesView extends StatefulWidget { diff --git a/lib/views/ranks_averages_view.dart b/lib/views/ranks_averages_view.dart index d928728..02f4e56 100644 --- a/lib/views/ranks_averages_view.dart +++ b/lib/views/ranks_averages_view.dart @@ -1,8 +1,8 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; import 'package:tetra_stats/gen/strings.g.dart'; +import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/views/rank_averages_view.dart'; import 'package:window_manager/window_manager.dart'; import 'main_view.dart'; // lol @@ -40,7 +40,6 @@ class RanksAverages extends State { @override Widget build(BuildContext context) { - final NumberFormat f2 = NumberFormat.decimalPattern(LocaleSettings.currentLocale.languageCode)..maximumFractionDigits = 2; return Scaffold( appBar: AppBar( title: Text(t.rankAveragesViewTitle), diff --git a/lib/views/settings_view.dart b/lib/views/settings_view.dart index b4d88ac..97e3c70 100644 --- a/lib/views/settings_view.dart +++ b/lib/views/settings_view.dart @@ -26,7 +26,7 @@ class SettingsState extends State { late SharedPreferences prefs; final TetrioService teto = TetrioService(); String defaultNickname = "Checking..."; - late bool loadLeaderboard; + late bool showPositions; final TextEditingController _playertext = TextEditingController(); @override @@ -47,10 +47,10 @@ class SettingsState extends State { Future _getPreferences() async { prefs = await SharedPreferences.getInstance(); - if (prefs.getBool("loadLeaderboard") != null) { - loadLeaderboard = prefs.getBool("loadLeaderboard")!; + if (prefs.getBool("showPositions") != null) { + showPositions = prefs.getBool("showPositions")!; } else { - loadLeaderboard = false; + showPositions = false; } _setDefaultNickname(prefs.getString("player")); } @@ -266,12 +266,12 @@ class SettingsState extends State { onTap: () { Navigator.pushNamed(context, "/customization"); },), - ListTile(title: Text("Load leaderboard on startup"), - subtitle: Text("That will allow app to show additional stats, like..."), - trailing: Switch(value: loadLeaderboard, onChanged: (bool value){ - prefs.setBool("loadLeaderboard", value); + ListTile(title: Text("Show LB position for each stat"), + subtitle: Text("That will impact on app performance..."), + trailing: Switch(value: showPositions, onChanged: (bool value){ + prefs.setBool("showPositions", value); setState(() { - loadLeaderboard = value; + showPositions = value; }); }),), const Divider(), diff --git a/lib/views/tl_leaderboard_view.dart b/lib/views/tl_leaderboard_view.dart index 1f21484..7eb9f29 100644 --- a/lib/views/tl_leaderboard_view.dart +++ b/lib/views/tl_leaderboard_view.dart @@ -71,7 +71,7 @@ class TLLeaderboardState extends State { case ConnectionState.none: case ConnectionState.waiting: case ConnectionState.active: - return const Center(child: Text('Fetching...')); + return const Center(child: CircularProgressIndicator()); case ConnectionState.done: final allPlayers = snapshot.data?.getStatRanking(snapshot.data!.leaderboard, _sortBy, reversed: reversed, country: _country); if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) windowManager.setTitle("Tetra Stats: ${t.tlLeaderboard} - ${t.players(n: allPlayers!.length)}"); diff --git a/lib/widgets/gauget_num.dart b/lib/widgets/gauget_num.dart new file mode 100644 index 0000000..17295ed --- /dev/null +++ b/lib/widgets/gauget_num.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; +import 'package:syncfusion_flutter_gauges/gauges.dart'; +import 'package:tetra_stats/data_objects/tetrio.dart'; +import 'package:tetra_stats/gen/strings.g.dart'; +import 'package:tetra_stats/utils/numers_formats.dart'; +import 'package:tetra_stats/widgets/tl_thingy.dart'; + +class GaugetNum extends StatelessWidget { + final num playerStat; + final num? oldPlayerStat; + final bool higherIsBetter; + final List ranges; + final double minimum; + final double maximum; + final String playerStatLabel; + final String? okText; + final String? alertTitle; + final List? alertWidgets; + final LeaderboardPosition? pos; + + const GaugetNum( + {super.key, + required this.playerStat, + required this.playerStatLabel, + this.alertWidgets, + this.oldPlayerStat, + required this.higherIsBetter, + required this.minimum, + required this.maximum, + required this.ranges, + this.okText, this.alertTitle, this.pos}); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 200, + height: 120, + child: SfRadialGauge( + title: GaugeTitle(text: playerStatLabel), + axes: [RadialAxis( + startAngle: 180, + endAngle: 360, + showLabels: false, + showTicks: false, + radiusFactor: 2.1, + centerY: 0.5, + minimum: minimum, + maximum: maximum, + ranges: ranges, + pointers: [ + NeedlePointer( + value: playerStat as double, + enableAnimation: true, + needleLength: 0.9, + needleStartWidth: 2, + needleEndWidth: 15, + knobStyle: const KnobStyle(color: Colors.transparent), + gradient: const LinearGradient(colors: [Colors.transparent, Colors.white], begin: Alignment.bottomCenter, end: Alignment.topCenter, stops: [0.5, 1]),) + ], + annotations: [GaugeAnnotation( + widget: TextButton(child: Text(f3.format(playerStat), + style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, color: Colors.white)), + onPressed: (){ + showDialog( + context: context, + builder: (BuildContext context) => AlertDialog( + title: Text(alertTitle??playerStatLabel, style: const TextStyle(fontFamily: "Eurostile Round Extended")), + content: SingleChildScrollView(child: ListBody(children: alertWidgets!)), + actions: [ + TextButton( + child: Text(okText??t.popupActions.ok), + onPressed: () { + Navigator.of(context).pop(); + }, + ) + ], + )); + },), verticalAlignment: GaugeAlignment.far, positionFactor: 0.05), + if (oldPlayerStat != null || pos != null) GaugeAnnotation( + widget: RichText(text: TextSpan( + text: "", + style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey), + children: [ + if (oldPlayerStat != null) TextSpan(text: comparef.format(playerStat - oldPlayerStat!), style: TextStyle( + color: higherIsBetter ? + oldPlayerStat! > playerStat ? Colors.redAccent : Colors.greenAccent : + oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent + ),), + if ((oldTl != null && oldTl!.gamesPlayed > 0) && pos != null) const TextSpan(text: " • "), + if (pos != null) TextSpan(text: pos!.position >= 1000 ? "Top ${f2.format(pos!.percentage*100)}%" : "№${pos!.position}") + ] + ), + ), + positionFactor: 0.05)], + )],), + ); + } +} \ No newline at end of file diff --git a/lib/widgets/list_tile_trailing_stats.dart b/lib/widgets/list_tile_trailing_stats.dart index c5b223b..5690929 100644 --- a/lib/widgets/list_tile_trailing_stats.dart +++ b/lib/widgets/list_tile_trailing_stats.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'package:tetra_stats/gen/strings.g.dart'; +import 'package:tetra_stats/utils/numers_formats.dart'; class TrailingStats extends StatelessWidget{ final double yourAPM; @@ -14,7 +13,6 @@ class TrailingStats extends StatelessWidget{ @override Widget build(BuildContext context) { - final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2); const TextStyle style = TextStyle(height: 1.1, fontWeight: FontWeight.w100); return Table( defaultColumnWidth: const IntrinsicColumnWidth(), diff --git a/lib/widgets/stat_sell_num.dart b/lib/widgets/stat_sell_num.dart index 6a11a89..09bead9 100644 --- a/lib/widgets/stat_sell_num.dart +++ b/lib/widgets/stat_sell_num.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/gen/strings.g.dart'; +import 'package:tetra_stats/utils/numers_formats.dart'; class StatCellNum extends StatelessWidget { const StatCellNum( @@ -12,7 +14,7 @@ class StatCellNum extends StatelessWidget { this.fractionDigits, this.oldPlayerStat, required this.higherIsBetter, - this.okText, this.alertTitle}); + this.okText, this.alertTitle, this.pos}); final num playerStat; final num? oldPlayerStat; @@ -23,11 +25,11 @@ class StatCellNum extends StatelessWidget { final String? alertTitle; final List? alertWidgets; final int? fractionDigits; + final LeaderboardPosition? pos; @override Widget build(BuildContext context) { NumberFormat comparef = NumberFormat("+#,###.###;-#,###.###")..maximumFractionDigits = fractionDigits ?? 0; - NumberFormat intf = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0); NumberFormat fractionf = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: fractionDigits ?? 0)..maximumIntegerDigits = 0; num fraction = playerStat.isNegative ? 1 - (playerStat - playerStat.floor()) : playerStat - playerStat.floor(); int integer = playerStat.isNegative ? (playerStat + fraction).toInt() : (playerStat - fraction).toInt(); @@ -48,11 +50,20 @@ class StatCellNum extends StatelessWidget { ) ) ), - if (oldPlayerStat != null) Text(comparef.format(playerStat - oldPlayerStat!), style: TextStyle( - color: higherIsBetter ? - oldPlayerStat! > playerStat ? Colors.redAccent : Colors.greenAccent : - oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent - ),), + if (oldPlayerStat != null || pos != null) RichText(text: TextSpan( + text: "", + style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey), + children: [ + if (oldPlayerStat != null) TextSpan(text: comparef.format(playerStat - oldPlayerStat!), style: TextStyle( + color: higherIsBetter ? + oldPlayerStat! > playerStat ? Colors.redAccent : Colors.greenAccent : + oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent + ),), + if (oldPlayerStat != null && pos != null) const TextSpan(text: " • "), + if (pos != null) TextSpan(text: pos!.position >= 1000 ? "Top ${f2.format(pos!.percentage*100)}%" : "№${pos!.position}") + ] + ), + ), alertWidgets == null ? Text( playerStatLabel, diff --git a/lib/widgets/tl_thingy.dart b/lib/widgets/tl_thingy.dart index 175a93a..dc59322 100644 --- a/lib/widgets/tl_thingy.dart +++ b/lib/widgets/tl_thingy.dart @@ -3,13 +3,14 @@ import 'package:intl/intl.dart'; import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:syncfusion_flutter_gauges/gauges.dart'; import 'package:tetra_stats/gen/strings.g.dart'; +import 'package:tetra_stats/utils/numers_formats.dart'; +import 'package:tetra_stats/widgets/gauget_num.dart'; import 'package:tetra_stats/widgets/graphs.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart'; var fDiff = NumberFormat("+#,###.###;-#,###.###"); +var intFDiff = NumberFormat("+#,###;-#,###"); final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms(); -final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2); -final NumberFormat f3 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3); late RangeValues _currentRangeValues; TetraLeagueAlpha? oldTl; late TetraLeagueAlpha currentTl; @@ -23,7 +24,8 @@ class TLThingy extends StatefulWidget { final bool bot; final bool guest; final double? topTR; - const TLThingy({super.key, required this.tl, required this.userID, required this.states, this.showTitle = true, this.bot=false, this.guest=false, this.topTR}); + final PlayerLeaderboardPosition? lbPositions; + const TLThingy({super.key, required this.tl, required this.userID, required this.states, this.showTitle = true, this.bot=false, this.guest=false, this.topTR, this.lbPositions}); @override State createState() => _TLThingyState(); @@ -47,6 +49,8 @@ class _TLThingyState extends State { @override Widget build(BuildContext context) { final t = Translations.of(context); + NumberFormat fractionfEstTR = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2)..maximumIntegerDigits = 0; + NumberFormat fractionfEstTRAcc = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3)..maximumIntegerDigits = 0; if (currentTl.gamesPlayed == 0) return Center(child: Text(widget.guest ? t.anonTL : widget.bot ? t.botTL : t.neverPlayedTL, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28), textAlign: TextAlign.center,)); return LayoutBuilder(builder: (context, constraints) { bool bigScreen = constraints.maxWidth > 768; @@ -159,13 +163,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), - if (currentTl.pps != null) StatCellNum(playerStat: currentTl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.pps, higherIsBetter: true, oldPlayerStat: oldTl?.pps), - if (currentTl.vs != null) StatCellNum(playerStat: currentTl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.vs, higherIsBetter: true, oldPlayerStat: oldTl?.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), + 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), + 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), 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), - StatCellNum(playerStat: currentTl.gamesWon, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.gamesWonTL, higherIsBetter: true, oldPlayerStat: oldTl?.gamesWon), - StatCellNum(playerStat: currentTl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.winrate, higherIsBetter: true, oldPlayerStat: oldTl != null ? oldTl!.winrate*100 : null), + 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), ], ), ), @@ -176,138 +180,31 @@ class _TLThingyState extends State { Padding( padding: const EdgeInsets.fromLTRB(0, 40, 0, 0), child: Wrap( - direction: Axis.horizontal, - alignment: WrapAlignment.center, - spacing: 35, - crossAxisAlignment: WrapCrossAlignment.start, - clipBehavior: Clip.hardEdge, - children: [ - SizedBox( - width: 200, - height: 120, - child: SfRadialGauge( - title: GaugeTitle(text: t.statCellNum.app), - axes: [RadialAxis( - startAngle: 180, - endAngle: 360, - showLabels: false, - showTicks: false, - radiusFactor: 2.1, - centerY: 0.5, - minimum: 0, - maximum: 1, - ranges: [ - GaugeRange(startValue: 0, endValue: 0.2, color: Colors.red), - GaugeRange(startValue: 0.2, endValue: 0.4, color: Colors.yellow), - GaugeRange(startValue: 0.4, endValue: 0.6, color: Colors.green), - GaugeRange(startValue: 0.6, endValue: 0.8, color: Colors.blue), - GaugeRange(startValue: 0.8, endValue: 1, color: Colors.purple), - ], - pointers: [ - NeedlePointer( - value: currentTl.nerdStats!.app, - enableAnimation: true, - needleLength: 0.9, - needleStartWidth: 2, - needleEndWidth: 15, - knobStyle: const KnobStyle(color: Colors.transparent), - gradient: const LinearGradient(colors: [Colors.transparent, Colors.white], begin: Alignment.bottomCenter, end: Alignment.topCenter, stops: [0.5, 1]),) - ], - annotations: [GaugeAnnotation( - widget: TextButton(child: Text(f3.format(currentTl.nerdStats!.app), - style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, color: Colors.white)), - onPressed: (){ - showDialog( - context: context, - builder: (BuildContext context) => AlertDialog( - title: Text(t.statCellNum.app, - style: const TextStyle( - fontFamily: "Eurostile Round Extended")), - content: SingleChildScrollView( - child: ListBody(children: [ - Text(t.statCellNum.appDescription), - Text("${t.exactValue}: ${currentTl.nerdStats!.app}") - ]), - ), - actions: [ - TextButton( - child: Text(t.popupActions.ok), - onPressed: () { - Navigator.of(context).pop(); - }, - ) - ], - )); - },), verticalAlignment: GaugeAlignment.far, positionFactor: 0.05,), - if (oldTl != null && oldTl!.gamesPlayed > 0) GaugeAnnotation(widget: Text(fDiff.format(currentTl.nerdStats!.app - oldTl!.nerdStats!.app), style: TextStyle( - color: currentTl.nerdStats!.app - oldTl!.nerdStats!.app < 0 ? - Colors.redAccent : - Colors.greenAccent - ),), positionFactor: 0.05,)], - )],), - ), - SizedBox( - width: 200, - height: 120, - child: SfRadialGauge( - title: const GaugeTitle(text: "VS / APM"), - axes: [RadialAxis( - startAngle: 180, - endAngle: 360, - showTicks: false, - showLabels: false, - radiusFactor: 2.1, - centerY: 0.5, - 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), - GaugeRange(startValue: 2.2, endValue: 2.4, color: Colors.purple), - ], - pointers: [ - NeedlePointer( - value: currentTl.nerdStats!.vsapm, - enableAnimation: true, - needleLength: 0.9, - needleStartWidth: 2, - needleEndWidth: 15, - knobStyle: const KnobStyle(color: Colors.transparent), - gradient: const LinearGradient(colors: [Colors.transparent, Colors.white], begin: Alignment.bottomCenter, end: Alignment.topCenter, stops: [0.5, 1]),) - ], - annotations: [GaugeAnnotation( - widget: TextButton(child: Text(f3.format(currentTl.nerdStats!.vsapm), - style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, color: Colors.white)), - onPressed: (){ - showDialog( - context: context, - builder: (BuildContext context) => AlertDialog( - title: const Text("VS / APM", - style: TextStyle( - fontFamily: "Eurostile Round Extended")), - content: SingleChildScrollView( - child: ListBody(children: [ - Text(t.statCellNum.vsapmDescription), - Text("${t.exactValue}: ${currentTl.nerdStats!.vsapm}") - ]), - ), - actions: [ - TextButton( - child: Text(t.popupActions.ok), - onPressed: () { - Navigator.of(context).pop(); - }, - ) - ], - )); - },), verticalAlignment: GaugeAlignment.far, positionFactor: 0.05), - if (oldTl != null && oldTl!.gamesPlayed > 0) GaugeAnnotation(widget: Text(fDiff.format(currentTl.nerdStats!.vsapm - oldTl!.nerdStats!.vsapm), style: TextStyle( - color: currentTl.nerdStats!.vsapm - oldTl!.nerdStats!.vsapm < 0 ? - Colors.redAccent : - Colors.greenAccent - ),), positionFactor: 0.05,)], - )],), - ),]), + direction: Axis.horizontal, + alignment: WrapAlignment.center, + spacing: 35, + crossAxisAlignment: WrapCrossAlignment.start, + clipBehavior: Clip.hardEdge, + children: [ + GaugetNum(playerStat: currentTl.nerdStats!.app, playerStatLabel: t.statCellNum.app, higherIsBetter: true, minimum: 0, maximum: 1, ranges: [ + GaugeRange(startValue: 0, endValue: 0.2, color: Colors.red), + GaugeRange(startValue: 0.2, endValue: 0.4, color: Colors.yellow), + GaugeRange(startValue: 0.4, endValue: 0.6, color: Colors.green), + GaugeRange(startValue: 0.6, endValue: 0.8, color: Colors.blue), + GaugeRange(startValue: 0.8, endValue: 1, color: Colors.purple), + ], alertWidgets: [ + Text(t.statCellNum.appDescription), + Text("${t.exactValue}: ${currentTl.nerdStats!.app}") + ], oldPlayerStat: oldTl?.nerdStats?.app, pos: widget.lbPositions?.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), + GaugeRange(startValue: 2.2, endValue: 2.4, color: Colors.purple), + ], alertWidgets: [ + Text(t.statCellNum.vsapmDescription), + Text("${t.exactValue}: ${currentTl.nerdStats!.vsapm}") + ], oldPlayerStat: oldTl?.nerdStats?.vsapm, pos: widget.lbPositions?.vsapm) + ]), ), Wrap( direction: Axis.horizontal, @@ -317,6 +214,7 @@ class _TLThingyState extends State { clipBehavior: Clip.hardEdge, children: [ StatCellNum(playerStat: currentTl.nerdStats!.dss, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dss, + pos: widget.lbPositions?.dss, alertWidgets: [Text(t.statCellNum.dssDescription), Text("${t.formula}: (VS / 100) - (APM / 60)"), Text("${t.exactValue}: ${currentTl.nerdStats!.dss}"),], @@ -324,6 +222,7 @@ class _TLThingyState extends State { higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.dss,), StatCellNum(playerStat: currentTl.nerdStats!.dsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dsp, + pos: widget.lbPositions?.dsp, alertWidgets: [Text(t.statCellNum.dspDescription), Text("${t.formula}: DS/S / PPS"), Text("${t.exactValue}: ${currentTl.nerdStats!.dsp}"),], @@ -331,6 +230,7 @@ class _TLThingyState extends State { higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.dsp,), StatCellNum(playerStat: currentTl.nerdStats!.appdsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.appdsp, + pos: widget.lbPositions?.appdsp, alertWidgets: [Text(t.statCellNum.appdspDescription), Text("${t.formula}: APP + DS/P"), Text("${t.exactValue}: ${currentTl.nerdStats!.appdsp}"),], @@ -338,6 +238,7 @@ class _TLThingyState extends State { higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.appdsp,), StatCellNum(playerStat: currentTl.nerdStats!.cheese, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.cheese, + pos: widget.lbPositions?.cheese, alertWidgets: [Text(t.statCellNum.cheeseDescription), Text("${t.formula}: (DS/P * 150) + ((VS/APM - 2) * 50) + (0.6 - APP) * 125"), Text("${t.exactValue}: ${currentTl.nerdStats!.cheese}"),], @@ -345,6 +246,7 @@ class _TLThingyState extends State { higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.cheese,), StatCellNum(playerStat: currentTl.nerdStats!.gbe, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.gbe, + pos: widget.lbPositions?.gbe, alertWidgets: [Text(t.statCellNum.gbeDescription), Text("${t.formula}: APP * DS/P * 2"), Text("${t.exactValue}: ${currentTl.nerdStats!.gbe}"),], @@ -352,6 +254,7 @@ class _TLThingyState extends State { higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.gbe,), StatCellNum(playerStat: currentTl.nerdStats!.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.nyaapp, + pos: widget.lbPositions?.nyaapp, alertWidgets: [Text(t.statCellNum.nyaappDescription), Text("${t.formula}: APP - 5 * tan(radians((Cheese Index / -30) + 1))"), Text("${t.exactValue}: ${currentTl.nerdStats!.nyaapp}"),], @@ -359,6 +262,7 @@ class _TLThingyState extends State { higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.nyaapp,), StatCellNum(playerStat: currentTl.nerdStats!.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: t.statCellNum.area, + pos: widget.lbPositions?.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}"),], @@ -370,42 +274,104 @@ class _TLThingyState extends State { ), if (currentTl.estTr != null) Padding( - padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), - child: SizedBox( + padding: const EdgeInsets.fromLTRB(0, 48, 0, 48), + child: Container( + //alignment: Alignment.center, width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + constraints: BoxConstraints(maxWidth: 768), + child: Wrap( + alignment: WrapAlignment.spaceBetween, + spacing: 20, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "${bigScreen ? t.statCellNum.estOfTR : t.statCellNum.estOfTRShort}:", - style: const TextStyle(fontSize: 24), + Text(t.statCellNum.estOfTR, style: TextStyle(height: 0.1),), + RichText( + text: TextSpan( + text: intf.format(currentTl.estTr!.esttr.truncate()), + style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500), + children: [TextSpan(text: fractionfEstTR.format(currentTl.estTr!.esttr - currentTl.estTr!.esttr.truncate()).substring(1), style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))] + ), ), - Text( - f2.format(currentTl.estTr!.esttr), - style: const TextStyle(fontSize: 24), + if (oldTl?.estTr?.esttr != null || widget.lbPositions != null) RichText(text: TextSpan( + text: "", + style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey, height: 0.5), + children: [ + if (oldTl?.estTr?.esttr != null) TextSpan(text: comparef.format(currentTl.estTr!.esttr - oldTl!.estTr!.esttr), style: TextStyle( + color: oldTl!.estTr!.esttr > currentTl.estTr!.esttr ? Colors.redAccent : Colors.greenAccent + ),), + if (oldTl?.estTr?.esttr != null && widget.lbPositions?.estTr != null) const TextSpan(text: " • "), + if (widget.lbPositions?.estTr != null) TextSpan(text: widget.lbPositions!.estTr.position >= 1000 ? "Top ${f2.format(widget.lbPositions!.estTr.percentage*100)}%" : "№${widget.lbPositions!.estTr.position}") + ] + ), ), - ], - ), - if (currentTl.rating >= 0) - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + ],), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text(t.statCellNum.accOfEst, style: const TextStyle(height: 0.1),), + RichText( + text: TextSpan( + text: (currentTl.esttracc != null) ? intFDiff.format(currentTl.esttracc!.truncate()) : "-", + style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500), + children: [ + TextSpan(text: (currentTl.esttracc != null) ? fractionfEstTRAcc.format(currentTl.esttracc!.isNegative ? 1 - (currentTl.esttracc! - currentTl.esttracc!.truncate()) : (currentTl.esttracc! - currentTl.esttracc!.truncate())).substring(1) : ".---", style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100)) + ] + ), + ), + if (oldTl?.esttracc != null || widget.lbPositions != null) RichText(text: TextSpan( + text: "", + style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey, height: 0.5), children: [ - Text( - "${bigScreen ? t.statCellNum.accOfEst : t.statCellNum.accOfEstShort}:", - style: const TextStyle(fontSize: 24), - ), - Text( - fDiff.format(currentTl.esttracc!), - style: const TextStyle(fontSize: 24), - ), - ], + if (oldTl?.esttracc != null) TextSpan(text: comparef.format(currentTl.esttracc! - oldTl!.esttracc!), style: TextStyle( + color: oldTl!.esttracc! > currentTl.esttracc! ? Colors.redAccent : Colors.greenAccent + ),), + if (oldTl?.esttracc != null && widget.lbPositions?.accOfEst != null) const TextSpan(text: " • "), + if (widget.lbPositions?.accOfEst != null) TextSpan(text: widget.lbPositions!.accOfEst.position >= 1000 ? "Top ${f2.format(widget.lbPositions!.accOfEst.percentage*100)}%" : "№${widget.lbPositions!.accOfEst.position}") + ] + ), ), + ],) ], ), - ), + ) + // child: Container( + // width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85, + // constraints: BoxConstraints(maxWidth: 452), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // "${bigScreen ? t.statCellNum.estOfTR : t.statCellNum.estOfTRShort}:", + // style: const TextStyle(fontSize: 24), + // ), + // Text( + // f2.format(currentTl.estTr!.esttr), + // style: const TextStyle(fontSize: 24), + // ), + // ], + // ), + // if (currentTl.rating >= 0) + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // "${bigScreen ? t.statCellNum.accOfEst : t.statCellNum.accOfEstShort}:", + // style: const TextStyle(fontSize: 24), + // ), + // Text( + // fDiff.format(currentTl.esttracc!), + // style: const TextStyle(fontSize: 24), + // ), + // ], + // ), + // ], + // ), + // ), ), if (currentTl.nerdStats != null) Graphs(currentTl.apm!, currentTl.pps!, currentTl.vs!, currentTl.nerdStats!, currentTl.playstyle!) ]