detailed stats for rank (not finished) + bug fix
This commit is contained in:
parent
f7784cb494
commit
518f2db7ea
|
@ -894,32 +894,214 @@ 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);
|
||||
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;
|
||||
for (var entry in filtredLeaderboard){
|
||||
avgAPM += entry.apm;
|
||||
avgPPS += entry.pps;
|
||||
avgVS += entry.vs;
|
||||
avgTR += entry.rating;
|
||||
avgGlicko += entry.glicko;
|
||||
avgRD += entry.rd;
|
||||
totalGamesPlayed += entry.gamesPlayed;
|
||||
totalGamesWon += entry.gamesWon;
|
||||
if (entry.rating < lowestTR) lowestTR = entry.rating;
|
||||
if (rank.isNotEmpty) {
|
||||
filtredLeaderboard.removeWhere((element) => element.rank != rank);
|
||||
} 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;
|
||||
avgVS += entry.vs;
|
||||
avgTR += entry.rating;
|
||||
avgGlicko += entry.glicko;
|
||||
avgRD += entry.rd;
|
||||
totalGamesPlayed += entry.gamesPlayed;
|
||||
totalGamesWon += entry.gamesWon;
|
||||
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;
|
||||
avgVS /= filtredLeaderboard.length;
|
||||
avgTR /= filtredLeaderboard.length;
|
||||
avgGlicko /= filtredLeaderboard.length;
|
||||
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: 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}];
|
||||
}
|
||||
avgAPM /= filtredLeaderboard.length;
|
||||
avgPPS /= filtredLeaderboard.length;
|
||||
avgVS /= filtredLeaderboard.length;
|
||||
avgTR /= filtredLeaderboard.length;
|
||||
avgGlicko /= filtredLeaderboard.length;
|
||||
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}];
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
@ -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())){
|
||||
|
@ -430,7 +429,29 @@ class TetrioService extends DB {
|
|||
}catch(e){
|
||||
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()});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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]]!),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
})
|
||||
),
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue