From 67da831cd2277eeec1158a312f53c5b9d8ee6338 Mon Sep 17 00:00:00 2001 From: dan63047 Date: Sun, 18 Aug 2024 02:39:20 +0300 Subject: [PATCH] 1.6.4, many fixes --- lib/data_objects/tetra_stats.dart | 4 +- lib/data_objects/tetrio.dart | 56 +++++++----- lib/services/custom_http_client.dart | 3 + lib/services/tetrio_crud.dart | 44 ++++++---- lib/views/main_view.dart | 63 ++++++------- lib/views/main_view_tiles.dart | 10 +-- lib/views/ranks_averages_view.dart | 127 +++++++++++++++++++++------ lib/widgets/tl_progress_bar.dart | 2 +- lib/widgets/zenith_thingy.dart | 3 +- pubspec.yaml | 2 +- 10 files changed, 208 insertions(+), 106 deletions(-) diff --git a/lib/data_objects/tetra_stats.dart b/lib/data_objects/tetra_stats.dart index e5af9cf..e90c8dd 100644 --- a/lib/data_objects/tetra_stats.dart +++ b/lib/data_objects/tetra_stats.dart @@ -1,10 +1,12 @@ // p1nkl0bst3r data objects class Cutoffs{ + DateTime ts; Map tr; Map glicko; + Map gxe; - Cutoffs(this.tr, this.glicko); + Cutoffs(this.ts, this.tr, this.glicko, this.gxe); } class TopTr{ diff --git a/lib/data_objects/tetrio.dart b/lib/data_objects/tetrio.dart index 1683e1b..79def98 100644 --- a/lib/data_objects/tetrio.dart +++ b/lib/data_objects/tetrio.dart @@ -42,26 +42,26 @@ const Map rankCutoffs = { "z": -1, "": 0.5 }; -const Map rankTargets = { - "x": 24503.75, // where that comes from? - "u": 23038, - "ss": 21583, - "s+": 20128, - "s": 18673, - "s-": 16975, - "a+": 15035, - "a": 13095, - "a-": 11155, - "b+": 9215, - "b": 7275, - "b-": 5335, - "c+": 3880, - "c": 2425, - "c-": 1213, - "d+": 606, - "d": 0, -}; -DateTime seasonStart = DateTime.utc(2024, 08, 16, 18); +// const Map rankTargets = { +// "x": 24503.75, // where that comes from? +// "u": 23038, +// "ss": 21583, +// "s+": 20128, +// "s": 18673, +// "s-": 16975, +// "a+": 15035, +// "a": 13095, +// "a-": 11155, +// "b+": 9215, +// "b": 7275, +// "b-": 5335, +// "c+": 3880, +// "c": 2425, +// "c-": 1213, +// "d+": 606, +// "d": 0, +// }; +// DateTime seasonStart = DateTime.utc(2024, 08, 16, 18); //DateTime seasonEnd = DateTime.utc(2024, 07, 26, 15); enum Stats { tr, @@ -123,7 +123,8 @@ const Map chartsShortTitles = { Stats.openerMinusInfDS: "Opener - Inf. DS" }; -const Map rankColors = { // thanks osk for const rankColors at https://ch.tetr.io/res/js/base.js:418 +const Map rankColors = { // thanks osk for const rankColors at https://ch.tetr.io/res/js/base.js:458 + 'x+': Color(0xFF643C8D), 'x': Color(0xFFFF45FF), 'u': Color(0xFFFF3813), 'ss': Color(0xFFDB8B1F), @@ -1422,10 +1423,10 @@ class TetraLeague { timestamp = ts; gamesPlayed = json['gamesplayed'] ?? 0; gamesWon = json['gameswon'] ?? 0; - tr = json['tr'] != null ? json['tr'].toDouble() : -1; + tr = json['tr'] != null ? json['tr'].toDouble() : json['rating'] != null ? json['rating'].toDouble() : -1; glicko = json['glicko']?.toDouble(); rd = json['rd'] != null ? json['rd']!.toDouble() : noTrRd; - gxe = json['gxe'].toDouble(); + gxe = json['gxe'] != null ? json['gxe'].toDouble() : -1; rank = json['rank'] != null ? json['rank']!.toString() : 'z'; bestRank = json['bestrank'] != null ? json['bestrank']!.toString() : 'z'; apm = json['apm']?.toDouble(); @@ -1721,6 +1722,11 @@ class TetrioPlayersLeaderboard { TetrioPlayersLeaderboard(this.type, this.leaderboard); + @override + String toString(){ + return "$type leaderboard: ${leaderboard.length} players"; + } + List getStatRanking(List leaderboard, Stats stat, {bool reversed = false, String country = ""}){ List lb = List.from(leaderboard); if (country.isNotEmpty){ @@ -2425,6 +2431,10 @@ class TetrioPlayersLeaderboard { leaderboard.add(TetrioPlayerFromLeaderboard.fromJson(entry, ts)); } } + + addPlayers(List list){ + leaderboard.addAll(list); + } } class TetrioPlayerFromLeaderboard { diff --git a/lib/services/custom_http_client.dart b/lib/services/custom_http_client.dart index 004f2aa..e19b56e 100644 --- a/lib/services/custom_http_client.dart +++ b/lib/services/custom_http_client.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:http/http.dart' as http; class UserAgentClient extends http.BaseClient { @@ -9,6 +11,7 @@ class UserAgentClient extends http.BaseClient { @override Future send(http.BaseRequest request) { request.headers['user-agent'] = userAgent; + request.headers['X-Session-ID'] = "${Random().nextInt(1<<32)}"; return _inner.send(request); } } \ No newline at end of file diff --git a/lib/services/tetrio_crud.dart b/lib/services/tetrio_crud.dart index 3a39315..05352ae 100644 --- a/lib/services/tetrio_crud.dart +++ b/lib/services/tetrio_crud.dart @@ -411,12 +411,7 @@ class TetrioService extends DB { Cutoffs? cached = _cache.get("", Cutoffs); if (cached != null) return cached; - Uri url; - if (kIsWeb) { - url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLCutoffs"}); - } else { - url = Uri.https('api.p1nkl0bst3r.xyz', 'rankcutoff', {"users": null}); - } + Uri url = Uri.https('ts.dan63.by', 'beanserver_blaster/cutoffs.json', {"users": null}); try{ final response = await client.get(url); @@ -424,13 +419,14 @@ class TetrioService extends DB { switch (response.statusCode) { case 200: Map rawData = jsonDecode(response.body); - Map data = rawData["cutoffs"] as Map; - Cutoffs result = Cutoffs({}, {}); + Map data = rawData["data"] as Map; + Cutoffs result = Cutoffs(DateTime.fromMillisecondsSinceEpoch(rawData["created"]), {}, {}, {}); for (String rank in data.keys){ - result.tr[rank] = data[rank]["rating"]; + result.tr[rank] = data[rank]["tr"]; result.glicko[rank] = data[rank]["glicko"]; + result.gxe[rank] = data[rank]["gxe"]; } - _cache.store(result, rawData["ts"] + 300000); + _cache.store(result, rawData["cache_until"]); return result; case 404: developer.log("fetchCutoffs: Cutoffs are gone", name: "services/tetrio_crud", error: response.statusCode); @@ -466,7 +462,7 @@ class TetrioService extends DB { if (kIsWeb) { url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLTopOne"}); } else { - url = Uri.https('ch.tetr.io', 'api/users/lists/league', {"after": "25000", "limit": "1"}); + url = Uri.https('ch.tetr.io', 'api/users/by/league', {"after": "25000:0:0", "limit": "1"}); } try{ @@ -475,7 +471,7 @@ class TetrioService extends DB { switch (response.statusCode) { case 200: var rawJson = jsonDecode(response.body); - TetrioPlayerFromLeaderboard result = TetrioPlayerFromLeaderboard.fromJson(rawJson["data"]["users"][0], DateTime.fromMillisecondsSinceEpoch(rawJson["cache"]["cached_at"])); + TetrioPlayerFromLeaderboard result = TetrioPlayerFromLeaderboard.fromJson(rawJson["data"]["entries"][0], DateTime.fromMillisecondsSinceEpoch(rawJson["cache"]["cached_at"])); _cache.store(result, rawJson["cache"]["cached_until"]); return result; case 404: @@ -640,15 +636,17 @@ class TetrioService extends DB { } /// Retrieves full Tetra League leaderboard from Tetra Channel api. Returns a leaderboard object. Throws an exception if fails to retrieve. - Future fetchTLLeaderboard() async { - TetrioPlayersLeaderboard? cached = _cache.get("league", TetrioPlayersLeaderboard); + Future fetchTLLeaderboard({double? after}) async { + TetrioPlayersLeaderboard? cached = _cache.get("league${after != null ? after.toString() : ""}", TetrioPlayersLeaderboard); if (cached != null) return cached; - Uri url; if (kIsWeb) { url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLLeaderboard"}); } else { - url = Uri.https('ch.tetr.io', 'api/users/by/league'); + url = Uri.https('ch.tetr.io', 'api/users/by/league', { + "limit": "100", + if (after != null) "after": "$after:0:0" + }); } try{ final response = await client.get(url); @@ -688,6 +686,20 @@ class TetrioService extends DB { } } + Stream fetchFullLeaderboard() async* { + late double after; + int lbLength = 100; + TetrioPlayersLeaderboard leaderboard = await fetchTLLeaderboard(); + after = leaderboard.leaderboard.last.tr; + while (lbLength == 100){ + TetrioPlayersLeaderboard pseudoLb = await fetchTLLeaderboard(after: after); + leaderboard.addPlayers(pseudoLb.leaderboard); + lbLength = pseudoLb.leaderboard.length; + after = pseudoLb.leaderboard.last.tr; + yield leaderboard; + } + } + // i want to know progress, so i trying to figure out this thing: // Stream fetchTLLeaderboardAsStream() async { // TetrioPlayersLeaderboard? cached = _cache.get("league", TetrioPlayersLeaderboard); diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 3fa3e66..1fbdc79 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -11,6 +11,7 @@ import 'package:intl/intl.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter/services.dart'; import 'package:syncfusion_flutter_charts/charts.dart'; +import 'package:tetra_stats/data_objects/tetra_stats.dart'; import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/main.dart' show prefs, teto; @@ -156,8 +157,8 @@ class _MainState extends State with TickerProviderStateMixin { if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) await windowManager.setTitle(title); // Requesting Tetra League (alpha), records, news and top TR of player - late List requests; - late Summaries summaries; + List requests; + Summaries summaries = await teto.fetchSummaries(_searchFor); late TetraLeagueBetaStream tlStream; late News news; // late SingleplayerStream recentSprint; @@ -166,20 +167,20 @@ class _MainState extends State with TickerProviderStateMixin { // late SingleplayerStream blitz; late SingleplayerStream recentZenith; late SingleplayerStream recentZenithEX; - // late TetrioPlayerFromLeaderboard? topOne; + late TetrioPlayerFromLeaderboard? topOne; // late TopTr? topTR; - requests = await Future.wait([ // all at once (8 requests to oskware in total) + requests = await Future.wait([ teto.fetchSummaries(_searchFor), teto.fetchTLStream(_searchFor), teto.fetchNews(_searchFor), teto.fetchStream(_searchFor, "zenith/recent"), teto.fetchStream(_searchFor, "zenithex/recent"), - //teto.fetchStream(_searchFor, "40l/top"), - //teto.fetchStream(_searchFor, "blitz/top"), + teto.fetchCutoffs(), + (summaries.league.rank != "z" ? summaries.league.rank == "x+" : summaries.league.percentileRank == "x+") ? teto.fetchTopOneFromTheLeaderboard() : Future.delayed(Duration.zero, ()=>null), ]); //prefs.getBool("showPositions") != true ? teto.fetchCutoffs() : Future.delayed(Duration.zero, ()=>>[]), - //(me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? teto.fetchTopOneFromTheLeaderboard() : Future.delayed(Duration.zero, ()=>null), - //(me.tlSeason1.gamesPlayed > 9) ? teto.fetchTopTR(_searchFor) : Future.delayed(Duration.zero, () => null) // can retrieve this only if player has TR + + //(summaries.league.gamesPlayed > 9) ? teto.fetchTopTR(_searchFor) : Future.delayed(Duration.zero, () => null) // can retrieve this only if player has TR summaries = requests[0] as Summaries; tlStream = requests[1] as TetraLeagueBetaStream; // records = requests[1] as UserRecords; @@ -189,7 +190,7 @@ class _MainState extends State with TickerProviderStateMixin { // recent = requests[3] as SingleplayerStream; // sprint = requests[4] as SingleplayerStream; // blitz = requests[5] as SingleplayerStream; - // topOne = requests[7] as TetrioPlayerFromLeaderboard?; + topOne = requests[6] as TetrioPlayerFromLeaderboard?; // topTR = requests[8] as TopTr?; // No TR - no Top TR meAmongEveryone = teto.getCachedLeaderboardPositions(me.userId); @@ -202,17 +203,17 @@ class _MainState extends State with TickerProviderStateMixin { if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!); } } - //Map? cutoffs = prefs.getBool("showPositions") == true ? everyone!.cutoffs : (requests[6] as Cutoffs?)?.tr; - //Map? cutoffsGlicko = prefs.getBool("showPositions") == true ? everyone!.cutoffsGlicko : (requests[6] as Cutoffs?)?.glicko; + Map? cutoffs = (requests[5] as Cutoffs?)?.tr; + Map? cutoffsGlicko = (requests[5] as Cutoffs?)?.glicko; - // if (me.tlSeason1.gamesPlayed > 9) { - // thatRankCutoff = cutoffs?[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank]; - // thatRankGlickoCutoff = cutoffsGlicko?[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank]; - // nextRankCutoff = (me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? topOne?.tr??25000 : cutoffs?[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)]; - // nextRankGlickoCutoff = (me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? topOne?.glicko??double.infinity : cutoffsGlicko?[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)]; - // } + if (summaries.league.gamesPlayed > 9) { + thatRankCutoff = cutoffs?[summaries.league.rank != "z" ? summaries.league.rank : summaries.league.percentileRank]; + thatRankGlickoCutoff = cutoffsGlicko?[summaries.league.rank != "z" ? summaries.league.rank : summaries.league.percentileRank]; + nextRankCutoff = (summaries.league.rank != "z" ? summaries.league.rank == "x+" : summaries.league.percentileRank == "x+") ? topOne?.tr??25000 : cutoffs?[ranks.elementAtOrNull(ranks.indexOf(summaries.league.rank != "z" ? summaries.league.rank : summaries.league.percentileRank)+1)]; + nextRankGlickoCutoff = (summaries.league.rank != "z" ? summaries.league.rank == "x+" : summaries.league.percentileRank == "x+") ? topOne?.glicko??double.infinity : cutoffsGlicko?[ranks.elementAtOrNull(ranks.indexOf(summaries.league.rank != "z" ? summaries.league.rank : summaries.league.percentileRank)+1)]; + } - // if (everyone != null && me.tlSeason1.gamesPlayed > 9) rankAverages = everyone?.averages[me.tlSeason1.percentileRank]?[0]; + // if (everyone != null && summaries.league.gamesPlayed > 9) rankAverages = everyone?.averages[summaries.league.percentileRank]?[0]; // Making list of Tetra League matches bool isTracking = await teto.isPlayerTracking(me.userId); @@ -270,7 +271,7 @@ class _MainState extends State with TickerProviderStateMixin { } } - //states.addAll(await teto.getPlayer(me.userId)); + states.addAll(await teto.getPlayer(me.userId)); for (var element in states) { // For graphs I need only unique entries if (element.tlSeason1 != null && uniqueTL.isNotEmpty && uniqueTL.last != element.tlSeason1) uniqueTL.add(element.tlSeason1!); if (uniqueTL.isEmpty) uniqueTL.add(element.tlSeason1!); @@ -475,12 +476,12 @@ class _MainState extends State with TickerProviderStateMixin { //lastMatchPlayed: snapshot.data![11], bot: snapshot.data![0].role == "bot", guest: snapshot.data![0].role == "anon", - //thatRankCutoff: thatRankCutoff, - //thatRankCutoffGlicko: thatRankGlickoCutoff, - //thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null, - //nextRankCutoff: nextRankCutoff, - //nextRankCutoffGlicko: nextRankGlickoCutoff, - //nextRankTarget: (snapshot.data![0].tlSeason1.rank != "z" && snapshot.data![0].tlSeason1.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1)] : null, + thatRankCutoff: thatRankCutoff, + thatRankCutoffGlicko: thatRankGlickoCutoff, + //thatRankTarget: snapshot.data![1].league.rank != "z" ? rankTargets[snapshot.data![1].league.rank] : null, + nextRankCutoff: nextRankCutoff, + nextRankCutoffGlicko: nextRankGlickoCutoff, + //nextRankTarget: (snapshot.data![1].league.rank != "z" && snapshot.data![1].league.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![1].league.rank)+1)] : null, //averages: rankAverages, //lbPositions: meAmongEveryone ), @@ -516,12 +517,12 @@ class _MainState extends State with TickerProviderStateMixin { //lastMatchPlayed: snapshot.data![11], bot: snapshot.data![0].role == "bot", guest: snapshot.data![0].role == "anon", - //thatRankCutoff: thatRankCutoff, - //thatRankCutoffGlicko: thatRankGlickoCutoff, - //thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null, - //nextRankCutoff: nextRankCutoff, - //nextRankCutoffGlicko: nextRankGlickoCutoff, - //nextRankTarget: (snapshot.data![0].tlSeason1.rank != "z" && snapshot.data![0].tlSeason1.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1)] : null, + thatRankCutoff: thatRankCutoff, + thatRankCutoffGlicko: thatRankGlickoCutoff, + //thatRankTarget: snapshot.data![1].league.rank != "z" ? rankTargets[snapshot.data![1].league.rank] : null, + nextRankCutoff: nextRankCutoff, + nextRankCutoffGlicko: nextRankGlickoCutoff, + //nextRankTarget: (snapshot.data![1].league.rank != "z" && snapshot.data![1].league.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![1].league.rank)+1)] : null, //averages: rankAverages, //lbPositions: meAmongEveryone ), diff --git a/lib/views/main_view_tiles.dart b/lib/views/main_view_tiles.dart index 4e04035..da979c4 100644 --- a/lib/views/main_view_tiles.dart +++ b/lib/views/main_view_tiles.dart @@ -170,7 +170,7 @@ class DestinationLeaderboards extends StatefulWidget{ class _DestinationLeaderboardsState extends State { Cards rightCard = Cards.tetraLeague; - Duration postSeasonLeft = seasonStart.difference(DateTime.now()); + //Duration postSeasonLeft = seasonStart.difference(DateTime.now()); final List leaderboards = ["Tetra League", "Quick Play", "Quick Play Expert"]; @override @@ -245,7 +245,7 @@ class _DestinationGraphsState extends State { final 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"]; int _chartsIndex = 0; late List>> chartsData; - Duration postSeasonLeft = seasonStart.difference(DateTime.now()); + //Duration postSeasonLeft = seasonStart.difference(DateTime.now()); @override void initState(){ @@ -611,7 +611,7 @@ class RecordSummary extends StatelessWidget{ class _DestinationHomeState extends State { Cards rightCard = Cards.overview; CardMod cardMod = CardMod.info; - Duration postSeasonLeft = seasonStart.difference(DateTime.now()); + //Duration postSeasonLeft = seasonStart.difference(DateTime.now()); late Map>> modeButtons; late MapEntry? closestAverageBlitz; late bool blitzBetterThanClosestAverage; @@ -849,7 +849,7 @@ class _DestinationHomeState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ Text(t.tetraLeague, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42)), - Text("${t.seasonStarts} ${countdown(postSeasonLeft)}", textAlign: TextAlign.center) + //Text("${t.seasonStarts} ${countdown(postSeasonLeft)}", textAlign: TextAlign.center) ], ), ), @@ -1021,7 +1021,7 @@ class _DestinationHomeState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ Text(t.quickPlay, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42)), - Text("Leaderboard reset in ${countdown(postSeasonLeft)}", textAlign: TextAlign.center), + //Text("Leaderboard reset in ${countdown(postSeasonLeft)}", textAlign: TextAlign.center), ], ), ), diff --git a/lib/views/ranks_averages_view.dart b/lib/views/ranks_averages_view.dart index dbfa0e9..78c5bbd 100644 --- a/lib/views/ranks_averages_view.dart +++ b/lib/views/ranks_averages_view.dart @@ -1,9 +1,14 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:tetra_stats/data_objects/tetra_stats.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/utils/text_shadow.dart'; +import 'package:tetra_stats/views/compare_view.dart'; import 'package:tetra_stats/views/rank_averages_view.dart'; +import 'package:tetra_stats/widgets/text_timestamp.dart'; import 'package:window_manager/window_manager.dart'; import 'package:tetra_stats/main.dart' show teto; @@ -17,14 +22,9 @@ class RankAveragesView extends StatefulWidget { late String oldWindowTitle; class RanksAverages extends State { - Map> averages = {}; @override void initState() { - teto.fetchTLLeaderboard().then((value){ - averages = value.averages; - setState(() {}); - }); if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){ windowManager.getTitle().then((value) => oldWindowTitle = value); windowManager.setTitle("Tetra Stats: ${t.rankAveragesViewTitle}"); @@ -40,35 +40,110 @@ class RanksAverages extends State { @override Widget build(BuildContext context) { + bool bigScreen = MediaQuery.of(context).size.width >= 700; return Scaffold( appBar: AppBar( title: Text(t.rankAveragesViewTitle), ), backgroundColor: Colors.black, body: SafeArea( - child: averages.isEmpty ? const Center(child: Text('Fetching...')) : ListView.builder( - itemCount: averages.length, - itemBuilder: (context, index){ - List keys = averages.keys.toList(); - return ListTile( - leading: Image.asset("res/tetrio_tl_alpha_ranks/${keys[index]}.png", height: 48), - title: Text(t.players(n: averages[keys[index]]?[1]["players"]), style: const TextStyle(fontFamily: "Eurostile Round Extended")), - subtitle: Text("${f2.format(averages[keys[index]]?[0].apm)} APM, ${f2.format(averages[keys[index]]?[0].pps)} PPS, ${f2.format(averages[keys[index]]?[0].vs)} VS, ${f2.format(averages[keys[index]]?[0].nerdStats.app)} APP, ${f2.format(averages[keys[index]]?[0].nerdStats.vsapm)} VS/APM", - style: const TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey, fontSize: 13)), - trailing: Text("${f2.format(averages[keys[index]]?[1]["toEnterTR"])} TR", style: const TextStyle(fontSize: 28, fontFamily: "Eurostile Round")), - onTap: (){ - if (averages[keys[index]]?[1]["players"] > 0) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => RankView(rank: averages[keys[index]]!), + child: FutureBuilder(future: teto.fetchCutoffs(), builder: (context, snapshot){ + switch (snapshot.connectionState) { + case ConnectionState.none: + case ConnectionState.waiting: + case ConnectionState.active: + return const Center(child: CircularProgressIndicator(color: Colors.white)); + case ConnectionState.done: + if (snapshot.hasData){ + return Container( + alignment: Alignment.center, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Container( + alignment: Alignment.center, + width: MediaQuery.of(context).size.width, + constraints: const BoxConstraints(maxWidth: 900, minWidth: 610), + child: SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Table( + defaultVerticalAlignment: TableCellVerticalAlignment.middle, + border: TableBorder.all(color: Colors.grey.shade900), + columnWidths: const {0: FixedColumnWidth(48)}, + children: [ + TableRow( + children: [ + Text(t.rank, textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white)), + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text("TR", textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)), + ), + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text("Glicko", textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)), + ), + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text("Glixare", textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)), + ), + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text("S1 TR", textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)), + ), + ] + ), + for (String rank in snapshot.data!.tr.keys) TableRow( + decoration: BoxDecoration(gradient: LinearGradient(colors: [rankColors[rank]!.withAlpha(100), rankColors[rank]!.withAlpha(200)])), + children: [ + Container(decoration: BoxDecoration(boxShadow: [BoxShadow(color: Colors.black.withAlpha(132), blurRadius: 32.0, blurStyle: BlurStyle.inner)]), child: Image.asset("res/tetrio_tl_alpha_ranks/$rank.png", height: 48)), + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text(f2.format(snapshot.data!.tr[rank]), textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white, shadows: textShadow)), + ), + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text(f2.format(snapshot.data!.glicko[rank]), textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white, shadows: textShadow)), + ), + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text(f2.format(snapshot.data!.gxe[rank]), textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white, shadows: textShadow)), + ), + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text(f2.format(snapshot.data!.gxe[rank]!*250), textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white, shadows: textShadow)), + ), + ] + ) + ], + ), + Text(t.sprintAndBlitsRelevance(date: timestamp(snapshot.data!.ts))) + ], ), - ); - } - }, + ), + ), + ), ); - }) - ), + } + if (snapshot.hasError){ + return Center(child: + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(snapshot.error.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 42, fontWeight: FontWeight.bold), textAlign: TextAlign.center), + if (snapshot.stackTrace != null) Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text(snapshot.stackTrace.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 18), textAlign: TextAlign.center), + ), + ], + ) + ); + } + return const Text("end of FutureBuilder"); + } + }) + ), ); } } diff --git a/lib/widgets/tl_progress_bar.dart b/lib/widgets/tl_progress_bar.dart index b59b181..8a1be3c 100644 --- a/lib/widgets/tl_progress_bar.dart +++ b/lib/widgets/tl_progress_bar.dart @@ -61,7 +61,7 @@ class TLProgress extends StatelessWidget{ if (tlData.nextAt > 0 && nextRankTRcutoff != null) const TextSpan(text: "\n"), if (nextRankTRcutoff != null) TextSpan(text: "${f2.format(nextRankTRcutoff)} (${comparef2.format(nextRankTRcutoff!-tlData.tr)}) TR"), if ((tlData.nextAt > 0 || nextRankTRcutoff != null) && nextRankGlickoCutoff != null) const TextSpan(text: "\n"), - if (nextRankGlickoCutoff != null) TextSpan(text: (tlData.standing < tlData.nextAt || ((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin < 0.5 && ((tlData.rank != "x" && tlData.rank != "z") || tlData.percentileRank != "x"))) ? t.promotionOnNextWin : t.numOfVictories(wins: f2.format((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin)), style: TextStyle(color: (tlData.standing < tlData.nextAt || ((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin < 0.5 && tlData.percentileRank != "x")) ? Colors.greenAccent : null)) + if (nextRankGlickoCutoff != null) TextSpan(text: (tlData.standing < tlData.nextAt || ((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin < 0.5 && ((tlData.rank != "x+" && tlData.rank != "z") || tlData.percentileRank != "x+"))) ? t.promotionOnNextWin : t.numOfVictories(wins: f2.format((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin)), style: TextStyle(color: (tlData.standing < tlData.nextAt || ((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin < 0.5 && tlData.percentileRank != "x+")) ? Colors.greenAccent : null)) ] ) ) diff --git a/lib/widgets/zenith_thingy.dart b/lib/widgets/zenith_thingy.dart index 0c0164e..0a9394c 100644 --- a/lib/widgets/zenith_thingy.dart +++ b/lib/widgets/zenith_thingy.dart @@ -148,7 +148,7 @@ class _ZenithThingyState extends State { const Positioned(left: 25, top: 20, child: Text("otal time", style: TextStyle(fontFamily: "Eurostile Round Extended"))), Padding( padding: const EdgeInsets.only(left: 10.0), - child: Text("${getMoreNormalTime(record!.stats.finalTime)}%", style: TextStyle( + child: Text("${getMoreNormalTime(record!.stats.finalTime)}", style: TextStyle( shadows: textShadow, fontFamily: "Eurostile Round Extended", fontSize: 36, @@ -158,7 +158,6 @@ class _ZenithThingyState extends State { ) ], ), - Text("Total time: ${getMoreNormalTime(record!.stats.finalTime)}", style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center), Table( columnWidths: const { 0: FixedColumnWidth(36) diff --git a/pubspec.yaml b/pubspec.yaml index f8a3f3e..bf95d9e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: tetra_stats description: Track your and other player stats in TETR.IO publish_to: 'none' -version: 1.6.3+29 +version: 1.6.4+30 environment: sdk: '>=3.0.0'