small work

This commit is contained in:
dan63047 2024-09-09 01:10:51 +03:00
parent e403b0cbee
commit 2f9cf0ee20
7 changed files with 335 additions and 200 deletions

View File

@ -6,7 +6,7 @@ import 'package:tetra_stats/data_objects/tetra_league.dart';
import 'package:tetra_stats/data_objects/tetrio_constants.dart'; import 'package:tetra_stats/data_objects/tetrio_constants.dart';
import 'package:tetra_stats/data_objects/tetrio_zen.dart'; import 'package:tetra_stats/data_objects/tetrio_zen.dart';
class Summaries{ class Summaries {
late String id; late String id;
RecordSingle? sprint; RecordSingle? sprint;
RecordSingle? blitz; RecordSingle? blitz;
@ -21,18 +21,41 @@ class Summaries{
Summaries(this.id, this.league, this.zen); Summaries(this.id, this.league, this.zen);
Summaries.fromJson(Map<String, dynamic> json, String i){ Summaries.fromJson(Map<String, dynamic> json, String i) {
id = i; id = i;
if (json['40l']['record'] != null) sprint = RecordSingle.fromJson(json['40l']['record'], json['40l']['rank'], json['40l']['rank_local']); if (json['40l']['record'] != null)
if (json['blitz']['record'] != null) blitz = RecordSingle.fromJson(json['blitz']['record'], json['blitz']['rank'], json['40l']['rank_local']); sprint = RecordSingle.fromJson(json['40l']['record'], json['40l']['rank'],
if (json['zenith']['record'] != null) zenith = RecordSingle.fromJson(json['zenith']['record'], json['zenith']['rank'], json['zenith']['rank_local']); json['40l']['rank_local']);
if (json['zenith']['best']['record'] != null) zenithCareerBest = RecordSingle.fromJson(json['zenith']['best']['record'], json['zenith']['best']['rank'], -1); if (json['blitz']['record'] != null)
if (json['zenithex']['record'] != null) zenithEx = RecordSingle.fromJson(json['zenithex']['record'], json['zenithex']['rank'], json['zenithex']['rank_local']); blitz = RecordSingle.fromJson(json['blitz']['record'],
if (json['zenithex']['best']['record'] != null) zenithCareerBest = RecordSingle.fromJson(json['zenithex']['best']['record'], json['zenith']['best']['rank'], -1); json['blitz']['rank'], json['40l']['rank_local']);
achievements = [for (var achievement in json['achievements']) Achievement.fromJson(achievement)]; if (json['zenith']['record'] != null)
league = TetraLeague.fromJson(json['league'], DateTime.now(), currentSeason, i); zenith = RecordSingle.fromJson(json['zenith']['record'],
if (json['league']['past'].isNotEmpty) for (var key in json['league']['past'].keys){ json['zenith']['rank'], json['zenith']['rank_local']);
pastLeague[int.parse(key)] = TetraLeague.fromJson(json['league']['past'][key], DateTime(1970), int.parse(json['league']['past'][key]['season']), i); if (json['zenith']['best']['record'] != null)
zenithCareerBest = RecordSingle.fromJson(
json['zenith']['best']['record'], json['zenith']['best']['rank'], -1);
if (json['zenithex']['record'] != null)
zenithEx = RecordSingle.fromJson(json['zenithex']['record'],
json['zenithex']['rank'], json['zenithex']['rank_local']);
if (json['zenithex']['best']['record'] != null)
zenithCareerBest = RecordSingle.fromJson(
json['zenithex']['best']['record'],
json['zenith']['best']['rank'],
-1);
achievements = [
for (var achievement in json['achievements'])
Achievement.fromJson(achievement)
];
league =
TetraLeague.fromJson(json['league'], DateTime.now(), currentSeason, i);
if (json['league']['past'].isNotEmpty)
for (var key in json['league']['past'].keys) {
pastLeague[int.parse(key)] = TetraLeague.fromJson(
json['league']['past'][key],
null,
int.parse(json['league']['past'][key]['season']),
i);
} }
zen = TetrioZen.fromJson(json['zen']); zen = TetrioZen.fromJson(json['zen']);
} }

View File

@ -57,22 +57,35 @@ class TetraLeague {
this.apm, this.apm,
this.pps, this.pps,
this.vs, this.vs,
required this.season}){ required this.season}) {
nerdStats = (apm != null && pps != null && vs != null) ? NerdStats(apm!, pps!, vs!) : null; nerdStats = (apm != null && pps != null && vs != null)
estTr = (nerdStats != null) ? EstTr(apm!, pps!, vs!, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe) : null; ? NerdStats(apm!, pps!, vs!)
playstyle =(nerdStats != null) ? Playstyle(apm!, pps!, nerdStats!.app, nerdStats!.vsapm, nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, estTr!.statrank) : null; : null;
estTr = (nerdStats != null)
? EstTr(apm!, pps!, vs!, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp,
nerdStats!.gbe)
: null;
playstyle = (nerdStats != null)
? Playstyle(apm!, pps!, nerdStats!.app, nerdStats!.vsapm,
nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, estTr!.statrank)
: null;
} }
double get winrate => gamesWon / gamesPlayed; double get winrate => gamesWon / gamesPlayed;
double get s1tr => gxe * 250; double get s1tr => gxe * 250;
TetraLeague.fromJson(Map<String, dynamic> json, ts, int s, String i) { TetraLeague.fromJson(
timestamp = ts; Map<String, dynamic> json, DateTime? ts, int s, String i) {
timestamp = ts != null ? ts : seasonEnds[s - 1];
season = s; season = s;
id = i; id = i;
gamesPlayed = json['gamesplayed'] ?? 0; gamesPlayed = json['gamesplayed'] ?? 0;
gamesWon = json['gameswon'] ?? 0; gamesWon = json['gameswon'] ?? 0;
tr = json['tr'] != null ? json['tr'].toDouble() : json['rating'] != null ? json['rating'].toDouble() : -1; tr = json['tr'] != null
? json['tr'].toDouble()
: json['rating'] != null
? json['rating'].toDouble()
: -1;
glicko = json['glicko']?.toDouble(); glicko = json['glicko']?.toDouble();
rd = json['rd'] != null ? json['rd']!.toDouble() : noTrRd; rd = json['rd'] != null ? json['rd']!.toDouble() : noTrRd;
gxe = json['gxe'] != null ? json['gxe'].toDouble() : -1; gxe = json['gxe'] != null ? json['gxe'].toDouble() : -1;
@ -81,38 +94,67 @@ class TetraLeague {
apm = json['apm']?.toDouble(); apm = json['apm']?.toDouble();
pps = json['pps']?.toDouble(); pps = json['pps']?.toDouble();
vs = json['vs']?.toDouble(); vs = json['vs']?.toDouble();
decaying = switch(json['decaying'].runtimeType){ decaying = switch (json['decaying'].runtimeType) {
int => json['decaying'] == 1, int => json['decaying'] == 1,
bool => json['decaying'], bool => json['decaying'],
_ => false _ => false
}; };
standing = json['standing'] ?? json['placement'] ?? -1; standing = json['standing'] ?? json['placement'] ?? -1;
percentile = json['percentile'] != null ? json['percentile'].toDouble() : rankCutoffs[rank]; percentile = json['percentile'] != null
? json['percentile'].toDouble()
: rankCutoffs[rank];
standingLocal = json['standing_local'] ?? -1; standingLocal = json['standing_local'] ?? -1;
prevRank = json['prev_rank']; prevRank = json['prev_rank'];
prevAt = json['prev_at'] ?? -1; prevAt = json['prev_at'] ?? -1;
nextRank = json['next_rank']; nextRank = json['next_rank'];
nextAt = json['next_at'] ?? -1; nextAt = json['next_at'] ?? -1;
percentileRank = json['percentile_rank'] ?? rank; percentileRank = json['percentile_rank'] ?? rank;
nerdStats = (apm != null && pps != null && vs != null) ? NerdStats(apm!, pps!, vs!) : null; nerdStats = (apm != null && pps != null && vs != null)
estTr = (nerdStats != null) ? EstTr(apm!, pps!, vs!, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe) : null; ? NerdStats(apm!, pps!, vs!)
playstyle = (nerdStats != null) ? Playstyle(apm!, pps!, nerdStats!.app, nerdStats!.vsapm, nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, estTr!.statrank) : null; : null;
estTr = (nerdStats != null)
? EstTr(apm!, pps!, vs!, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp,
nerdStats!.gbe)
: null;
playstyle = (nerdStats != null)
? Playstyle(apm!, pps!, nerdStats!.app, nerdStats!.vsapm,
nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, estTr!.statrank)
: null;
} }
@override @override
bool operator ==(covariant TetraLeague other) => gamesPlayed == other.gamesPlayed && rd == other.rd; bool operator ==(covariant TetraLeague other) =>
gamesPlayed == other.gamesPlayed && rd == other.rd;
bool lessStrictCheck (covariant TetraLeague other) => gamesPlayed == other.gamesPlayed && glicko == other.glicko; bool lessStrictCheck(covariant TetraLeague other) =>
gamesPlayed == other.gamesPlayed && glicko == other.glicko;
double? get esttracc => (estTr != null) ? estTr!.esttr - tr : null; double? get esttracc => (estTr != null) ? estTr!.esttr - tr : null;
TetrioPlayerFromLeaderboard convertToPlayerFromLeaderboard(String id) => TetrioPlayerFromLeaderboard( TetrioPlayerFromLeaderboard convertToPlayerFromLeaderboard(String id) =>
id, "", "user", -1, null, timestamp, gamesPlayed, gamesWon, TetrioPlayerFromLeaderboard(
tr, gxe, glicko??0, rd??noTrRd, rank, bestRank, apm??0, pps??0, vs??0, decaying); id,
"",
"user",
-1,
null,
timestamp,
gamesPlayed,
gamesWon,
tr,
gxe,
glicko ?? 0,
rd ?? noTrRd,
rank,
bestRank,
apm ?? 0,
pps ?? 0,
vs ?? 0,
decaying);
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{}; final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id+timestamp.millisecondsSinceEpoch.toRadixString(16); data['id'] = id + timestamp.millisecondsSinceEpoch.toRadixString(16);
if (gamesPlayed > 0) data['gamesplayed'] = gamesPlayed; if (gamesPlayed > 0) data['gamesplayed'] = gamesPlayed;
if (gamesWon > 0) data['gameswon'] = gamesWon; if (gamesWon > 0) data['gameswon'] = gamesWon;
if (tr >= 0) data['tr'] = tr; if (tr >= 0) data['tr'] = tr;

View File

@ -13,7 +13,24 @@ const double vsapmWeight = 60;
const double cheeseWeight = 1.25; const double cheeseWeight = 1.25;
const double gbeWeight = 315; const double gbeWeight = 315;
const List<String> ranks = [ const List<String> ranks = [
"d", "d+", "c-", "c", "c+", "b-", "b", "b+", "a-", "a", "a+", "s-", "s", "s+", "ss", "u", "x", "x+" "d",
"d+",
"c-",
"c",
"c+",
"b-",
"b",
"b+",
"a-",
"a",
"a+",
"s-",
"s",
"s+",
"ss",
"u",
"x",
"x+"
]; ];
const Map<String, double> rankCutoffs = { const Map<String, double> rankCutoffs = {
"x+": 0.002, "x+": 0.002,
@ -57,6 +74,7 @@ const Map<String, double> rankTargets = {
"d+": 800.00, "d+": 800.00,
"d": 0.00, "d": 0.00,
}; };
// DateTime seasonStart = DateTime.utc(2024, 08, 16, 18); // DateTime seasonStart = DateTime.utc(2024, 08, 16, 18);
//DateTime seasonEnd = DateTime.utc(2024, 07, 26, 15); //DateTime seasonEnd = DateTime.utc(2024, 07, 26, 15);
enum Stats { enum Stats {
@ -89,7 +107,7 @@ enum Stats {
stride, stride,
stridemMinusPlonk, stridemMinusPlonk,
openerMinusInfDS openerMinusInfDS
} }
const Map<Stats, String> chartsShortTitles = { const Map<Stats, String> chartsShortTitles = {
Stats.tr: "TR", Stats.tr: "TR",
@ -121,9 +139,10 @@ const Map<Stats, String> chartsShortTitles = {
Stats.stride: "Stride", Stats.stride: "Stride",
Stats.stridemMinusPlonk: "Stride - Plonk", Stats.stridemMinusPlonk: "Stride - Plonk",
Stats.openerMinusInfDS: "Opener - Inf. DS" Stats.openerMinusInfDS: "Opener - Inf. DS"
}; };
const Map<String, Color> rankColors = { // thanks osk for const rankColors at https://ch.tetr.io/res/js/base.js:458 const Map<String, Color> rankColors = {
// thanks osk for const rankColors at https://ch.tetr.io/res/js/base.js:458
'x+': Color(0xFF643C8D), 'x+': Color(0xFF643C8D),
'x': Color(0xFFFF45FF), 'x': Color(0xFFFF45FF),
'u': Color(0xFFFF3813), 'u': Color(0xFFFF3813),
@ -145,7 +164,8 @@ const Map<String, Color> rankColors = { // thanks osk for const rankColors at ht
'z': Color(0xFF375433) 'z': Color(0xFF375433)
}; };
const Map<String, Duration> sprintAverages = { // based on https://discord.com/channels/673303546107658242/674421736162197515/1244287342965952562 const Map<String, Duration> sprintAverages = {
// based on https://discord.com/channels/673303546107658242/674421736162197515/1244287342965952562
'x': Duration(seconds: 25, milliseconds: 144), 'x': Duration(seconds: 25, milliseconds: 144),
'u': Duration(seconds: 36, milliseconds: 115), 'u': Duration(seconds: 36, milliseconds: 115),
'ss': Duration(seconds: 46, milliseconds: 396), 'ss': Duration(seconds: 46, milliseconds: 396),
@ -186,3 +206,13 @@ const Map<String, int> blitzAverages = {
'd': 20728, 'd': 20728,
//'z': 72084 //'z': 72084
}; };
List<DateTime> seasonStarts = [
DateTime.utc(2020, DateTime.april, 18, 4), // Source = twitter or something
DateTime.utc(
2024, DateTime.august, 16, 18, 41, 10) // Source = osk status page
];
List<DateTime> seasonEnds = [
DateTime.utc(2024, DateTime.july, 26, 15) // Source - TETR.IO discord guild
];

View File

@ -8,3 +8,7 @@ Color getColorOfRank(int rank){
if (rank <= 99) return Colors.greenAccent; if (rank <= 99) return Colors.greenAccent;
return Colors.grey; return Colors.grey;
} }
Color getDifferenceColor(num diff){
return diff.isNegative ? Colors.redAccent : Colors.greenAccent;
}

View File

@ -1,6 +1,7 @@
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
final NumberFormat compareIntf = NumberFormat("+#,###;-#,###")..maximumFractionDigits = 0;
final NumberFormat comparef = NumberFormat("+#,###.###;-#,###.###")..maximumFractionDigits = 3; final NumberFormat comparef = NumberFormat("+#,###.###;-#,###.###")..maximumFractionDigits = 3;
final NumberFormat comparef2 = NumberFormat("+#,###.##;-#,###.##")..maximumFractionDigits = 2; final NumberFormat comparef2 = NumberFormat("+#,###.##;-#,###.##")..maximumFractionDigits = 2;
final NumberFormat intf = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0); final NumberFormat intf = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0);

View File

@ -243,7 +243,7 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
bool _smooth = false; bool _smooth = false;
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"]; 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; int _chartsIndex = 0;
late List<DropdownMenuItem<List<_HistoryChartSpot>>> chartsData; late List<List<DropdownMenuItem<List<_HistoryChartSpot>>>> historyData;
//Duration postSeasonLeft = seasonStart.difference(DateTime.now()); //Duration postSeasonLeft = seasonStart.difference(DateTime.now());
@override @override
@ -282,10 +282,7 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
super.initState(); super.initState();
} }
Future<List<DropdownMenuItem<List<_HistoryChartSpot>>>> getChartsData(bool fetchHistory) async { Future<List<List<DropdownMenuItem<List<_HistoryChartSpot>>>>> getHistoryData(bool fetchHistory) async {
List<TetrioPlayer> states = [];
Set<TetraLeague> uniqueTL = {};
if(fetchHistory){ if(fetchHistory){
try{ try{
var history = await teto.fetchAndsaveTLHistory(widget.searchFor); var history = await teto.fetchAndsaveTLHistory(widget.searchFor);
@ -301,49 +298,47 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
} }
} }
//states.addAll(await teto.getPlayer(widget.searchFor)); List<List<TetraLeague>> states = await Future.wait<List<TetraLeague>>([
// for (var element in states) { teto.getStates(widget.searchFor, season: 1), teto.getStates(widget.searchFor, season: 2),
// if (element.tlSeason1 != null && uniqueTL.isNotEmpty && uniqueTL.last != element.tlSeason1) uniqueTL.add(element.tlSeason1!); ]);
// if (uniqueTL.isEmpty) uniqueTL.add(element.tlSeason1!);
// }
if (uniqueTL.length >= 2){ if (states.length >= 2){
chartsData = <DropdownMenuItem<List<_HistoryChartSpot>>>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid historyData = [for (List<TetraLeague> s in states) <DropdownMenuItem<List<_HistoryChartSpot>>>[ // 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) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.tr)], child: Text(t.statCellNum.tr)), DropdownMenuItem(value: [for (var tl in s) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.tr)], child: Text(t.statCellNum.tr)),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.glicko!)], child: const Text("Glicko")), DropdownMenuItem(value: [for (var tl in s) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.glicko!)], child: const Text("Glicko")),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.rd!)], child: const Text("Rating Deviation")), DropdownMenuItem(value: [for (var tl in s) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.rd!)], child: const Text("Rating Deviation")),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.apm != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.apm!)], child: Text(t.statCellNum.apm.replaceAll(RegExp(r'\n'), " "))), DropdownMenuItem(value: [for (var tl in s) if (tl.apm != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.apm!)], child: Text(t.statCellNum.apm.replaceAll(RegExp(r'\n'), " "))),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.pps != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.pps!)], child: Text(t.statCellNum.pps.replaceAll(RegExp(r'\n'), " "))), DropdownMenuItem(value: [for (var tl in s) if (tl.pps != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.pps!)], child: Text(t.statCellNum.pps.replaceAll(RegExp(r'\n'), " "))),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.vs != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.vs!)], child: Text(t.statCellNum.vs.replaceAll(RegExp(r'\n'), " "))), DropdownMenuItem(value: [for (var tl in s) if (tl.vs != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.vs!)], child: Text(t.statCellNum.vs.replaceAll(RegExp(r'\n'), " "))),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.app)], child: Text(t.statCellNum.app.replaceAll(RegExp(r'\n'), " "))), DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.app)], child: Text(t.statCellNum.app.replaceAll(RegExp(r'\n'), " "))),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.dss)], child: Text(t.statCellNum.dss.replaceAll(RegExp(r'\n'), " "))), DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.dss)], child: Text(t.statCellNum.dss.replaceAll(RegExp(r'\n'), " "))),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.dsp)], child: Text(t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "))), DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.dsp)], child: Text(t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "))),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.appdsp)], child: const Text("APP + DS/P")), DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.appdsp)], child: const Text("APP + DS/P")),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.vsapm)], child: const Text("VS/APM")), DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.vsapm)], child: const Text("VS/APM")),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.cheese)], child: Text(t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "))), DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.cheese)], child: Text(t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "))),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.gbe)], child: Text(t.statCellNum.gbe.replaceAll(RegExp(r'\n'), " "))), DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.gbe)], child: Text(t.statCellNum.gbe.replaceAll(RegExp(r'\n'), " "))),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.nyaapp)], child: Text(t.statCellNum.nyaapp.replaceAll(RegExp(r'\n'), " "))), DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.nyaapp)], child: Text(t.statCellNum.nyaapp.replaceAll(RegExp(r'\n'), " "))),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.area)], child: Text(t.statCellNum.area.replaceAll(RegExp(r'\n'), " "))), DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.area)], child: Text(t.statCellNum.area.replaceAll(RegExp(r'\n'), " "))),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.estTr != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.estTr!.esttr)], child: Text(t.statCellNum.estOfTR.replaceAll(RegExp(r'\n'), " "))), DropdownMenuItem(value: [for (var tl in s) if (tl.estTr != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.estTr!.esttr)], child: Text(t.statCellNum.estOfTR.replaceAll(RegExp(r'\n'), " "))),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.esttracc != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.esttracc!)], child: Text(t.statCellNum.accOfEst.replaceAll(RegExp(r'\n'), " "))), DropdownMenuItem(value: [for (var tl in s) if (tl.esttracc != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.esttracc!)], child: Text(t.statCellNum.accOfEst.replaceAll(RegExp(r'\n'), " "))),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.opener)], child: const Text("Opener")), DropdownMenuItem(value: [for (var tl in s) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.opener)], child: const Text("Opener")),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.plonk)], child: const Text("Plonk")), DropdownMenuItem(value: [for (var tl in s) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.plonk)], child: const Text("Plonk")),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.infds)], child: const Text("Inf. DS")), DropdownMenuItem(value: [for (var tl in s) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.infds)], child: const Text("Inf. DS")),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.stride)], child: const Text("Stride")), DropdownMenuItem(value: [for (var tl in s) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.stride)], child: const Text("Stride")),
]; ]];
}else{ }else{
chartsData = []; historyData = [];
} }
fetchData = false; fetchData = false;
return chartsData; return historyData;
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FutureBuilder<List<DropdownMenuItem<List<_HistoryChartSpot>>>>( return FutureBuilder<List<List<DropdownMenuItem<List<_HistoryChartSpot>>>>>(
future: getChartsData(fetchData), future: getHistoryData(fetchData),
builder: (context, snapshot) { builder: (context, snapshot) {
switch (snapshot.connectionState){ switch (snapshot.connectionState){
case ConnectionState.none: case ConnectionState.none:
@ -352,7 +347,7 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
case ConnectionState.done: case ConnectionState.done:
if (snapshot.hasData && snapshot.data!.isNotEmpty){ if (snapshot.hasData && snapshot.data!.isNotEmpty){
List<_HistoryChartSpot> selectedGraph = snapshot.data![_chartsIndex].value!; List<_HistoryChartSpot> selectedGraph = snapshot.data![currentSeason-1][_chartsIndex].value!;
yAxisTitle = _historyShortTitles[_chartsIndex]; yAxisTitle = _historyShortTitles[_chartsIndex];
return SingleChildScrollView( return SingleChildScrollView(
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
@ -384,11 +379,11 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
children: [ children: [
const Padding(padding: EdgeInsets.all(8.0), child: Text("Y:", style: TextStyle(fontSize: 22))), const Padding(padding: EdgeInsets.all(8.0), child: Text("Y:", style: TextStyle(fontSize: 22))),
DropdownButton( DropdownButton(
items: chartsData, items: historyData[currentSeason-1],
value: chartsData[_chartsIndex].value, value: historyData[currentSeason-1][_chartsIndex].value,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_chartsIndex = chartsData.indexWhere((element) => element.value == value); _chartsIndex = historyData[currentSeason-1].indexWhere((element) => element.value == value);
}); });
} }
), ),
@ -411,7 +406,7 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
], ],
), ),
), ),
if(chartsData[_chartsIndex].value!.length > 1) Card( if(historyData[currentSeason-1][_chartsIndex].value!.length > 1) Card(
child: SizedBox( child: SizedBox(
width: MediaQuery.of(context).size.width - 88, width: MediaQuery.of(context).size.width - 88,
height: MediaQuery.of(context).size.height - 60, height: MediaQuery.of(context).size.height - 60,
@ -427,7 +422,7 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
series: <CartesianSeries>[ series: <CartesianSeries>[
if (_gamesPlayedInsteadOfDateAndTime) StepLineSeries<_HistoryChartSpot, int>( if (_gamesPlayedInsteadOfDateAndTime) StepLineSeries<_HistoryChartSpot, int>(
enableTooltip: true, enableTooltip: true,
dataSource: chartsData[_chartsIndex].value!, dataSource: historyData[currentSeason-1][_chartsIndex].value!,
animationDuration: 0, animationDuration: 0,
opacity: _smooth ? 0 : 1, opacity: _smooth ? 0 : 1,
xValueMapper: (_HistoryChartSpot data, _) => data.gamesPlayed, xValueMapper: (_HistoryChartSpot data, _) => data.gamesPlayed,
@ -436,14 +431,14 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
trendlines:<Trendline>[ trendlines:<Trendline>[
Trendline( Trendline(
isVisible: _smooth, isVisible: _smooth,
period: (chartsData[_chartsIndex].value!.length/175).floor(), period: (historyData[currentSeason-1][_chartsIndex].value!.length/175).floor(),
type: TrendlineType.movingAverage, type: TrendlineType.movingAverage,
color: Theme.of(context).colorScheme.primary) color: Theme.of(context).colorScheme.primary)
], ],
) )
else StepLineSeries<_HistoryChartSpot, DateTime>( else StepLineSeries<_HistoryChartSpot, DateTime>(
enableTooltip: true, enableTooltip: true,
dataSource: chartsData[_chartsIndex].value!, dataSource: historyData[currentSeason-1][_chartsIndex].value!,
animationDuration: 0, animationDuration: 0,
opacity: _smooth ? 0 : 1, opacity: _smooth ? 0 : 1,
xValueMapper: (_HistoryChartSpot data, _) => data.timestamp, xValueMapper: (_HistoryChartSpot data, _) => data.timestamp,
@ -452,7 +447,7 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
trendlines:<Trendline>[ trendlines:<Trendline>[
Trendline( Trendline(
isVisible: _smooth, isVisible: _smooth,
period: (chartsData[_chartsIndex].value!.length/175).floor(), period: (historyData[currentSeason-1][_chartsIndex].value!.length/175).floor(),
type: TrendlineType.movingAverage, type: TrendlineType.movingAverage,
color: Theme.of(context).colorScheme.primary) color: Theme.of(context).colorScheme.primary)
], ],
@ -462,7 +457,7 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
) )
), ),
) )
else if (chartsData[_chartsIndex].value!.length <= 1) Center(child: Column( else if (historyData[currentSeason-1][_chartsIndex].value!.length <= 1) Center(child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text(t.notEnoughData, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)), Text(t.notEnoughData, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)),
@ -525,11 +520,12 @@ class DestinationHome extends StatefulWidget{
class FetchResults{ class FetchResults{
bool success; bool success;
TetrioPlayer? player; TetrioPlayer? player;
List<TetraLeague> states;
Summaries? summaries; Summaries? summaries;
Cutoffs? cutoffs; Cutoffs? cutoffs;
Exception? exception; Exception? exception;
FetchResults(this.success, this.player, this.summaries, this.cutoffs, this.exception); FetchResults(this.success, this.player, this.states, this.summaries, this.cutoffs, this.exception);
} }
class RecordSummary extends StatelessWidget{ class RecordSummary extends StatelessWidget{
@ -623,7 +619,19 @@ class LeagueCard extends StatelessWidget{
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text(showSeasonNumber ? "Season ${league.season}" : "Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, height: 0.9)), if (showSeasonNumber) Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Text("Season ${league.season}", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, height: 0.9)),
Spacer(),
Text(
"${seasonStarts.elementAtOrNull(league.season - 1) != null ? timestamp(seasonStarts[league.season - 1]) : "---"}${seasonEnds.elementAtOrNull(league.season - 1) != null ? timestamp(seasonEnds[league.season - 1]) : "---"}",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey)),
],
)
else Text("Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, height: 0.9)),
const Divider(color: Color.fromARGB(50, 158, 158, 158)), const Divider(color: Color.fromARGB(50, 158, 158, 158)),
TLRatingThingy(userID: "", tlData: league, showPositions: true), TLRatingThingy(userID: "", tlData: league, showPositions: true),
const Divider(color: Color.fromARGB(50, 158, 158, 158)), const Divider(color: Color.fromARGB(50, 158, 158, 158)),
@ -658,7 +666,7 @@ class _DestinationHomeState extends State<DestinationHome> {
player = await teto.fetchPlayer(widget.searchFor); // Otherwise it's probably a user id or username player = await teto.fetchPlayer(widget.searchFor); // Otherwise it's probably a user id or username
} }
}on TetrioPlayerNotExist{ }on TetrioPlayerNotExist{
return FetchResults(false, null, null, null, TetrioPlayerNotExist()); return FetchResults(false, null, [], null, null, TetrioPlayerNotExist());
} }
late Summaries summaries; late Summaries summaries;
late Cutoffs cutoffs; late Cutoffs cutoffs;
@ -666,9 +674,16 @@ class _DestinationHomeState extends State<DestinationHome> {
teto.fetchSummaries(player.userId), teto.fetchSummaries(player.userId),
teto.fetchCutoffsBeanserver(), teto.fetchCutoffsBeanserver(),
]); ]);
List<TetraLeague> states = await teto.getStates(player.userId, season: currentSeason);
summaries = requests[0]; summaries = requests[0];
cutoffs = requests[1]; cutoffs = requests[1];
return FetchResults(true, player, summaries, cutoffs, null);
bool isTracking = await teto.isPlayerTracking(player.userId);
if (isTracking){ // if tracked - save data to local DB
await teto.storeState(summaries.league);
}
return FetchResults(true, player, states, summaries, cutoffs, null);
} }
Widget getOverviewCard(Summaries summaries){ Widget getOverviewCard(Summaries summaries){
@ -863,7 +878,8 @@ class _DestinationHomeState extends State<DestinationHome> {
); );
} }
Widget getTetraLeagueCard(TetraLeague data, Cutoffs? cutoffs){ Widget getTetraLeagueCard(TetraLeague data, Cutoffs? cutoffs, List<TetraLeague> states){
TetraLeague? toCompare = states.length >= 2 ? states.elementAtOrNull(states.length-2) : null;
return Column( return Column(
children: [ children: [
Card( Card(
@ -876,13 +892,13 @@ class _DestinationHomeState extends State<DestinationHome> {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text(t.tetraLeague, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42)), Text(t.tetraLeague, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42)),
//Text("${t.seasonStarts} ${countdown(postSeasonLeft)}", textAlign: TextAlign.center) //Text("${states.last.timestamp} ${states.last.tr}", textAlign: TextAlign.center)
], ],
), ),
), ),
), ),
), ),
TetraLeagueThingy(league: data, cutoffs: cutoffs), TetraLeagueThingy(league: data, toCompare: toCompare, cutoffs: cutoffs),
if (data.nerdStats != null) Card( if (data.nerdStats != null) Card(
//surfaceTintColor: rankColors[data.rank], //surfaceTintColor: rankColors[data.rank],
child: Row( child: Row(
@ -894,7 +910,7 @@ class _DestinationHomeState extends State<DestinationHome> {
], ],
), ),
), ),
if (data.nerdStats != null) NerdStatsThingy(nerdStats: data.nerdStats!), if (data.nerdStats != null) NerdStatsThingy(nerdStats: data.nerdStats!, oldNerdStats: toCompare?.nerdStats),
if (data.nerdStats != null) GraphsThingy(nerdStats: data.nerdStats!, playstyle: data.playstyle!, apm: data.apm!, pps: data.pps!, vs: data.vs!) if (data.nerdStats != null) GraphsThingy(nerdStats: data.nerdStats!, playstyle: data.playstyle!, apm: data.apm!, pps: data.pps!, vs: data.vs!)
], ],
); );
@ -1581,7 +1597,7 @@ class _DestinationHomeState extends State<DestinationHome> {
child: switch (rightCard){ child: switch (rightCard){
Cards.overview => getOverviewCard(snapshot.data!.summaries!), Cards.overview => getOverviewCard(snapshot.data!.summaries!),
Cards.tetraLeague => switch (cardMod){ Cards.tetraLeague => switch (cardMod){
CardMod.info => getTetraLeagueCard(snapshot.data!.summaries!.league, snapshot.data!.cutoffs), CardMod.info => getTetraLeagueCard(snapshot.data!.summaries!.league, snapshot.data!.cutoffs, snapshot.data!.states),
CardMod.ex => getPreviousSeasonsList(snapshot.data!.summaries!.pastLeague), CardMod.ex => getPreviousSeasonsList(snapshot.data!.summaries!.pastLeague),
CardMod.records => getRecentTLrecords(widget.constraints), CardMod.records => getRecentTLrecords(widget.constraints),
_ => const Center(child: Text("huh?")) _ => const Center(child: Text("huh?"))
@ -2065,12 +2081,10 @@ class BadgesThingy extends StatelessWidget{
icon: Image.asset( icon: Image.asset(
"res/tetrio_badges/${badge.badgeId}.png", "res/tetrio_badges/${badge.badgeId}.png",
height: 32, height: 32,
width: 32,
errorBuilder: (context, error, stackTrace) { errorBuilder: (context, error, stackTrace) {
return Image.network( return Image.network(
kIsWeb ? "https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioBadge&badge=${badge.badgeId}" : "https://tetr.io/res/badges/${badge.badgeId}.png", kIsWeb ? "https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioBadge&badge=${badge.badgeId}" : "https://tetr.io/res/badges/${badge.badgeId}.png",
height: 32, height: 32,
width: 32,
errorBuilder:(context, error, stackTrace) { errorBuilder:(context, error, stackTrace) {
return Image.asset("res/icons/kagari.png", height: 32, width: 32); return Image.asset("res/icons/kagari.png", height: 32, width: 32);
} }
@ -2407,9 +2421,10 @@ class _SearchDrawerState extends State<SearchDrawer> {
class TetraLeagueThingy extends StatelessWidget{ class TetraLeagueThingy extends StatelessWidget{
final TetraLeague league; final TetraLeague league;
final TetraLeague? toCompare;
final Cutoffs? cutoffs; final Cutoffs? cutoffs;
const TetraLeagueThingy({super.key, required this.league, this.cutoffs}); const TetraLeagueThingy({super.key, required this.league, this.toCompare, this.cutoffs});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -2422,8 +2437,8 @@ class TetraLeagueThingy extends StatelessWidget{
tlData: league, tlData: league,
previousRankTRcutoff: cutoffs != null ? cutoffs!.tr[league.rank != "z" ? league.rank : league.percentileRank] : null, previousRankTRcutoff: cutoffs != null ? cutoffs!.tr[league.rank != "z" ? league.rank : league.percentileRank] : null,
nextRankTRcutoff: cutoffs != null ? (league.rank != "z" ? league.rank == "x+" : league.percentileRank == "x+") ? 25000 : cutoffs!.tr[ranks.elementAtOrNull(ranks.indexOf(league.rank != "z" ? league.rank : league.percentileRank)+1)] : null, nextRankTRcutoff: cutoffs != null ? (league.rank != "z" ? league.rank == "x+" : league.percentileRank == "x+") ? 25000 : cutoffs!.tr[ranks.elementAtOrNull(ranks.indexOf(league.rank != "z" ? league.rank : league.percentileRank)+1)] : null,
nextRankTRcutoffTarget: league.rank != "z" ? rankTargets[league.rank] : null, previousRankTRcutoffTarget: league.rank != "z" ? rankTargets[league.rank] : null,
previousRankTRcutoffTarget: (league.rank != "z" && league.rank != "x+") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(league.rank)+1)] : null, nextRankTRcutoffTarget: (league.rank != "z" && league.rank != "x+") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(league.rank)+1)] : null,
previousGlickoCutoff: cutoffs != null ? cutoffs!.glicko[league.rank != "z" ? league.rank : league.percentileRank] : null, previousGlickoCutoff: cutoffs != null ? cutoffs!.glicko[league.rank != "z" ? league.rank : league.percentileRank] : null,
nextRankGlickoCutoff: cutoffs != null ? (league.rank != "z" ? league.rank == "x+" : league.percentileRank == "x+") ? 25000 : cutoffs!.glicko[ranks.elementAtOrNull(ranks.indexOf(league.rank != "z" ? league.rank : league.percentileRank)+1)] : null, nextRankGlickoCutoff: cutoffs != null ? (league.rank != "z" ? league.rank == "x+" : league.percentileRank == "x+") ? 25000 : cutoffs!.glicko[ranks.elementAtOrNull(ranks.indexOf(league.rank != "z" ? league.rank : league.percentileRank)+1)] : null,
), ),
@ -2438,16 +2453,19 @@ class TetraLeagueThingy extends StatelessWidget{
defaultColumnWidth:const IntrinsicColumnWidth(), defaultColumnWidth:const IntrinsicColumnWidth(),
children: [ children: [
TableRow(children: [ TableRow(children: [
const Text("APM: ", style: TextStyle(fontSize: 21)),
Text(f2.format(league.apm??0.00), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), Text(f2.format(league.apm??0.00), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
const Text(" APM", style: TextStyle(fontSize: 21)),
if (toCompare != null) Text(" (${comparef2.format(league.apm!-toCompare!.apm!)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: getDifferenceColor(league.apm!-toCompare!.apm!)))
]), ]),
TableRow(children: [ TableRow(children: [
const Text("PPS: ", style: TextStyle(fontSize: 21)),
Text(f2.format(league.pps??0.00), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), Text(f2.format(league.pps??0.00), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
const Text(" PPS", style: TextStyle(fontSize: 21)),
if (toCompare != null) Text(" (${comparef2.format(league.pps!-toCompare!.pps!)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: getDifferenceColor(league.pps!-toCompare!.pps!)))
]), ]),
TableRow(children: [ TableRow(children: [
const Text("VS: ", style: TextStyle(fontSize: 21)),
Text(f2.format(league.vs??0.00), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), Text(f2.format(league.vs??0.00), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
const Text(" VS", style: TextStyle(fontSize: 21)),
if (toCompare != null) Text(" (${comparef2.format(league.vs!-toCompare!.vs!)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: getDifferenceColor(league.vs!-toCompare!.vs!)))
]) ])
], ],
), ),
@ -2480,6 +2498,10 @@ class TetraLeagueThingy extends StatelessWidget{
GaugeAnnotation(widget: Container(child: GaugeAnnotation(widget: Container(child:
Text(t.statCellNum.winrate, textAlign: TextAlign.center)), Text(t.statCellNum.winrate, textAlign: TextAlign.center)),
angle: 270,positionFactor: 0.4 angle: 270,positionFactor: 0.4
),
if (toCompare != null) GaugeAnnotation(widget: Container(child:
Text(comparef2.format((league.winrate-toCompare!.winrate)*100), textAlign: TextAlign.center, style: TextStyle(color: getDifferenceColor(league.winrate-toCompare!.winrate)))),
angle: 90,positionFactor: 0.45
) )
], ],
) )
@ -2495,17 +2517,20 @@ class TetraLeagueThingy extends StatelessWidget{
TableRow(children: [ TableRow(children: [
//Text("VS: ", style: TextStyle(fontSize: 21)), //Text("VS: ", style: TextStyle(fontSize: 21)),
Text("${league.standingLocal.isNegative ? "---" : intf.format(league.standingLocal)}", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: league.standingLocal.isNegative ? Colors.grey : Colors.white)), Text("${league.standingLocal.isNegative ? "---" : intf.format(league.standingLocal)}", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: league.standingLocal.isNegative ? Colors.grey : Colors.white)),
Text(" local", style: TextStyle(fontSize: 21, color: league.standingLocal.isNegative ? Colors.grey : Colors.white)) Text(" local", style: TextStyle(fontSize: 21, color: league.standingLocal.isNegative ? Colors.grey : Colors.white)),
if (toCompare != null) Text(" (${compareIntf.format(league.standingLocal-toCompare!.standingLocal)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: getDifferenceColor(league.standingLocal-toCompare!.standingLocal)))
]), ]),
TableRow(children: [ TableRow(children: [
//Text("APM: ", style: TextStyle(fontSize: 21)), //Text("APM: ", style: TextStyle(fontSize: 21)),
Text(intf.format(league.gamesPlayed), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), Text(intf.format(league.gamesPlayed), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
const Text(" Games", style: TextStyle(fontSize: 21)) const Text(" Games", style: TextStyle(fontSize: 21)),
if (toCompare != null) Text(" (${comparef2.format(league.gamesPlayed-toCompare!.gamesPlayed)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey))
]), ]),
TableRow(children: [ TableRow(children: [
//Text("PPS: ", style: TextStyle(fontSize: 21)), //Text("PPS: ", style: TextStyle(fontSize: 21)),
Text(intf.format(league.gamesWon), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), Text(intf.format(league.gamesWon), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
const Text(" Won", style: TextStyle(fontSize: 21)) const Text(" Won", style: TextStyle(fontSize: 21)),
if (toCompare != null) Text(" (${comparef2.format(league.gamesWon-toCompare!.gamesWon)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey))
]) ])
], ],
), ),
@ -2521,8 +2546,9 @@ class TetraLeagueThingy extends StatelessWidget{
class NerdStatsThingy extends StatelessWidget{ class NerdStatsThingy extends StatelessWidget{
final NerdStats nerdStats; final NerdStats nerdStats;
final NerdStats? oldNerdStats;
const NerdStatsThingy({super.key, required this.nerdStats}); const NerdStatsThingy({super.key, required this.nerdStats, this.oldNerdStats});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -2530,7 +2556,7 @@ class NerdStatsThingy extends StatelessWidget{
child: Column( child: Column(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.fromLTRB(12.0, 0.0, 12.0, 0.0), padding: const EdgeInsets.fromLTRB(12.0, 8.0, 12.0, 8.0),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -2565,7 +2591,7 @@ class NerdStatsThingy extends StatelessWidget{
children: [ children: [
const TextSpan(text: "APP\n"), const TextSpan(text: "APP\n"),
TextSpan(text: f3.format(nerdStats.app), style: const TextStyle(fontSize: 25, fontFamily: "Eurostile Round Extended", fontWeight: FontWeight.w100)), TextSpan(text: f3.format(nerdStats.app), style: const TextStyle(fontSize: 25, fontFamily: "Eurostile Round Extended", fontWeight: FontWeight.w100)),
//TextSpan(text: "\nAPP"), if (oldNerdStats != null) TextSpan(text: "\n${comparef.format(nerdStats.app - oldNerdStats!.app)}", style: TextStyle(color: getDifferenceColor(nerdStats.app - oldNerdStats!.app))),
] ]
))), ))),
angle: 270,positionFactor: 0.5 angle: 270,positionFactor: 0.5
@ -2595,6 +2621,7 @@ class NerdStatsThingy extends StatelessWidget{
children: [ children: [
const TextSpan(text: "VS/APM\n"), const TextSpan(text: "VS/APM\n"),
TextSpan(text: f3.format(nerdStats.vsapm), style: const TextStyle(fontSize: 25, fontFamily: "Eurostile Round Extended", fontWeight: FontWeight.w100)), TextSpan(text: f3.format(nerdStats.vsapm), style: const TextStyle(fontSize: 25, fontFamily: "Eurostile Round Extended", fontWeight: FontWeight.w100)),
if (oldNerdStats != null) TextSpan(text: "\n${comparef.format(nerdStats.vsapm - oldNerdStats!.vsapm)}", style: TextStyle(color: getDifferenceColor(nerdStats.vsapm - oldNerdStats!.vsapm))),
] ]
))), ))),
angle: 90,positionFactor: 0.5 angle: 90,positionFactor: 0.5
@ -2608,15 +2635,17 @@ class NerdStatsThingy extends StatelessWidget{
Expanded( Expanded(
child: Wrap( child: Wrap(
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
spacing: 10, spacing: 10.0,
runSpacing: 10.0,
runAlignment: WrapAlignment.start,
children: [ children: [
GaugetThingy(value: nerdStats.dss, min: 0, max: 1.0, tickInterval: .2, label: "DS/S", sideSize: 128.0, fractionDigits: 3), GaugetThingy(value: nerdStats.dss, oldValue: oldNerdStats?.dss, min: 0, max: 1.0, tickInterval: .2, label: "DS/S", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true),
GaugetThingy(value: nerdStats.dsp, min: 0, max: 1.0, tickInterval: .2, label: "DS/P", sideSize: 128.0, fractionDigits: 3), GaugetThingy(value: nerdStats.dsp, oldValue: oldNerdStats?.dsp, min: 0, max: 1.0, tickInterval: .2, label: "DS/P", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true),
GaugetThingy(value: nerdStats.appdsp, min: 0, max: 1.2, tickInterval: .2, label: "APP+DS/P", sideSize: 128.0, fractionDigits: 3), GaugetThingy(value: nerdStats.appdsp, oldValue: oldNerdStats?.appdsp, min: 0, max: 1.2, tickInterval: .2, label: "APP+DS/P", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true),
GaugetThingy(value: nerdStats.cheese, min: -80, max: 80, tickInterval: 40, label: "Cheese", sideSize: 128.0, fractionDigits: 2), GaugetThingy(value: nerdStats.cheese, oldValue: oldNerdStats?.cheese, min: -80, max: 80, tickInterval: 40, label: "Cheese", sideSize: 128.0, fractionDigits: 2, moreIsBetter: false),
GaugetThingy(value: nerdStats.gbe, min: 0, max: 1.0, tickInterval: .2, label: "GbE", sideSize: 128.0, fractionDigits: 3), GaugetThingy(value: nerdStats.gbe, oldValue: oldNerdStats?.gbe, min: 0, max: 1.0, tickInterval: .2, label: "GbE", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true),
GaugetThingy(value: nerdStats.nyaapp, min: 0, max: 1.2, tickInterval: .2, label: "wAPP", sideSize: 128.0, fractionDigits: 3), GaugetThingy(value: nerdStats.nyaapp, oldValue: oldNerdStats?.nyaapp, min: 0, max: 1.2, tickInterval: .2, label: "wAPP", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true),
GaugetThingy(value: nerdStats.area, min: 0, max: 1000, tickInterval: 100, label: "Area", sideSize: 128.0, fractionDigits: 1), GaugetThingy(value: nerdStats.area, oldValue: oldNerdStats?.area, min: 0, max: 1000, tickInterval: 100, label: "Area", sideSize: 128.0, fractionDigits: 1, moreIsBetter: true),
], ],
), ),
) )
@ -2667,12 +2696,14 @@ class GaugetThingy extends StatelessWidget{
final double value; final double value;
final double min; final double min;
final double max; final double max;
final double? oldValue;
final bool moreIsBetter;
final double tickInterval; final double tickInterval;
final String label; final String label;
final double sideSize; final double sideSize;
final int fractionDigits; final int fractionDigits;
GaugetThingy({super.key, required this.value, required this.min, required this.max, required this.tickInterval, required this.label, required this.sideSize, required this.fractionDigits}); GaugetThingy({super.key, required this.value, required this.min, required this.max, this.oldValue, required this.tickInterval, required this.label, required this.sideSize, required this.fractionDigits, required this.moreIsBetter});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -2704,6 +2735,10 @@ class GaugetThingy extends StatelessWidget{
GaugeAnnotation(widget: Container(child: GaugeAnnotation(widget: Container(child:
Text(label, textAlign: TextAlign.center, style: const TextStyle(height: .9))), Text(label, textAlign: TextAlign.center, style: const TextStyle(height: .9))),
angle: 270,positionFactor: 0.4 angle: 270,positionFactor: 0.4
),
if (oldValue != null) GaugeAnnotation(widget: Container(child:
Text(comparef2.format(value-oldValue!), textAlign: TextAlign.center, style: TextStyle(color: getDifferenceColor(moreIsBetter ? value-oldValue! : oldValue!-value)))),
angle: 90,positionFactor: 0.45
) )
], ],
) )

View File

@ -166,17 +166,17 @@ class MyRadarChartPainter extends RadarChartPainter{
); );
} }
_ticksTextPaint // _ticksTextPaint
..text = TextSpan( // ..text = TextSpan(
text: percentage.format(tick), // text: percentage.format(tick),
style: Utils().getThemeAwareTextStyle(context, data.ticksTextStyle), // style: Utils().getThemeAwareTextStyle(context, data.ticksTextStyle),
) // )
..textDirection = TextDirection.ltr // ..textDirection = TextDirection.ltr
..layout(maxWidth: size.width); // ..layout(maxWidth: size.width);
canvasWrapper.drawText( // canvasWrapper.drawText(
_ticksTextPaint, // _ticksTextPaint,
Offset(centerX + 5, centerY - tickRadius - _ticksTextPaint.height/2), // Offset(centerX + 5, centerY - tickRadius - _ticksTextPaint.height/2),
); // );
}, },
); );
} }
@ -302,12 +302,12 @@ class Graphs extends StatelessWidget{
width: 310, width: 310,
child: MyRadarChart( child: MyRadarChart(
RadarChartData( RadarChartData(
radarShape: RadarShape.polygon, radarShape: RadarShape.circle,
tickCount: 4, tickCount: 4,
ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10), radarBackgroundColor: Colors.black.withAlpha(170),
radarBorderData: const BorderSide(color: Colors.transparent, width: 1), radarBorderData: const BorderSide(color: Colors.white24, width: 1),
gridBorderData: const BorderSide(color: Colors.white24, width: 1), gridBorderData: const BorderSide(color: Colors.white24, width: 1),
tickBorderData: const BorderSide(color: Colors.transparent, width: 1), tickBorderData: const BorderSide(color: Colors.white24, width: 1),
getTitle: (index, angle) { getTitle: (index, angle) {
switch (index) { switch (index) {
case 0: case 0:
@ -336,7 +336,7 @@ class Graphs extends StatelessWidget{
}, },
dataSets: [ dataSets: [
RadarDataSet( RadarDataSet(
fillColor: Theme.of(context).colorScheme.primary.withAlpha(100), fillColor: Theme.of(context).colorScheme.primary.withAlpha(170),
borderColor: Theme.of(context).colorScheme.primary, borderColor: Theme.of(context).colorScheme.primary,
dataEntries: [ dataEntries: [
RadarEntry(value: apm * apmWeight), RadarEntry(value: apm * apmWeight),
@ -381,12 +381,12 @@ class Graphs extends StatelessWidget{
width: 310, width: 310,
child: MyRadarChart( child: MyRadarChart(
RadarChartData( RadarChartData(
radarShape: RadarShape.polygon, radarShape: RadarShape.circle,
tickCount: 4, tickCount: 4,
ticksTextStyle: const TextStyle(color: Colors.white24, fontSize: 10), radarBackgroundColor: Colors.black.withAlpha(170),
radarBorderData: const BorderSide(color: Colors.transparent, width: 1), radarBorderData: const BorderSide(color: Colors.white24, width: 1),
gridBorderData: const BorderSide(color: Colors.white24, width: 1), gridBorderData: const BorderSide(color: Colors.white24, width: 1),
tickBorderData: const BorderSide(color: Colors.transparent, width: 1), tickBorderData: const BorderSide(color: Colors.white24, width: 1),
titleTextStyle: const TextStyle(height: 1.1), titleTextStyle: const TextStyle(height: 1.1),
radarTouchData: RadarTouchData(), radarTouchData: RadarTouchData(),
getTitle: (index, angle) { getTitle: (index, angle) {
@ -405,7 +405,7 @@ class Graphs extends StatelessWidget{
}, },
dataSets: [ dataSets: [
RadarDataSet( RadarDataSet(
fillColor: Theme.of(context).colorScheme.primary.withAlpha(100), fillColor: Theme.of(context).colorScheme.primary.withAlpha(170),
borderColor: Theme.of(context).colorScheme.primary, borderColor: Theme.of(context).colorScheme.primary,
dataEntries: [ dataEntries: [
RadarEntry(value: playstyle.opener), RadarEntry(value: playstyle.opener),
@ -438,12 +438,12 @@ class Graphs extends StatelessWidget{
width: 310, width: 310,
child: MyRadarChart( child: MyRadarChart(
RadarChartData( RadarChartData(
radarShape: RadarShape.polygon, radarShape: RadarShape.circle,
tickCount: 4, tickCount: 4,
ticksTextStyle: const TextStyle(color: Colors.white24, fontSize: 10), radarBackgroundColor: Colors.black.withAlpha(170),
radarBorderData: const BorderSide(color: Colors.transparent, width: 1), radarBorderData: const BorderSide(color: Colors.white24, width: 1),
gridBorderData: const BorderSide(color: Colors.white24, width: 1), gridBorderData: const BorderSide(color: Colors.white24, width: 1),
tickBorderData: const BorderSide(color: Colors.transparent, width: 1), tickBorderData: const BorderSide(color: Colors.white24, width: 1),
titleTextStyle: const TextStyle(height: 1.1), titleTextStyle: const TextStyle(height: 1.1),
radarTouchData: RadarTouchData(), radarTouchData: RadarTouchData(),
getTitle: (index, angle) { getTitle: (index, angle) {
@ -462,7 +462,7 @@ class Graphs extends StatelessWidget{
}, },
dataSets: [ dataSets: [
RadarDataSet( RadarDataSet(
fillColor: Theme.of(context).colorScheme.primary.withAlpha(100), fillColor: Theme.of(context).colorScheme.primary.withAlpha(170),
borderColor: Theme.of(context).colorScheme.primary, borderColor: Theme.of(context).colorScheme.primary,
dataEntries: [ dataEntries: [
RadarEntry(value: attack), RadarEntry(value: attack),