redesign 10% ready (but only for desktop :tf:)

This commit is contained in:
dan63047 2024-08-14 01:45:28 +03:00
parent b3fd96e58c
commit 6d950d30da
9 changed files with 366 additions and 91 deletions

View File

@ -546,6 +546,8 @@ class Clears {
late int tSpinMiniZeros; late int tSpinMiniZeros;
late int tSpinMiniSingles; late int tSpinMiniSingles;
late int tSpinMiniDoubles; late int tSpinMiniDoubles;
late int tSpinMiniTriples;
late int tSpinMiniQuads;
Clears( Clears(
{required this.singles, {required this.singles,
@ -562,7 +564,9 @@ class Clears {
required this.tSpinQuads, required this.tSpinQuads,
required this.tSpinMiniZeros, required this.tSpinMiniZeros,
required this.tSpinMiniSingles, required this.tSpinMiniSingles,
required this.tSpinMiniDoubles}); required this.tSpinMiniDoubles,
required this.tSpinMiniTriples,
required this.tSpinMiniQuads});
Clears.fromJson(Map<String, dynamic> json) { Clears.fromJson(Map<String, dynamic> json) {
singles = json['singles']; singles = json['singles'];
@ -576,7 +580,9 @@ class Clears {
tSpinSingles = json['tspinsingles']; tSpinSingles = json['tspinsingles'];
tSpinMiniDoubles = json['minitspindoubles']; tSpinMiniDoubles = json['minitspindoubles'];
tSpinDoubles = json['tspindoubles']; tSpinDoubles = json['tspindoubles'];
tSpinMiniTriples = json['minitspintriples']??0;
tSpinTriples = json['tspintriples']; tSpinTriples = json['tspintriples'];
tSpinMiniQuads = json['minitspinquads']??0;
tSpinQuads = json['tspinquads']; tSpinQuads = json['tspinquads'];
tSpinPentas = json['tspinpentas']??0; tSpinPentas = json['tspinpentas']??0;
allClears = json['allclear']; allClears = json['allclear'];
@ -598,7 +604,9 @@ class Clears {
tSpinQuads: tSpinQuads + other.tSpinQuads, tSpinQuads: tSpinQuads + other.tSpinQuads,
tSpinMiniZeros: tSpinMiniZeros + other.tSpinMiniZeros, tSpinMiniZeros: tSpinMiniZeros + other.tSpinMiniZeros,
tSpinMiniSingles: tSpinMiniSingles + other.tSpinMiniSingles, tSpinMiniSingles: tSpinMiniSingles + other.tSpinMiniSingles,
tSpinMiniDoubles: tSpinMiniDoubles + other.tSpinMiniDoubles tSpinMiniDoubles: tSpinMiniDoubles + other.tSpinMiniDoubles,
tSpinMiniTriples: tSpinMiniTriples + other.tSpinMiniTriples,
tSpinMiniQuads: tSpinMiniQuads + other.tSpinMiniQuads
); );
} }

View File

@ -134,7 +134,7 @@ class ReplayStats{
topSpike = 0; topSpike = 0;
tspins = 0; tspins = 0;
roundLength = 0.0; roundLength = 0.0;
clears = Clears(singles: 0, doubles: 0, triples: 0, quads: 0, pentas: 0, allClears: 0, tSpinZeros: 0, tSpinSingles: 0, tSpinDoubles: 0, tSpinTriples: 0, tSpinPentas: 0, tSpinQuads: 0, tSpinMiniZeros: 0, tSpinMiniSingles: 0, tSpinMiniDoubles: 0); clears = Clears(singles: 0, doubles: 0, triples: 0, quads: 0, pentas: 0, allClears: 0, tSpinZeros: 0, tSpinSingles: 0, tSpinDoubles: 0, tSpinTriples: 0, tSpinPentas: 0, tSpinQuads: 0, tSpinMiniZeros: 0, tSpinMiniSingles: 0, tSpinMiniDoubles: 0, tSpinMiniTriples: 0, tSpinMiniQuads: 0);
garbage = Garbage(sent: 0, recived: 0, attack: 0, cleared: 0); garbage = Garbage(sent: 0, recived: 0, attack: 0, cleared: 0);
finesse = Finesse(combo: 0, faults: 0, perfectPieces: 0); finesse = Finesse(combo: 0, faults: 0, perfectPieces: 0);
} }

View File

@ -1191,7 +1191,7 @@ class _TwoRecordsThingy extends StatelessWidget {
title: Text(get40lTime(sprintStream.records[i].stats.finalTime.inMicroseconds), title: Text(get40lTime(sprintStream.records[i].stats.finalTime.inMicroseconds),
style: const TextStyle(fontSize: 18)), style: const TextStyle(fontSize: 18)),
subtitle: Text(timestamp(sprintStream.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)), subtitle: Text(timestamp(sprintStream.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
trailing: SpTrailingStats(sprintStream.records[i].stats, sprintStream.records[i].gamemode) trailing: SpTrailingStats(sprintStream.records[i], sprintStream.records[i].gamemode)
) )
], ],
), ),
@ -1277,7 +1277,7 @@ class _TwoRecordsThingy extends StatelessWidget {
title: Text("${NumberFormat.decimalPattern().format(blitzStream.records[i].stats.score)} points", title: Text("${NumberFormat.decimalPattern().format(blitzStream.records[i].stats.score)} points",
style: const TextStyle(fontSize: 18)), style: const TextStyle(fontSize: 18)),
subtitle: Text(timestamp(blitzStream.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)), subtitle: Text(timestamp(blitzStream.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
trailing: SpTrailingStats(blitzStream.records[i].stats, blitzStream.records[i].gamemode) trailing: SpTrailingStats(blitzStream.records[i], blitzStream.records[i].gamemode)
) )
], ],
), ),

View File

@ -12,9 +12,13 @@ import 'package:tetra_stats/utils/colors_functions.dart';
import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/utils/numers_formats.dart';
import 'package:tetra_stats/utils/relative_timestamps.dart'; import 'package:tetra_stats/utils/relative_timestamps.dart';
import 'package:tetra_stats/utils/text_shadow.dart'; import 'package:tetra_stats/utils/text_shadow.dart';
import 'package:tetra_stats/views/singleplayer_record_view.dart';
import 'package:tetra_stats/views/tl_match_view.dart'; import 'package:tetra_stats/views/tl_match_view.dart';
import 'package:tetra_stats/widgets/finesse_thingy.dart';
import 'package:tetra_stats/widgets/graphs.dart'; import 'package:tetra_stats/widgets/graphs.dart';
import 'package:tetra_stats/widgets/lineclears_thingy.dart';
import 'package:tetra_stats/widgets/list_tile_trailing_stats.dart'; import 'package:tetra_stats/widgets/list_tile_trailing_stats.dart';
import 'package:tetra_stats/widgets/sp_trailing_stats.dart';
import 'package:tetra_stats/widgets/stat_sell_num.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart';
import 'package:tetra_stats/widgets/text_timestamp.dart'; import 'package:tetra_stats/widgets/text_timestamp.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
@ -577,9 +581,9 @@ class _DestinationHomeState extends State<DestinationHome> {
CardMod cardMod = CardMod.info; CardMod cardMod = CardMod.info;
Duration postSeasonLeft = seasonStart.difference(DateTime.now()); Duration postSeasonLeft = seasonStart.difference(DateTime.now());
late Map<Cards, List<ButtonSegment<CardMod>>> modeButtons; late Map<Cards, List<ButtonSegment<CardMod>>> modeButtons;
late MapEntry closestAverageBlitz; late MapEntry? closestAverageBlitz;
late bool blitzBetterThanClosestAverage; late bool blitzBetterThanClosestAverage;
late MapEntry closestAverageSprint; late MapEntry? closestAverageSprint;
late bool sprintBetterThanClosestAverage; late bool sprintBetterThanClosestAverage;
bool? sprintBetterThanRankAverage; bool? sprintBetterThanRankAverage;
bool? blitzBetterThanRankAverage; bool? blitzBetterThanRankAverage;
@ -656,7 +660,7 @@ class _DestinationHomeState extends State<DestinationHome> {
); );
} }
Widget getRecentTLrecords(String searchFor, BoxConstraints constraints){ Widget getListOfRecords(String stream, bool isTop, BoxConstraints constraints){
return Column( return Column(
children: [ children: [
Card( Card(
@ -674,8 +678,9 @@ class _DestinationHomeState extends State<DestinationHome> {
), ),
), ),
Card( Card(
child: FutureBuilder<TetraLeagueBetaStream>( clipBehavior: Clip.antiAlias,
future: teto.fetchTLStream(searchFor), child: FutureBuilder<SingleplayerStream>(
future: teto.fetchStream(widget.searchFor, stream),
builder: (context, snapshot) { builder: (context, snapshot) {
switch (snapshot.connectionState){ switch (snapshot.connectionState){
case ConnectionState.none: case ConnectionState.none:
@ -684,7 +689,90 @@ class _DestinationHomeState extends State<DestinationHome> {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
case ConnectionState.done: case ConnectionState.done:
if (snapshot.hasData){ if (snapshot.hasData){
return SizedBox(height: constraints.maxHeight, child: _TLRecords(userID: searchFor, changePlayer: (){}, data: snapshot.data!.records, wasActiveInTL: snapshot.data!.records.isNotEmpty, oldMathcesHere: false)); return Column(
children: [
for (int i = 0; i < snapshot.data!.records.length; i++) ListTile(
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: snapshot.data!.records[i]))),
leading: Text(
isTop ? "#${i+1}" : switch (snapshot.data!.records[i].gamemode){
"40l" => "40L",
"blitz" => "BLZ",
"5mblast" => "5MB",
"zenith" => "QP",
"zenithex" => "QPE",
String() => "huh",
},
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9)
),
title: Text(
switch (snapshot.data!.records[i].gamemode){
"40l" => get40lTime(snapshot.data!.records[i].stats.finalTime.inMicroseconds),
"blitz" => t.blitzScore(p: NumberFormat.decimalPattern().format(snapshot.data!.records[i].stats.score)),
"5mblast" => get40lTime(snapshot.data!.records[i].stats.finalTime.inMicroseconds),
"zenith" => "${f2.format(snapshot.data!.records[i].stats.zenith!.altitude)} m${(snapshot.data!.records[i].extras as ZenithExtras).mods.isNotEmpty ? " (${t.withModsPlural(n: (snapshot.data!.records[i].extras as ZenithExtras).mods.length)})" : ""}",
"zenithex" => "${f2.format(snapshot.data!.records[i].stats.zenith!.altitude)} m${(snapshot.data!.records[i].extras as ZenithExtras).mods.isNotEmpty ? " (${t.withModsPlural(n: (snapshot.data!.records[i].extras as ZenithExtras).mods.length)})" : ""}",
String() => "huh",
},
style: const TextStyle(fontSize: 18)),
subtitle: Text(timestamp(snapshot.data!.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
trailing: SpTrailingStats(snapshot.data!.records[i], snapshot.data!.records[i].gamemode)
)
],
);
}
if (snapshot.hasError){
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(t.errors.noSuchUser, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 42, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(t.errors.noSuchUserSub, textAlign: TextAlign.center),
),
],
)
);
}
}
return Text("what?");
},
),
),
],
);
}
Widget getRecentTLrecords(BoxConstraints constraints){
return Column(
children: [
Card(
child: Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(t.recent, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42)),
],
),
),
),
),
Card(
clipBehavior: Clip.antiAlias,
child: FutureBuilder<TetraLeagueBetaStream>(
future: teto.fetchTLStream(widget.searchFor),
builder: (context, snapshot) {
switch (snapshot.connectionState){
case ConnectionState.none:
case ConnectionState.waiting:
case ConnectionState.active:
return const Center(child: CircularProgressIndicator());
case ConnectionState.done:
if (snapshot.hasData){
return SizedBox(height: constraints.maxHeight - 145, child: _TLRecords(userID: widget.searchFor, changePlayer: (){}, data: snapshot.data!.records, wasActiveInTL: snapshot.data!.records.isNotEmpty, oldMathcesHere: false));
} }
if (snapshot.hasError){ if (snapshot.hasError){
return Center( return Center(
@ -728,6 +816,78 @@ class _DestinationHomeState extends State<DestinationHome> {
), ),
), ),
ZenithThingy(zenith: record), ZenithThingy(zenith: record),
if (record != null) Row(
children: [
Expanded(
child: Card(
child: Column(
children: [
FinesseThingy(record.stats.finesse, record.stats.finessePercentage),
LineclearsThingy(record.stats.clears, record.stats.lines, record.stats.holds, record.stats.tSpins, showMoreClears: true),
if (record.gamemode == 'blitz') Text("${f2.format(record.stats.kpp)} KPP")
],
),
),
),
Expanded(
child: Card(
child: SizedBox(
width: 300,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Stack(
alignment: AlignmentDirectional.bottomStart,
children: [
const Text("T", style: TextStyle(
fontStyle: FontStyle.italic,
fontSize: 65,
height: 1.2,
)),
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(
shadows: textShadow,
fontFamily: "Eurostile Round Extended",
fontSize: 36,
fontWeight: FontWeight.w500,
color: Colors.white
)),
)
],
),
SizedBox(
width: 300.0,
child: Table(
columnWidths: const {
0: FixedColumnWidth(36)
},
children: [
const TableRow(
children: [
Text("Floor"),
Text("Split", textAlign: TextAlign.right),
Text("Total", textAlign: TextAlign.right),
]
),
for (int i = 0; i < record!.stats.zenith!.splits.length; i++) TableRow(
children: [
Text((i+1).toString()),
Text(record!.stats.zenith!.splits[i] != Duration.zero ? getMoreNormalTime(record!.stats.zenith!.splits[i]-(i-1 != -1 ? record!.stats.zenith!.splits[i-1] : Duration.zero)) : "--:--.---", textAlign: TextAlign.right),
Text(record!.stats.zenith!.splits[i] != Duration.zero ? getMoreNormalTime(record!.stats.zenith!.splits[i]) : "--:--.---", textAlign: TextAlign.right),
]
)
],
),
),
],
),
),
),
),
],
),
if (record != null) Card( if (record != null) Card(
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -745,6 +905,11 @@ class _DestinationHomeState extends State<DestinationHome> {
} }
Widget getRecordCard(RecordSingle? record, bool? betterThanRankAverage, MapEntry? closestAverage, bool? betterThanClosestAverage, String? rank){ Widget getRecordCard(RecordSingle? record, bool? betterThanRankAverage, MapEntry? closestAverage, bool? betterThanClosestAverage, String? rank){
if (record == null) {
return Card(
child: Center(child: Text("No record", style: const TextStyle(fontSize: 42))),
);
}
return Column( return Column(
children: [ children: [
Card( Card(
@ -755,7 +920,7 @@ class _DestinationHomeState extends State<DestinationHome> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text(switch(record!.gamemode){ Text(switch(record.gamemode){
"40l" => t.sprint, "40l" => t.sprint,
"blitz" => t.blitz, "blitz" => t.blitz,
"5mblast" => "5,000,000 Blast", "5mblast" => "5,000,000 Blast",
@ -767,57 +932,62 @@ class _DestinationHomeState extends State<DestinationHome> {
), ),
), ),
Card( Card(
child: Padding( child: Column(
padding: const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 0.0), children: [
child: Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
if (closestAverage != null) Padding(padding: const EdgeInsets.only(right: 8.0), if (closestAverage != null) Padding(padding: const EdgeInsets.only(right: 8.0),
child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverage.key}.png", height: 96) child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverage.key}.png", height: 96)
), ),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
RichText(text: TextSpan( RichText(text: TextSpan(
text: switch(record.gamemode){ text: switch(record.gamemode){
"40l" => get40lTime(record.stats.finalTime.inMicroseconds), "40l" => get40lTime(record.stats.finalTime.inMicroseconds),
"blitz" => NumberFormat.decimalPattern().format(record.stats.score), "blitz" => NumberFormat.decimalPattern().format(record.stats.score),
"5mblast" => get40lTime(record.stats.finalTime.inMicroseconds), "5mblast" => get40lTime(record.stats.finalTime.inMicroseconds),
_ => record.stats.score.toString() _ => record.stats.score.toString()
}, },
style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white), style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white),
),
),
RichText(text: TextSpan(
text: "",
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
children: [
if (rank != null && rank != "z") TextSpan(text: "${t.verdictGeneral(n: switch(record.gamemode){
"40l" => readableTimeDifference(record.stats.finalTime, sprintAverages[rank]!),
"blitz" => readableIntDifference(record.stats.score, blitzAverages[rank]!),
_ => record.stats.score.toString()
}, verdict: betterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank.toUpperCase())}\n", style: TextStyle(
color: betterThanClosestAverage??false ? Colors.greenAccent : Colors.redAccent
))
else if ((rank == null || rank == "z") && closestAverage != null) TextSpan(text: "${t.verdictGeneral(n: switch(record.gamemode){
"40l" => readableTimeDifference(record.stats.finalTime, closestAverage.value),
"blitz" => readableIntDifference(record.stats.score, closestAverage.value),
_ => record.stats.score.toString()
}, verdict: betterThanClosestAverage??false ? t.verdictBetter : t.verdictWorse, rank: closestAverage.key.toUpperCase())}\n", style: TextStyle(
color: betterThanClosestAverage??false ? Colors.greenAccent : Colors.redAccent
)),
if (record.rank != -1) TextSpan(text: "${intf.format(record.rank)}", style: TextStyle(color: getColorOfRank(record.rank))),
if (record.rank != -1) const TextSpan(text: ""),
if (record.countryRank != -1) TextSpan(text: "${intf.format(record.countryRank)} local", style: TextStyle(color: getColorOfRank(record.countryRank))),
if (record.countryRank != -1) const TextSpan(text: ""),
TextSpan(text: timestamp(record.timestamp)),
]
), ),
), ),
RichText(text: TextSpan( ],
text: "", ),
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey), ],
children: [ ),
if (rank != null && rank != "z") TextSpan(text: "${t.verdictGeneral(n: switch(record.gamemode){ Row(
"40l" => readableTimeDifference(record.stats.finalTime, sprintAverages[rank]!), children: [
"blitz" => readableIntDifference(record.stats.score, blitzAverages[rank]!), Expanded(
_ => record.stats.score.toString() child: Table(
}, verdict: betterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank.toUpperCase())}\n", style: TextStyle(
color: betterThanClosestAverage??false ? Colors.greenAccent : Colors.redAccent
))
else if ((rank == null || rank == "z") && closestAverage != null) TextSpan(text: "${t.verdictGeneral(n: switch(record.gamemode){
"40l" => readableTimeDifference(record.stats.finalTime, closestAverage.value),
"blitz" => readableIntDifference(record.stats.score, closestAverage.value),
_ => record.stats.score.toString()
}, verdict: sprintBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageSprint.key.toUpperCase())}\n", style: TextStyle(
color: betterThanClosestAverage??false ? Colors.greenAccent : Colors.redAccent
)),
if (record.rank != -1) TextSpan(text: "${intf.format(record.rank)}", style: TextStyle(color: getColorOfRank(record.rank))),
if (record.rank != -1) const TextSpan(text: ""),
if (record.countryRank != -1) TextSpan(text: "${intf.format(record.countryRank)} local", style: TextStyle(color: getColorOfRank(record.countryRank))),
if (record.countryRank != -1) const TextSpan(text: ""),
TextSpan(text: timestamp(record.timestamp)),
]
),
),
],),
Spacer(),
Table(
defaultColumnWidth:const IntrinsicColumnWidth(), defaultColumnWidth:const IntrinsicColumnWidth(),
children: [ children: [
TableRow(children: [ TableRow(children: [
@ -854,25 +1024,58 @@ class _DestinationHomeState extends State<DestinationHome> {
]) ])
], ],
), ),
),
Expanded(
child: Table(
defaultColumnWidth:const IntrinsicColumnWidth(),
children: [
TableRow(children: [
Text(intf.format(record.stats.inputs), textAlign: TextAlign.right, style: TextStyle(fontSize: 21)),
Text(" Key presses", textAlign: TextAlign.left, style: const TextStyle(fontSize: 21)),
]),
TableRow(children: [
Text(f2.format(record.stats.kps), textAlign: TextAlign.right, style: TextStyle(fontSize: 21)),
Text(" KPS", textAlign: TextAlign.left, style: const TextStyle(fontSize: 21)),
]),
TableRow(children: [
Text(switch(record.gamemode){
"40l" => " ",
"blitz" => record.stats.piecesPlaced.toString(),
"5mblast" => record.stats.piecesPlaced.toString(),
_ => "but god said"
}, textAlign: TextAlign.right, style: TextStyle(fontSize: 21)),
Text(switch(record.gamemode){
"40l" => " ",
"blitz" => " Pieces",
"5mblast" => " Pieces",
_ => " no"
}, textAlign: TextAlign.left, style: const TextStyle(fontSize: 21)),
])
],
),
),
],
)
],
),
),
Card(
child: Center(
child: Column(
children: [
FinesseThingy(record.stats.finesse, record.stats.finessePercentage),
LineclearsThingy(record.stats.clears, record.stats.lines, record.stats.holds, record.stats.tSpins),
if (record.gamemode == 'blitz') Text("${f2.format(record.stats.kpp)} KPP")
], ],
), ),
), ),
), )
] ]
); );
} }
@override @override
initState(){ initState(){
// bool? blitzBetterThanRankAverage = (rank != null && rank != "z") ? record!.stats.score > blitzAverages[rank]! : null;
// bool? sprintBetterThanRankAverage = (rank != null && rank != "z") ? record!.stats.finalTime < sprintAverages[rank]! : null;
// if (record!.gamemode == "40l") {
// closestAverageSprint = sprintAverages.entries.singleWhere((element) => element.value == sprintAverages.values.reduce((a, b) => (a-record!.stats.finalTime).abs() < (b -record!.stats.finalTime).abs() ? a : b));
// sprintBetterThanClosestAverage = record!.stats.finalTime < closestAverageSprint.value;
// }else if (record!.gamemode == "blitz"){
// closestAverageBlitz = blitzAverages.entries.singleWhere((element) => element.value == blitzAverages.values.reduce((a, b) => (a-record!.stats.score).abs() < (b -record!.stats.score).abs() ? a : b));
// blitzBetterThanClosestAverage = record!.stats.score > closestAverageBlitz.value;
// }
modeButtons = { modeButtons = {
Cards.overview: [ Cards.overview: [
const ButtonSegment<CardMod>( const ButtonSegment<CardMod>(
@ -1071,22 +1274,40 @@ class _DestinationHomeState extends State<DestinationHome> {
sprintBetterThanRankAverage = (snapshot.data!.league.rank != "z" && snapshot.data!.sprint != null) ? snapshot.data!.sprint!.stats.finalTime < sprintAverages[snapshot.data!.league.rank]! : null; sprintBetterThanRankAverage = (snapshot.data!.league.rank != "z" && snapshot.data!.sprint != null) ? snapshot.data!.sprint!.stats.finalTime < sprintAverages[snapshot.data!.league.rank]! : null;
if (snapshot.data!.sprint != null) { if (snapshot.data!.sprint != null) {
closestAverageSprint = sprintAverages.entries.singleWhere((element) => element.value == sprintAverages.values.reduce((a, b) => (a-snapshot.data!.sprint!.stats.finalTime).abs() < (b -snapshot.data!.sprint!.stats.finalTime).abs() ? a : b)); closestAverageSprint = sprintAverages.entries.singleWhere((element) => element.value == sprintAverages.values.reduce((a, b) => (a-snapshot.data!.sprint!.stats.finalTime).abs() < (b -snapshot.data!.sprint!.stats.finalTime).abs() ? a : b));
sprintBetterThanClosestAverage = snapshot.data!.sprint!.stats.finalTime < closestAverageSprint.value; sprintBetterThanClosestAverage = snapshot.data!.sprint!.stats.finalTime < closestAverageSprint!.value;
} }
if (snapshot.data!.blitz != null){ if (snapshot.data!.blitz != null){
closestAverageBlitz = blitzAverages.entries.singleWhere((element) => element.value == blitzAverages.values.reduce((a, b) => (a-snapshot.data!.blitz!.stats.score).abs() < (b -snapshot.data!.blitz!.stats.score).abs() ? a : b)); closestAverageBlitz = blitzAverages.entries.singleWhere((element) => element.value == blitzAverages.values.reduce((a, b) => (a-snapshot.data!.blitz!.stats.score).abs() < (b -snapshot.data!.blitz!.stats.score).abs() ? a : b));
blitzBetterThanClosestAverage = snapshot.data!.blitz!.stats.score > closestAverageBlitz.value; blitzBetterThanClosestAverage = snapshot.data!.blitz!.stats.score > closestAverageBlitz!.value;
} }
return switch (rightCard){ return switch (rightCard){
Cards.overview => getOverviewCard(snapshot.data!), Cards.overview => getOverviewCard(snapshot.data!),
Cards.tetraLeague => switch (cardMod){ Cards.tetraLeague => switch (cardMod){
CardMod.info => getTetraLeagueCard(snapshot.data!.league), CardMod.info => getTetraLeagueCard(snapshot.data!.league),
CardMod.recent => getRecentTLrecords(widget.searchFor, widget.constraints), CardMod.recent => getRecentTLrecords(widget.constraints),
_ => Center(child: Text("huh?"))
},
Cards.quickPlay => switch (cardMod){
CardMod.info => getZenithCard(snapshot.data?.zenith),
CardMod.recent => getListOfRecords("zenith/recent", false, widget.constraints),
CardMod.top => getListOfRecords("zenith/top", true, widget.constraints),
CardMod.ex => getZenithCard(snapshot.data?.zenithEx),
CardMod.exRecent => getListOfRecords("zenithex/recent", false, widget.constraints),
CardMod.exTop => getListOfRecords("zenithex/top", true, widget.constraints),
_ => Center(child: Text("huh?"))
},
Cards.sprint => switch (cardMod){
CardMod.info => getRecordCard(snapshot.data?.sprint, sprintBetterThanRankAverage, closestAverageSprint, sprintBetterThanClosestAverage, snapshot.data!.league.rank),
CardMod.recent => getListOfRecords("40l/recent", false, widget.constraints),
CardMod.top => getListOfRecords("40l/top", true, widget.constraints),
_ => Center(child: Text("huh?"))
},
Cards.blitz => switch (cardMod){
CardMod.info => getRecordCard(snapshot.data?.blitz, blitzBetterThanRankAverage, closestAverageBlitz, blitzBetterThanClosestAverage, snapshot.data!.league.rank),
CardMod.recent => getListOfRecords("blitz/recent", false, widget.constraints),
CardMod.top => getListOfRecords("blitz/top", true, widget.constraints),
_ => Center(child: Text("huh?")) _ => Center(child: Text("huh?"))
}, },
Cards.quickPlay => getZenithCard(cardMod == CardMod.ex ? snapshot.data?.zenithEx : snapshot.data?.zenith),
Cards.sprint => getRecordCard(snapshot.data?.sprint, sprintBetterThanRankAverage, closestAverageSprint, sprintBetterThanClosestAverage, snapshot.data!.league.rank),
Cards.blitz => getRecordCard(snapshot.data?.blitz, blitzBetterThanRankAverage, closestAverageBlitz, blitzBetterThanClosestAverage, snapshot.data!.league.rank),
}; };
} }
if (snapshot.hasError){ if (snapshot.hasError){
@ -2225,9 +2446,9 @@ class ZenithThingy extends StatelessWidget{
text: "", text: "",
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
children: [ children: [
if (zenith!.rank != -1) TextSpan(text: "${zenith!.rank}", style: TextStyle(color: getColorOfRank(zenith!.rank))), if (zenith!.rank != -1) TextSpan(text: " ${intf.format(zenith!.rank)}", style: TextStyle(color: getColorOfRank(zenith!.rank))),
if (zenith!.rank != -1) const TextSpan(text: ""), if (zenith!.rank != -1) const TextSpan(text: ""),
if (zenith!.countryRank != -1) TextSpan(text: "${zenith!.countryRank} local", style: TextStyle(color: getColorOfRank(zenith!.countryRank))), if (zenith!.countryRank != -1) TextSpan(text: " ${intf.format(zenith!.countryRank)} local", style: TextStyle(color: getColorOfRank(zenith!.countryRank))),
if (zenith!.countryRank != -1) const TextSpan(text: ""), if (zenith!.countryRank != -1) const TextSpan(text: ""),
TextSpan(text: timestamp(zenith!.timestamp)), TextSpan(text: timestamp(zenith!.timestamp)),
] ]

View File

@ -7,8 +7,9 @@ class LineclearsThingy extends StatelessWidget{
final int lines; final int lines;
final int holds; final int holds;
final int tSpins; final int tSpins;
final bool showMoreClears;
const LineclearsThingy(this.clears, this.lines, this.holds, this.tSpins, {super.key}); const LineclearsThingy(this.clears, this.lines, this.holds, this.tSpins, {super.key, this.showMoreClears = false});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -21,6 +22,7 @@ class LineclearsThingy extends StatelessWidget{
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text(t.numOfGameActions.lineClears(n: lines), style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center), Text(t.numOfGameActions.lineClears(n: lines), style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center),
if (showMoreClears) Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Pentas"), Text(clears.pentas.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Quads"), Text(clears.quads.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Quads"), Text(clears.quads.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Triples"), Text(clears.triples.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Triples"), Text(clears.triples.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Doubles"), Text(clears.doubles.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Doubles"), Text(clears.doubles.toString())]),
@ -36,10 +38,14 @@ class LineclearsThingy extends StatelessWidget{
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text(t.numOfGameActions.tspinsTotal(n: tSpins), style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center), Text(t.numOfGameActions.tspinsTotal(n: tSpins), style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center),
if (showMoreClears) Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spin pentas"), Text(clears.tSpinPentas.toString())]),
if (showMoreClears) Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spin quads"), Text(clears.tSpinQuads.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins triples"), Text(clears.tSpinTriples.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins triples"), Text(clears.tSpinTriples.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins doubles"), Text(clears.tSpinDoubles.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins doubles"), Text(clears.tSpinDoubles.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins singles"), Text(clears.tSpinSingles.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins singles"), Text(clears.tSpinSingles.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins zeros"), Text(clears.tSpinZeros.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins zeros"), Text(clears.tSpinZeros.toString())]),
if (showMoreClears) Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins quads"), Text(clears.tSpinMiniQuads.toString())]),
if (showMoreClears) Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins triples"), Text(clears.tSpinMiniTriples.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins doubles"), Text(clears.tSpinMiniDoubles.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins doubles"), Text(clears.tSpinMiniDoubles.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins singles"), Text(clears.tSpinMiniSingles.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins singles"), Text(clears.tSpinMiniSingles.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins zeros"), Text(clears.tSpinMiniZeros.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins zeros"), Text(clears.tSpinMiniZeros.toString())]),

View File

@ -42,7 +42,7 @@ class RecentSingleplayerGames extends StatelessWidget{
}, },
style: const TextStyle(fontSize: 18)), style: const TextStyle(fontSize: 18)),
subtitle: Text(timestamp(record.timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)), subtitle: Text(timestamp(record.timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
trailing: SpTrailingStats(record.stats, record.gamemode) trailing: SpTrailingStats(record, record.gamemode)
) )
], ],
); );

View File

@ -141,7 +141,7 @@ class SingleplayerRecord extends StatelessWidget {
}, },
style: const TextStyle(fontSize: 18)), style: const TextStyle(fontSize: 18)),
subtitle: Text(timestamp(stream!.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)), subtitle: Text(timestamp(stream!.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
trailing: SpTrailingStats(stream!.records[i].stats, stream!.records[i].gamemode) trailing: SpTrailingStats(stream!.records[i], stream!.records[i].gamemode)
) )
] ]
), ),

View File

@ -1,12 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/utils/numers_formats.dart';
import 'package:tetra_stats/utils/relative_timestamps.dart';
class SpTrailingStats extends StatelessWidget{ class SpTrailingStats extends StatelessWidget{
final ResultsStats endContext; final RecordSingle record;
final String gamemode; final String gamemode;
const SpTrailingStats(this.endContext, this.gamemode, {super.key}); const SpTrailingStats(this.record, this.gamemode, {super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -15,12 +16,28 @@ class SpTrailingStats extends StatelessWidget{
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
Text("${endContext.piecesPlaced} P, ${f2.format(endContext.pps)} PPS", style: style, textAlign: TextAlign.right),
Text("${intf.format(endContext.finessePercentage*100)}% F, ${endContext.finesse?.faults} FF", style: style, textAlign: TextAlign.right),
Text(switch(gamemode){ Text(switch(gamemode){
"40l" => "${f2.format(endContext.kps)} KPS, ${f2.format(endContext.kpp)} KPP", "40l" => "${record.stats.piecesPlaced} P, ${f2.format(record.stats.pps)} PPS",
"blitz" => "${intf.format(endContext.spp)} SPP, lvl ${endContext.level}", "blitz" => "${record.stats.piecesPlaced} P, ${f2.format(record.stats.pps)} PPS",
"5mblast" => "${intf.format(endContext.spp)} SPP, ${endContext.lines} L", "5mblast" => "${record.stats.piecesPlaced} P, ${f2.format(record.stats.pps)} PPS",
"zenith" => "${f2.format(record.aggregateStats.apm)} APM, ${f2.format(record.aggregateStats.pps)} PPS",
"zenithex" => "${f2.format(record.aggregateStats.apm)} APM, ${f2.format(record.aggregateStats.pps)} PPS",
String() => "huh"
}, style: style, textAlign: TextAlign.right),
Text(switch(gamemode){
"40l" => "${intf.format(record.stats.finessePercentage*100)}% F, ${record.stats.finesse?.faults} FF",
"blitz" => "${intf.format(record.stats.finessePercentage*100)}% F, ${record.stats.finesse?.faults} FF",
"5mblast" => "${intf.format(record.stats.finessePercentage*100)}% F, ${record.stats.finesse?.faults} FF",
"zenith" => "${f2.format(record.stats.cps)} CSP (${f2.format(record.stats.zenith!.peakrank)} peak)",
"zenithex" => "${f2.format(record.stats.cps)} CSP (${f2.format(record.stats.zenith!.peakrank)} peak)",
String() => "huh"
}, style: style, textAlign: TextAlign.right),
Text(switch(gamemode){
"40l" => "${f2.format(record.stats.kps)} KPS, ${f2.format(record.stats.kpp)} KPP",
"blitz" => "${intf.format(record.stats.spp)} SPP, lvl ${record.stats.level}",
"5mblast" => "${intf.format(record.stats.spp)} SPP, ${record.stats.lines} L",
"zenith" => "${record.stats.kills} KO's, ${getMoreNormalTime(record.stats.finalTime)}",
"zenithex" => "${record.stats.kills} KO's, ${getMoreNormalTime(record.stats.finalTime)}",
String() => "huh" String() => "huh"
}, style: style, textAlign: TextAlign.right) }, style: style, textAlign: TextAlign.right)
], ],

View File

@ -2,8 +2,10 @@ import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_gauges/gauges.dart'; import 'package:syncfusion_flutter_gauges/gauges.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/utils/colors_functions.dart';
import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/utils/numers_formats.dart';
import 'package:tetra_stats/utils/relative_timestamps.dart'; import 'package:tetra_stats/utils/relative_timestamps.dart';
import 'package:tetra_stats/utils/text_shadow.dart';
import 'package:tetra_stats/widgets/finesse_thingy.dart'; import 'package:tetra_stats/widgets/finesse_thingy.dart';
import 'package:tetra_stats/widgets/gauget_num.dart'; import 'package:tetra_stats/widgets/gauget_num.dart';
import 'package:tetra_stats/widgets/graphs.dart'; import 'package:tetra_stats/widgets/graphs.dart';
@ -95,9 +97,9 @@ class _ZenithThingyState extends State<ZenithThingy> {
text: "", text: "",
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
children: [ children: [
if (record!.rank != -1) TextSpan(text: "${record!.rank}"), if (record!.rank != -1) TextSpan(text: " ${intf.format(record!.rank)}", style: TextStyle(color: getColorOfRank(record!.rank))),
if (record!.rank != -1) const TextSpan(text: ""), if (record!.rank != -1) const TextSpan(text: ""),
if (record!.countryRank != -1) TextSpan(text: "${record!.countryRank} local"), if (record!.countryRank != -1) TextSpan(text: " ${intf.format(record!.countryRank)} local", style: TextStyle(color: getColorOfRank(record!.countryRank))),
if (record!.countryRank != -1) const TextSpan(text: ""), if (record!.countryRank != -1) const TextSpan(text: ""),
TextSpan(text: timestamp(widget.record!.timestamp)), TextSpan(text: timestamp(widget.record!.timestamp)),
] ]
@ -135,6 +137,27 @@ class _ZenithThingyState extends State<ZenithThingy> {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Stack(
alignment: AlignmentDirectional.bottomStart,
children: [
const Text("T", style: TextStyle(
fontStyle: FontStyle.italic,
fontSize: 65,
height: 1.2,
)),
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(
shadows: textShadow,
fontFamily: "Eurostile Round Extended",
fontSize: 36,
fontWeight: FontWeight.w500,
color: Colors.white
)),
)
],
),
Text("Total time: ${getMoreNormalTime(record!.stats.finalTime)}", style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center), Text("Total time: ${getMoreNormalTime(record!.stats.finalTime)}", style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center),
Table( Table(
columnWidths: const { columnWidths: const {