detailed stats for rank (not finished) + bug fix

This commit is contained in:
dan63047 2023-07-29 21:01:49 +03:00
parent f7784cb494
commit 518f2db7ea
6 changed files with 657 additions and 40 deletions

View File

@ -894,11 +894,56 @@ class TetrioPlayersLeaderboard {
TetrioPlayersLeaderboard(this.type, this.leaderboard);
List<dynamic> getAverageOfRank(String rank){
if (rank.isNotEmpty && !rankCutoffs.keys.contains(rank)) throw Exception("Invalid rank");
List<TetrioPlayerFromLeaderboard> filtredLeaderboard = List.from(leaderboard);
if (rank.isNotEmpty) {
filtredLeaderboard.removeWhere((element) => element.rank != rank);
if (filtredLeaderboard.isEmpty) throw Exception("Invalid rank");
double avgAPM = 0, avgPPS = 0, avgVS = 0, avgTR = 0, avgGlicko = 0, avgRD = 0, lowestTR = 25000;
int avgGamesPlayed = 0, avgGamesWon = 0, totalGamesPlayed = 0, totalGamesWon = 0;
} else {
rank = "z";
}
if (filtredLeaderboard.isNotEmpty){
double avgAPM = 0,
avgPPS = 0,
avgVS = 0,
avgTR = 0,
avgGlicko = 0,
avgRD = 0,
lowestTR = 25000,
lowestGlicko = double.infinity,
lowestWinrate = double.infinity,
lowestAPM = double.infinity,
lowestPPS = double.infinity,
lowestVS = double.infinity,
highestTR = 0,
highestGlicko = 0,
highestWinrate = 0,
highestAPM = 0,
highestPPS = 0,
highestVS = 0;
int avgGamesPlayed = 0,
avgGamesWon = 0,
totalGamesPlayed = 0,
totalGamesWon = 0,
lowestGamesPlayed = pow(2, 53) as int,
lowestGamesWon = pow(2, 53) as int,
highestGamesPlayed = 0,
highestGamesWon = 0;
String lowestTRid = "", lowestTRnick = "",
lowestGlickoID = "", lowestGlickoNick = "",
lowestGamesPlayedID = "", lowestGamesPlayedNick = "",
lowestGamesWonID = "", lowestGamesWonNick = "",
lowestWinrateID = "", lowestWinrateNick = "",
lowestAPMid = "", lowestAPMnick = "",
lowestPPSid = "", lowestPPSnick = "",
lowestVSid = "", lowestVSnick = "",
highestTRid = "", highestTRnick = "",
highestGlickoID = "", highestGlickoNick = "",
highestGamesPlayedID = "", highestGamesPlayedNick = "",
highestGamesWonID = "", highestGamesWonNick = "",
highestWinrateID = "", highestWinrateNick = "",
highestAPMid = "", highestAPMnick = "",
highestPPSid = "", highestPPSnick = "",
highestVSid = "", highestVSnick = "";
for (var entry in filtredLeaderboard){
avgAPM += entry.apm;
avgPPS += entry.pps;
@ -908,7 +953,86 @@ class TetrioPlayersLeaderboard {
avgRD += entry.rd;
totalGamesPlayed += entry.gamesPlayed;
totalGamesWon += entry.gamesWon;
if (entry.rating < lowestTR) lowestTR = entry.rating;
if (entry.rating < lowestTR){
lowestTR = entry.rating;
lowestTRid = entry.userId;
lowestTRnick = entry.username;
}
if (entry.glicko < lowestGlicko){
lowestGlicko = entry.glicko;
lowestGlickoID = entry.userId;
lowestGlickoNick = entry.username;
}
if (entry.gamesPlayed < lowestGamesPlayed){
lowestGamesPlayed = entry.gamesPlayed;
lowestGamesPlayedID = entry.userId;
lowestGamesPlayedNick = entry.username;
}
if (entry.gamesWon < lowestGamesWon){
lowestGamesWon = entry.gamesWon;
lowestGamesWonID = entry.userId;
lowestGamesWonNick = entry.username;
}
if (entry.winrate < lowestWinrate){
lowestWinrate = entry.winrate;
lowestWinrateID = entry.userId;
lowestWinrateNick = entry.username;
}
if (entry.apm < lowestAPM){
lowestAPM = entry.apm;
lowestAPMid = entry.userId;
lowestAPMnick = entry.username;
}
if (entry.pps < lowestPPS){
lowestPPS = entry.pps;
lowestPPSid = entry.userId;
lowestPPSnick = entry.username;
}
if (entry.vs < lowestVS){
lowestVS = entry.vs;
lowestVSid = entry.userId;
lowestVSnick = entry.username;
}
if (entry.rating > highestTR){
highestTR = entry.rating;
highestTRid = entry.userId;
highestTRnick = entry.username;
}
if (entry.glicko > highestGlicko){
highestGlicko = entry.glicko;
highestGlickoID = entry.userId;
highestGlickoNick = entry.username;
}
if (entry.gamesPlayed > highestGamesPlayed){
highestGamesPlayed = entry.gamesPlayed;
highestGamesPlayedID = entry.userId;
highestGamesPlayedNick = entry.username;
}
if (entry.gamesWon > highestGamesWon){
highestGamesWon = entry.gamesWon;
highestGamesWonID = entry.userId;
highestGamesWonNick = entry.username;
}
if (entry.winrate > highestWinrate){
highestWinrate = entry.winrate;
highestWinrateID = entry.userId;
highestWinrateNick = entry.username;
}
if (entry.apm > highestAPM){
highestAPM = entry.apm;
highestAPMid = entry.userId;
highestAPMnick = entry.username;
}
if (entry.pps > highestPPS){
highestPPS = entry.pps;
highestPPSid = entry.userId;
highestPPSnick = entry.username;
}
if (entry.vs > highestVS){
highestVS = entry.vs;
highestVSid = entry.userId;
highestVSnick = entry.username;
}
}
avgAPM /= filtredLeaderboard.length;
avgPPS /= filtredLeaderboard.length;
@ -918,8 +1042,66 @@ class TetrioPlayersLeaderboard {
avgRD /= filtredLeaderboard.length;
avgGamesPlayed = (totalGamesPlayed / filtredLeaderboard.length).floor();
avgGamesWon = (totalGamesWon / filtredLeaderboard.length).floor();
return [TetraLeagueAlpha(timestamp: DateTime.now(), apm: avgAPM, pps: avgPPS, vs: avgVS, glicko: avgGlicko, rd: avgRD, gamesPlayed: avgGamesPlayed, gamesWon: avgGamesWon, bestRank: rank, decaying: false, rating: avgTR, rank: rank, percentileRank: rank, percentile: 0, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1),
{"totalGamesPlayed": totalGamesPlayed, "totalGamesWon": totalGamesWon, "players": filtredLeaderboard.length, "lowestTR": lowestTR, "toEnterTR": leaderboard[(leaderboard.length * rankCutoffs[rank]!).floor()-1].rating}];
return [TetraLeagueAlpha(timestamp: DateTime.now(), apm: avgAPM, pps: avgPPS, vs: avgVS, glicko: avgGlicko, rd: avgRD, gamesPlayed: avgGamesPlayed, gamesWon: avgGamesWon, bestRank: rank, decaying: false, rating: avgTR, rank: rank, percentileRank: rank, percentile: rankCutoffs[rank]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1),
{
"totalGamesPlayed": totalGamesPlayed,
"totalGamesWon": totalGamesWon,
"players": filtredLeaderboard.length,
"lowestTR": lowestTR,
"lowestTRid": lowestTRid,
"lowestTRnick": lowestTRnick,
"lowestGlicko": lowestGlicko,
"lowestGlickoID": lowestGlickoID,
"lowestGlickoNick": lowestGlickoNick,
"lowestGamesPlayed": lowestGamesPlayed,
"lowestGamesPlayedID": lowestGamesPlayedID,
"lowestGamesPlayedNick": lowestGamesPlayedNick,
"lowestGamesWon": lowestGamesWon,
"lowestGamesWonID": lowestGamesWonID,
"lowestGamesWonNick": lowestGamesWonNick,
"lowestWinrate": lowestWinrate,
"lowestWinrateID": lowestWinrateID,
"lowestWinrateNick": lowestWinrateNick,
"lowestAPM": lowestAPM,
"lowestAPMid": lowestAPMid,
"lowestAPMnick": lowestAPMnick,
"lowestPPS": lowestPPS,
"lowestPPSid": lowestPPSid,
"lowestPPSnick": lowestPPSnick,
"lowestVS": lowestVS,
"lowestVSid": lowestVSid,
"lowestVSnick": lowestVSnick,
"highestTR": highestTR,
"highestTRid": highestTRid,
"highestTRnick": highestTRnick,
"highestGlicko": highestGlicko,
"highestGlickoID": highestGlickoID,
"highestGlickoNick": highestGlickoNick,
"highestGamesPlayed": highestGamesPlayed,
"highestGamesPlayedID": highestGamesPlayedID,
"highestGamesPlayedNick": highestGamesPlayedNick,
"highestGamesWon": highestGamesWon,
"highestGamesWonID": highestGamesWonID,
"highestGamesWonNick": highestGamesWonNick,
"highestWinrate": highestWinrate,
"highestWinrateID": highestWinrateID,
"highestWinrateNick": highestWinrateNick,
"highestAPM": highestAPM,
"highestAPMid": highestAPMid,
"highestAPMnick": highestAPMnick,
"highestPPS": highestPPS,
"highestPPSid": highestPPSid,
"highestPPSnick": highestPPSnick,
"highestVS": highestVS,
"highestVSid": highestVSid,
"highestVSnick": highestVSnick,
"toEnterTR": rank.toLowerCase() != "z" ? leaderboard[(leaderboard.length * rankCutoffs[rank]!).floor()-1].rating : lowestTR,
"entries": filtredLeaderboard
}];
}else{
return [TetraLeagueAlpha(timestamp: DateTime.now(), apm: 0, pps: 0, vs: 0, glicko: 0, rd: noTrRd, gamesPlayed: 0, gamesWon: 0, bestRank: rank, decaying: false, rating: 0, rank: rank, percentileRank: rank, percentile: rankCutoffs[rank]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1),
{"players": filtredLeaderboard.length, "lowestTR": 0, "toEnterTR": 0}];
}
}
Map<String, List<dynamic>> get averages => {
@ -939,7 +1121,8 @@ class TetrioPlayersLeaderboard {
'c': getAverageOfRank("c"),
'c-': getAverageOfRank("c-"),
'd+': getAverageOfRank("d+"),
'd': getAverageOfRank("d")
'd': getAverageOfRank("d"),
'z': getAverageOfRank("z")
};
TetrioPlayersLeaderboard.fromJson(List<dynamic> json, String t, DateTime ts) {
@ -994,6 +1177,7 @@ class TetrioPlayerFromLeaderboard {
this.vs,
this.decaying);
double get winrate => gamesWon / gamesPlayed;
get app => apm / (pps * 60);
get vsapm => vs / apm;

View File

@ -135,7 +135,7 @@ class TetrioService extends DB {
);
history.add(state);
}
ensureDbIsOpen();
await ensureDbIsOpen();
final db = getDatabaseOrThrow();
late List<TetrioPlayer> states;
try{
@ -237,7 +237,7 @@ class TetrioService extends DB {
}
Future<void> saveTLMatchesFromStream(TetraLeagueAlphaStream stream) async {
ensureDbIsOpen();
await ensureDbIsOpen();
final db = getDatabaseOrThrow();
for (TetraLeagueAlphaRecord match in stream.records) {
final results = await db.query(tetraLeagueMatchesTable, where: '$idCol = ?', whereArgs: [match.ownId]);
@ -247,7 +247,7 @@ class TetrioService extends DB {
}
Future<List<TetraLeagueAlphaRecord>> getTLMatchesbyPlayerID(String playerID) async {
ensureDbIsOpen();
await ensureDbIsOpen();
final db = getDatabaseOrThrow();
List<TetraLeagueAlphaRecord> matches = [];
final results = await db.query(tetraLeagueMatchesTable, where: '($player1id = ?) OR ($player2id = ?)', whereArgs: [playerID, playerID]);
@ -304,7 +304,7 @@ class TetrioService extends DB {
}
Future<void> createPlayer(TetrioPlayer tetrioPlayer) async {
ensureDbIsOpen();
await ensureDbIsOpen();
final db = getDatabaseOrThrow();
final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [tetrioPlayer.userId.toLowerCase()]);
if (results.isNotEmpty) {
@ -319,7 +319,7 @@ class TetrioService extends DB {
}
Future<void> addPlayerToTrack(TetrioPlayer tetrioPlayer) async {
ensureDbIsOpen();
await ensureDbIsOpen();
final db = getDatabaseOrThrow();
final results = await db.query(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [tetrioPlayer.userId.toLowerCase()]);
if (results.isNotEmpty) {
@ -329,7 +329,7 @@ class TetrioService extends DB {
}
Future<bool> isPlayerTracking(String id) async {
ensureDbIsOpen();
await ensureDbIsOpen();
final db = getDatabaseOrThrow();
final results = await db.query(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
if (results.isEmpty) {
@ -360,12 +360,11 @@ class TetrioService extends DB {
}
Future<void> storeState(TetrioPlayer tetrioPlayer) async {
ensureDbIsOpen();
await ensureDbIsOpen();
final db = getDatabaseOrThrow();
late List<TetrioPlayer> states;
try {
states = _players[tetrioPlayer.userId]!;
//states = await getPlayer(tetrioPlayer.userId);
} catch (e) {
await createPlayer(tetrioPlayer);
states = await getPlayer(tetrioPlayer.userId);
@ -383,7 +382,7 @@ class TetrioService extends DB {
}
Future<void> deleteState(TetrioPlayer tetrioPlayer) async {
ensureDbIsOpen();
await ensureDbIsOpen();
final db = getDatabaseOrThrow();
late List<TetrioPlayer> states;
states = await getPlayer(tetrioPlayer.userId);
@ -400,7 +399,7 @@ class TetrioService extends DB {
}
Future<List<TetrioPlayer>> getPlayer(String id) async {
ensureDbIsOpen();
await ensureDbIsOpen();
final db = getDatabaseOrThrow();
List<TetrioPlayer> states = [];
final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
@ -417,7 +416,7 @@ class TetrioService extends DB {
}
}
Future<TetrioPlayer> fetchPlayer(String user) async {
Future<TetrioPlayer> fetchPlayer(String user, {bool isItDiscordID = false}) async {
try{
var cached = _playersCache.entries.firstWhere((element) => element.value.userId == user || element.value.username == user);
if (DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true).isAfter(DateTime.now())){
@ -431,6 +430,28 @@ class TetrioService extends DB {
developer.log("fetchPlayer: Trying to retrieve $user", name: "services/tetrio_crud");
}
if (isItDiscordID){
Uri dUrl;
if (kIsWeb) {
dUrl = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "tetrioUserByDiscordID", "user": user.toLowerCase().trim()});
} else {
dUrl = Uri.https('ch.tetr.io', 'api/users/search/${user.toLowerCase().trim()}');
}
final response = await http.get(dUrl);
if (response.statusCode == 200) {
var json = jsonDecode(response.body);
if (json['success'] && json['data'] != null) {
user = json['data']['user']['_id'];
} else {
developer.log("fetchPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body);
throw TetrioPlayerNotExist();
}
} else {
developer.log("fetchPlayer Failed to fetch player", name: "services/tetrio_crud", error: response.statusCode);
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
}
}
Uri url;
if (kIsWeb) {
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "tetrioUser", "user": user.toLowerCase().trim()});

View File

@ -116,7 +116,12 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
}
Future<List> fetch(String nickOrID, {bool fetchHistory = false}) async {
TetrioPlayer me = await teto.fetchPlayer(nickOrID);
TetrioPlayer me;
if (nickOrID.startsWith("ds:")){
me = await teto.fetchPlayer(nickOrID.substring(3), isItDiscordID: true);
}else{
me = await teto.fetchPlayer(nickOrID);
}
_searchFor = me.userId;
setState((){_titleNickname = me.username;});
var tlStream = await teto.getTLStream(me.userId);

View File

@ -0,0 +1,397 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
//import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/views/main_view.dart';
import 'package:tetra_stats/widgets/stat_sell_num.dart';
//import 'package:tetra_stats/widgets/tl_thingy.dart';
final DateFormat dateFormat =
DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
double pfpHeight = 128;
class RankView extends StatefulWidget {
final List rank;
const RankView({Key? key, required this.rank}) : super(key: key);
@override
State<StatefulWidget> createState() => RankState();
}
class RankState extends State<RankView> with SingleTickerProviderStateMixin {
late ScrollController _scrollController;
late TabController _tabController;
@override
void initState() {
_scrollController = ScrollController();
_tabController = TabController(length: 6, vsync: this);
super.initState();
}
@override
void dispose() {
_tabController.dispose();
_scrollController.dispose();
super.dispose();
}
void _justUpdate() {
setState(() {});
}
@override
Widget build(BuildContext context) {
final t = Translations.of(context);
bool bigScreen = MediaQuery.of(context).size.width > 768;
return Scaffold(
appBar: AppBar(
title: Text(widget.rank[0].rank.toUpperCase()),
),
backgroundColor: Colors.black,
body: SafeArea(
child: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (context, value) {
return [
SliverToBoxAdapter(
child: Column(
children: [
Flex(
direction: Axis.vertical,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Stack(
alignment: Alignment.topCenter,
children: [
Container(
padding:
EdgeInsets.fromLTRB(0, pfpHeight, 0, 0),
child: Image.asset(
"res/tetrio_tl_alpha_ranks/${widget.rank[0].rank}.png",
fit: BoxFit.fitHeight,
height: 128),
),
],
),
Flexible(
child: Column(
children: [
Text(
"Values for ${widget.rank[0].rank.toUpperCase()} rank",
style: TextStyle(
fontFamily: "Eurostile Round Extended",
fontSize: bigScreen ? 42 : 28)),
Text(
"${widget.rank[1]["entries"].length} players",
style: TextStyle(
fontFamily: "Eurostile Round Extended",
fontSize: bigScreen ? 42 : 28)),
],
)),
],
),
Wrap(
direction: Axis.horizontal,
alignment: WrapAlignment.center,
spacing: 25,
crossAxisAlignment: WrapCrossAlignment.start,
clipBehavior: Clip.hardEdge, // hard WHAT???
children: [
StatCellNum(
playerStat: widget.rank[1]["totalGamesPlayed"],
playerStatLabel: "Total games\nplayed",
isScreenBig: bigScreen,
higherIsBetter: true,
),
StatCellNum(
playerStat: widget.rank[1]["totalGamesWon"],
playerStatLabel: "Total games\nwon",
isScreenBig: bigScreen,
higherIsBetter: true,
),
StatCellNum(
playerStat: (widget.rank[1]["totalGamesWon"] /
widget.rank[1]["totalGamesPlayed"]) *
100,
playerStatLabel: t.statCellNum.winrate,
fractionDigits: 3,
isScreenBig: bigScreen,
higherIsBetter: true)
],
),
],
)),
SliverToBoxAdapter(
child: TabBar(
controller: _tabController,
isScrollable: true,
tabs: const [
Tab(text: "Chart"),
Tab(text: "Entries"),
Tab(text: "Minimums"),
Tab(text: "Averages"),
Tab(text: "Maximums"),
Tab(text: "Other"),
],
)),
];
},
body: TabBarView(
controller: _tabController,
children: [
Column(
children: [
Text("Chart",
style: TextStyle(
fontFamily: "Eurostile Round Extended",
fontSize: bigScreen ? 42 : 28)),
],
),
Column(
children: [
Text("Entries",
style: TextStyle(
fontFamily: "Eurostile Round Extended",
fontSize: bigScreen ? 42 : 28)),
Expanded(
child: ListView.builder(
itemCount: widget.rank[1]["entries"]!.length,
itemBuilder: (context, index) {
bool bigScreen =
MediaQuery.of(context).size.width > 768;
return ListTile(
title: Text(
widget.rank[1]["entries"][index].username,
style: const TextStyle(
fontFamily:
"Eurostile Round Extended")),
subtitle: Text(
"${f2.format(widget.rank[1]["entries"][index].apm)} APM, ${f2.format(widget.rank[1]["entries"][index].pps)} PPS, ${f2.format(widget.rank[1]["entries"][index].vs)} VS, ${f2.format(widget.rank[1]["entries"][index].app)} APP, ${f2.format(widget.rank[1]["entries"][index].vsapm)} VS/APM"),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"${f2.format(widget.rank[1]["entries"][index].rating)} TR",
style: bigScreen
? const TextStyle(fontSize: 28)
: null),
Image.asset(
"res/tetrio_tl_alpha_ranks/${widget.rank[1]["entries"][index].rank}.png",
height: bigScreen ? 48 : 16),
],
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MainView(
player: widget
.rank[1]["entries"][index]
.userId),
maintainState: false,
),
);
},
);
}),
)
],
),
Column(
children: [
Text("Lowest Values",
style: TextStyle(
fontFamily: "Eurostile Round Extended",
fontSize: bigScreen ? 42 : 28)),
Expanded(
child: ListView(
children: [
_ListEntry(
value: widget.rank[1]["lowestTR"],
label: "Tetra Rating",
id: widget.rank[1]["lowestTRid"],
username: widget.rank[1]["lowestTRnick"],
approximate: false,
fractionDigits: 2),
_ListEntry(
value: widget.rank[1]["lowestGlicko"],
label: "Glicko",
id: widget.rank[1]["lowestGlickoID"],
username: widget.rank[1]["lowestGlickoNick"],
approximate: false,
fractionDigits: 2),
_ListEntry(
value: widget.rank[1]["lowestGamesPlayed"],
label: "Games Played",
id: widget.rank[1]["lowestGamesPlayedID"],
username: widget.rank[1]
["lowestGamesPlayedNick"],
approximate: false),
_ListEntry(
value: widget.rank[1]["lowestGamesWon"],
label: "Games Won",
id: widget.rank[1]["lowestGamesWonID"],
username: widget.rank[1]
["lowestGamesWonNick"],
approximate: false),
_ListEntry(
value: widget.rank[1]["lowestWinrate"] * 100,
label: "Winrate Percentage",
id: widget.rank[1]["lowestWinrateID"],
username: widget.rank[1]["lowestWinrateNick"],
approximate: false,
fractionDigits: 2),
_ListEntry(
value: widget.rank[1]["lowestAPM"],
label: "Attack Per Minute",
id: widget.rank[1]["lowestAPMid"],
username: widget.rank[1]["lowestAPMnick"],
approximate: false,
fractionDigits: 2),
_ListEntry(
value: widget.rank[1]["lowestPPS"],
label: "Pieces Per Second",
id: widget.rank[1]["lowestPPSid"],
username: widget.rank[1]["lowestPPSnick"],
approximate: false,
fractionDigits: 2),
_ListEntry(
value: widget.rank[1]["lowestVS"],
label: "Versus Score",
id: widget.rank[1]["lowestVSid"],
username: widget.rank[1]["lowestVSnick"],
approximate: false,
fractionDigits: 2)
],
),
),
],
),
Column(
children: [],
),
Column(
children: [
Text("Highest Values",
style: TextStyle(
fontFamily: "Eurostile Round Extended",
fontSize: bigScreen ? 42 : 28)),
Expanded(
child: ListView(
children: [
_ListEntry(
value: widget.rank[1]["highestTR"],
label: "Tetra Rating",
id: widget.rank[1]["highestTRid"],
username: widget.rank[1]["highestTRnick"],
approximate: false,
fractionDigits: 2),
_ListEntry(
value: widget.rank[1]["highestGlicko"],
label: "Glicko",
id: widget.rank[1]["highestGlickoID"],
username: widget.rank[1]["highestGlickoNick"],
approximate: false,
fractionDigits: 2),
_ListEntry(
value: widget.rank[1]["highestGamesPlayed"],
label: "Games Played",
id: widget.rank[1]["highestGamesPlayedID"],
username: widget.rank[1]
["highestGamesPlayedNick"],
approximate: false),
_ListEntry(
value: widget.rank[1]["highestGamesWon"],
label: "Games Won",
id: widget.rank[1]["highestGamesWonID"],
username: widget.rank[1]
["highestGamesWonNick"],
approximate: false),
_ListEntry(
value: widget.rank[1]["highestWinrate"] * 100,
label: "Winrate Percentage",
id: widget.rank[1]["highestWinrateID"],
username: widget.rank[1]
["highestWinrateNick"],
approximate: false,
fractionDigits: 2),
_ListEntry(
value: widget.rank[1]["highestAPM"],
label: "Attack Per Minute",
id: widget.rank[1]["highestAPMid"],
username: widget.rank[1]["highestAPMnick"],
approximate: false,
fractionDigits: 2),
_ListEntry(
value: widget.rank[1]["highestPPS"],
label: "Pieces Per Second",
id: widget.rank[1]["highestPPSid"],
username: widget.rank[1]["highestPPSnick"],
approximate: false,
fractionDigits: 2),
_ListEntry(
value: widget.rank[1]["highestVS"],
label: "Versus Score",
id: widget.rank[1]["highestVSid"],
username: widget.rank[1]["highestVSnick"],
approximate: false,
fractionDigits: 2)
],
),
)
],
),
Column(
children: [],
),
],
))));
}
}
class _ListEntry extends StatelessWidget {
final num value;
final String label;
final String id;
final String username;
final bool approximate;
final int? fractionDigits;
const _ListEntry(
{required this.value,
required this.label,
this.fractionDigits,
required this.id,
required this.username,
required this.approximate});
@override
Widget build(BuildContext context) {
NumberFormat f = NumberFormat.decimalPatternDigits(
locale: LocaleSettings.currentLocale.languageCode,
decimalDigits: fractionDigits ?? 0);
return ListTile(
title: Text(label),
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(f.format(value),
style: const TextStyle(fontSize: 22, height: 0.9)),
if (id.isNotEmpty) Text('for player $username')
],
),
onTap: id.isNotEmpty
? () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MainView(player: id),
maintainState: false,
),
);
}
: null,
);
}
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/views/rank_averages_view.dart';
import 'package:tetra_stats/views/tl_leaderboard_view.dart';
class RankAveragesView extends StatefulWidget {
@ -41,7 +42,16 @@ class RanksAverages extends State<RankAveragesView> {
leading: Image.asset("res/tetrio_tl_alpha_ranks/${keys[index]}.png", height: 48),
title: Text("${averages[keys[index]]?[1]["players"]} players", style: const TextStyle(fontFamily: "Eurostile Round Extended")),
subtitle: Text("${f2.format(averages[keys[index]]?[0].apm)} APM, ${f2.format(averages[keys[index]]?[0].pps)} PPS, ${f2.format(averages[keys[index]]?[0].vs)} VS, ${f2.format(averages[keys[index]]?[0].nerdStats.app)} APP, ${f2.format(averages[keys[index]]?[0].nerdStats.vsapm)} VS/APM"),
trailing: Text("${f2.format(averages[keys[index]]?[1]["toEnterTR"])} TR", style: bigScreen ? const TextStyle(fontSize: 28) : null));
trailing: Text("${f2.format(averages[keys[index]]?[1]["toEnterTR"])} TR", style: bigScreen ? const TextStyle(fontSize: 28) : null),
onTap: (){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RankView(rank: averages[keys[index]]!),
),
);
},
);
})
),
);

View File

@ -67,7 +67,7 @@ class TLThingy extends StatelessWidget {
minimum: tl.nextAt.toDouble(),
maximum: tl.prevAt.toDouble(),
interval: tl.prevAt.toDouble() - tl.nextAt.toDouble(),
ranges: [LinearGaugeRange(startValue: tl.standing.toDouble(), endValue: tl.prevAt.toDouble(), color: Colors.cyanAccent,)],
ranges: [LinearGaugeRange(startValue: tl.standing.toDouble() <= tl.prevAt.toDouble() ? tl.standing.toDouble() : tl.prevAt.toDouble(), endValue: tl.prevAt.toDouble(), color: Colors.cyanAccent,)],
//barPointers: [LinearBarPointer(value: 80)],
isAxisInversed: true,
isMirrored: true,