diff --git a/lib/data_objects/tetrio.dart b/lib/data_objects/tetrio.dart index 4591e8f..4c60534 100644 --- a/lib/data_objects/tetrio.dart +++ b/lib/data_objects/tetrio.dart @@ -68,10 +68,7 @@ class TetrioPlayer { this.distinguishment, }); - double get level => - pow((xp / 500), 0.6) + - (xp / (5000 + (max(0, xp - 4 * pow(10, 6)) / 5000))) + - 1; + double get level => pow((xp / 500), 0.6) + (xp / (5000 + (max(0, xp - 4 * pow(10, 6)) / 5000))) + 1; TetrioPlayer.fromJson(Map json, DateTime stateTime) { userId = json['_id']; @@ -96,33 +93,25 @@ class TetrioPlayer { bannerRevision = json['banner_revision']; bio = json['bio']; connections = Connections.fromJson(json['connections']); - distinguishment = json['distinguishment'] != null - ? Distinguishment.fromJson(json['distinguishment']) - : null; - friendCount = json['friend_count'] != null ? json['friend_count'] : 0; - badstanding = json['badstanding'] != null ? json['badstanding'] : null; - } - - Future getRecords() async { + distinguishment = json['distinguishment'] != null ? Distinguishment.fromJson(json['distinguishment']) : null; + friendCount = json['friend_count'] ?? 0; var url = Uri.https('ch.tetr.io', 'api/users/$userId/records'); - final response = await http.get(url); - if (response.statusCode == 200) { - if (jsonDecode(response.body)['data']['records']['40l']['record'] != - null) { - sprint.add(RecordSingle.fromJson( - jsonDecode(response.body)['data']['records']['40l']['record'])); + Future response = http.get(url); + response.then((value) { + if (value.statusCode == 200) { + Map jsonRecords = jsonDecode(value.body); + sprint = jsonRecords['data']['records']['40l']['record'] != null + ? [RecordSingle.fromJson(jsonRecords['data']['records']['40l']['record'], jsonRecords['data']['records']['40l']['rank'])] + : []; + blitz = jsonRecords['data']['records']['blitz']['record'] != null + ? [RecordSingle.fromJson(jsonRecords['data']['records']['blitz']['record'], jsonRecords['data']['records']['blitz']['rank'])] + : []; + zen = TetrioZen.fromJson(jsonRecords['data']['zen']); + } else { + throw Exception('Failed to fetch player'); } - if (jsonDecode(response.body)['data']['records']['blitz']['record'] != - null) { - blitz.add(RecordSingle.fromJson( - jsonDecode(response.body)['data']['records']['blitz']['record'])); - } - zen = TetrioZen.fromJson(jsonDecode(response.body)['data']['zen']); - } else { - // If the server did not return a 200 OK response, - // then throw an exception. - throw Exception('Failed to fetch player'); - } + }); + badstanding = json['badstanding']; } Map toJson() { @@ -195,7 +184,7 @@ class Badge { Badge.fromJson(Map json) { badgeId = json['id']; label = json['label']; - ts = json['ts'] != null ? DateTime.parse(json['ts']) : null; + ts = (json['ts'] != null && json['ts'] is String) ? DateTime.parse(json['ts']) : null; } Map toJson() { @@ -224,8 +213,7 @@ class Connections { Connections({this.discord}); Connections.fromJson(Map json) { - discord = - json['discord'] != null ? Discord.fromJson(json['discord']) : null; + discord = json['discord'] != null ? Discord.fromJson(json['discord']) : null; } Map toJson() { @@ -326,8 +314,7 @@ class Finesse { late int faults; late int perfectPieces; - Finesse( - {required this.combo, required this.faults, required this.perfectPieces}); + Finesse({required this.combo, required this.faults, required this.perfectPieces}); Finesse.fromJson(Map json) { combo = json['combo']; @@ -345,42 +332,48 @@ class Finesse { } class EndContextSingle { - String? gameType; - int? topBtB; - int? topCombo; - int? holds; - int? inputs; - int? level; - int? piecesPlaced; - int? lines; - int? score; - int? seed; - Duration? finalTime; - int? tSpins; - Clears? clears; - Finesse? finesse; + late String gameType; + late int topBtB; + late int topCombo; + late int holds; + late int inputs; + late int level; + late int piecesPlaced; + late int lines; + late int score; + late int seed; + late Duration finalTime; + late int tSpins; + late Clears clears; + late Finesse finesse; + + double get pps => piecesPlaced / (finalTime.inMicroseconds / 1000000); + double get kpp => inputs / piecesPlaced; + double get spp => score / piecesPlaced; + double get kps => inputs / (finalTime.inMicroseconds / 1000000); + double get finessePercentage => finesse.perfectPieces / piecesPlaced; EndContextSingle( - {this.gameType, - this.topBtB, - this.topCombo, - this.holds, - this.inputs, - this.level, - this.piecesPlaced, - this.lines, - this.score, - this.seed, - this.finalTime, - this.tSpins, - this.clears, - this.finesse}); + {required this.gameType, + required this.topBtB, + required this.topCombo, + required this.holds, + required this.inputs, + required this.level, + required this.piecesPlaced, + required this.lines, + required this.score, + required this.seed, + required this.finalTime, + required this.tSpins, + required this.clears, + required this.finesse}); EndContextSingle.fromJson(Map json) { seed = json['seed']; lines = json['lines']; inputs = json['inputs']; - holds = json['holds']; + holds = json['holds'] ?? 0; finalTime = doubleMillisecondsToDuration(json['finalTime'].toDouble()); score = json['score']; level = json['level']; @@ -388,9 +381,8 @@ class EndContextSingle { topBtB = json['topbtb']; tSpins = json['tspins']; piecesPlaced = json['piecesplaced']; - clears = json['clears'] != null ? Clears.fromJson(json['clears']) : null; - finesse = - json['finesse'] != null ? Finesse.fromJson(json['finesse']) : null; + clears = Clears.fromJson(json['clears']); + finesse = Finesse.fromJson(json['finesse']); gameType = json['gametype']; } @@ -406,12 +398,8 @@ class EndContextSingle { data['topbtb'] = topBtB; data['tspins'] = tSpins; data['piecesplaced'] = piecesPlaced; - if (clears != null) { - data['clears'] = clears!.toJson(); - } - if (finesse != null) { - data['finesse'] = finesse!.toJson(); - } + data['clears'] = clears.toJson(); + data['finesse'] = finesse.toJson(); data['finalTime'] = finalTime; data['gametype'] = gameType; return data; @@ -426,13 +414,7 @@ class Handling { late bool cancel; late bool safeLock; - Handling( - {required this.arr, - required this.das, - required this.sdf, - required this.dcd, - required this.cancel, - required this.safeLock}); + Handling({required this.arr, required this.das, required this.sdf, required this.dcd, required this.cancel, required this.safeLock}); Handling.fromJson(Map json) { arr = json['arr']; @@ -455,6 +437,92 @@ class Handling { } } +class NerdStats { + final double _apm; + final double _pps; + final double _vs; + late double app; + late double vsapm; + late double dss; + late double dsp; + late double appdsp; + late double cheese; + late double gbe; + late double nyaapp; + late double area; + + NerdStats(this._apm, this._pps, this._vs) { + app = _apm / (_pps * 60); + vsapm = _vs / _apm; + dss = (_vs / 100) - (_apm / 60); + dsp = ((_vs / 100) - (_apm / 60)) / _pps; + appdsp = app + dsp; + cheese = (dsp * 150) + (((_vs / _apm) - 2) * 50) + (0.6 - app) * 125; + gbe = ((app * dss) / _pps) * 2; + nyaapp = app - 5 * tan(radians((cheese / -30) + 1)); + area = _apm * 1 + _pps * 45 + _vs * 0.444 + app * 185 + dss * 175 + dsp * 450 + gbe * 315; + } +} + +class EstTr { + final double _apm; + final double _pps; + final double _vs; + final double _rating; + final double _rd; + final double _app; + final double _dss; + final double _dsp; + final double _gbe; + late double esttr; + late double srarea; + late double statrank; + + EstTr(this._apm, this._pps, this._vs, this._rating, this._rd, this._app, this._dss, this._dsp, this._gbe) { + srarea = (_apm * 0) + (_pps * 135) + (_vs * 0) + (_app * 290) + (_dss * 0) + (_dsp * 700) + (_gbe * 0); + statrank = 11.2 * atan((srarea - 93) / 130) + 1; + if (statrank <= 0) statrank = 0.001; + double estglicko = (4.0867 * srarea + 186.68); + double temp = (1500 - estglicko) * pi; + double temp2 = pow((15.9056943314 * (pow(_rd, 2)) + 3527584.25978), 0.5) as double; + double temp3 = 1 + pow(10, (temp / temp2)) as double; + esttr = 25000 / temp3; + } +} + +class Playstyle { + final double _apm; + final double _pps; + final double _vs; + final double _rd; + final double _app; + final double _vsapm; + final double _dss; + final double _dsp; + final double _gbe; + final double _srarea; + final double _statrank; + late double opener; + late double plonk; + late double stride; + late double infds; + + Playstyle(this._apm, this._pps, this._vs, this._rd, this._app, this._vsapm, this._dss, this._dsp, this._gbe, this._srarea, this._statrank) { + double nmapm = ((_apm / _srarea) / ((0.069 * pow(1.0017, (pow(_statrank, 5) / 4700))) + _statrank / 360)) - 1; + double nmpps = ((_pps / _srarea) / (0.0084264 * pow(2.14, (-2 * (_statrank / 2.7 + 1.03))) - _statrank / 5750 + 0.0067)) - 1; + double nmvs = ((_vs / _srarea) / (0.1333 * pow(1.0021, ((pow(_statrank, 7) * (_statrank / 16.5)) / 1400000)) + _statrank / 133)) - 1; + double nmapp = (_app / (0.1368803292 * pow(1.0024, (pow(_statrank, 5) / 2800)) + _statrank / 54)) - 1; + double nmdss = (_dss / (0.01436466667 * pow(4.1, ((_statrank - 9.6) / 2.9)) + _statrank / 140 + 0.01)) - 1; + double nmdsp = (_dsp / (0.02136327583 * pow(14, ((_statrank - 14.75) / 3.9)) + _statrank / 152 + 0.022)) - 1; + double nmgbe = (_gbe / (_statrank / 350 + 0.005948424455 * pow(3.8, ((_statrank - 6.1) / 4)) + 0.006)) - 1; + double nmvsapm = (_vsapm / (-pow(((_statrank - 16) / 36), 2) + 2.133)) - 1; + opener = ((nmapm + nmpps * 0.75 + nmvsapm * -10 + nmapp * 0.75 + nmdsp * -0.25) / 3.5) + 0.5; + plonk = ((nmgbe + nmapp + nmdsp * 0.75 + nmpps * -1) / 2.73) + 0.5; + stride = ((nmapm * -0.25 + nmpps + nmapp * -2 + nmdsp * -0.5) * 0.79) + 0.5; + infds = ((nmdsp + nmapp * -0.75 + nmapm * 0.5 + nmvsapm * 1.5 + nmpps * 0.5) * 0.9) + 0.5; + } +} + class EndContextMulti { String? userId; int? naturalOrder; @@ -489,8 +557,7 @@ class EndContextMulti { EndContextMulti.fromJson(Map json) { userId = json['user']['_id']; - handling = - json['handling'] != null ? Handling.fromJson(json['handling']) : null; + handling = json['handling'] != null ? Handling.fromJson(json['handling']) : null; success = json['success']; inputs = json['inputs']; piecesPlaced = json['piecesplaced']; @@ -502,9 +569,7 @@ class EndContextMulti { secondaryTracking = json['points']['secondaryAvgTracking'].cast(); tertiaryTracking = json['points']['tertiaryAvgTracking'].cast(); extra = json['points']['extra']['vs']; - extraTracking = json['points']['extraAvgTracking'] - ['aggregatestats___vsscore'] - .cast(); + extraTracking = json['points']['extraAvgTracking']['aggregatestats___vsscore'].cast(); } Map toJson() { @@ -522,8 +587,7 @@ class EndContextMulti { data['points']['secondary'] = secondary; data['points']['tertiary'] = tertiary; data['points']['extra']['vs'] = extra; - data['points']['extraAvgTracking']['aggregatestats___vsscore'] = - extraTracking; + data['points']['extraAvgTracking']['aggregatestats___vsscore'] = extraTracking; return data; } } @@ -548,6 +612,9 @@ class TetraLeagueAlpha { double? apm; double? pps; double? vs; + NerdStats? nerdStats; + EstTr? estTr; + Playstyle? playstyle; List? records; TetraLeagueAlpha( @@ -594,45 +661,16 @@ class TetraLeagueAlpha { nextRank = json['next_rank']; nextAt = json['next_at']; percentileRank = json['percentile_rank']; + nerdStats = (apm != null && pps != null && apm != null) ? NerdStats(apm!, pps!, vs!) : null; + estTr = + (nerdStats != null) ? EstTr(apm!, pps!, vs!, rating, (rd != null) ? rd! : 69, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe) : null; + playstyle = (nerdStats != null) + ? Playstyle(apm!, pps!, vs!, (rd != null) ? rd! : 69, nerdStats!.app, nerdStats!.vsapm, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, + estTr!.statrank) + : null; } - double? get app => apm! / (pps! * 60); - double? get vsapm => vs! / apm!; - double? get dss => (vs! / 100) - (apm! / 60); - double? get dsp => ((vs! / 100) - (apm! / 60)) / pps!; - double? get appdsp => app! + dsp!; - double? get cheese => - (dsp! * 150) + (((vs! / apm!) - 2) * 50) + (0.6 - app!) * 125; - double? get gbe => ((app! * dss!) / pps!) * 2; - double? get nyaapp => app! - 5 * tan(radians((cheese! / -30) + 1)); - double? get area => - apm! * 1 + - pps! * 45 + - vs! * 0.444 + - app! * 185 + - dss! * 175 + - dsp! * 450 + - gbe! * 315; - - double? get esttr { - double srarea = (apm! * 0) + - (pps! * 135) + - (vs! * 0) + - (app! * 290) + - (dss! * 0) + - (dsp! * 700) + - (gbe! * 0); - double statrank = 11.2 * atan((srarea - 93) / 130) + 1; - if (statrank <= 0) statrank = 0.001; - double estglicko = (4.0867 * srarea + 186.68); - double temp = (1500 - estglicko) * pi; - double temp2 = - pow((15.9056943314 * (pow(rd!, 2)) + 3527584.25978), 0.5) as double; - double temp3 = 1 + pow(10, (temp / temp2)) as double; - return 25000 / temp3; - } - - double? get esttracc => esttr! - rating; + double? get esttracc => (estTr != null) ? estTr!.esttr - rating : null; Map toJson() { final Map data = {}; @@ -667,22 +705,15 @@ class RecordSingle { EndContextSingle? endContext; int? rank; - RecordSingle( - {required this.userId, - required this.replayId, - required this.ownId, - this.timestamp, - this.endContext, - this.rank}); + RecordSingle({required this.userId, required this.replayId, required this.ownId, this.timestamp, this.endContext, this.rank}); - RecordSingle.fromJson(Map json) { + RecordSingle.fromJson(Map json, int? ran) { ownId = json['_id']; - endContext = json['endcontext'] != null - ? EndContextSingle.fromJson(json['endcontext']) - : null; + endContext = json['endcontext'] != null ? EndContextSingle.fromJson(json['endcontext']) : null; replayId = json['replayid']; timestamp = DateTime.parse(json['ts']); userId = json['user']['_id']; + rank = ran; } Map toJson() { diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 024fffa..a9c1db4 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -14,7 +14,7 @@ extension StringExtension on String { } String _searchFor = "dan63047"; -late Future me; +Future? me; DB db = DB(); TetrioService teto = TetrioService(); const allowedHeightForPlayerIdInPixels = 40.0; @@ -37,20 +37,18 @@ Future fetchTetrioPlayer(String user) async { final response = await http.get(url); if (response.statusCode == 200) { - return jsonDecode(response.body)['success'] - ? TetrioPlayer.fromJson( - jsonDecode(response.body)['data']['user'], - DateTime.fromMillisecondsSinceEpoch( - jsonDecode(response.body)['cache']['cached_at'], - isUtc: true)) - : throw Exception("User doesn't exist"); + if (jsonDecode(response.body)['success']) { + return TetrioPlayer.fromJson( + jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true)); + } else { + throw Exception("User doesn't exist"); + } } else { throw Exception('Failed to fetch player'); } } -class _MyHomePageState extends State - with SingleTickerProviderStateMixin { +class _MyHomePageState extends State with SingleTickerProviderStateMixin { final bodyGlobalKey = GlobalKey(); final List myTabs = [ const Tab(text: "Tetra League"), @@ -81,19 +79,21 @@ class _MyHomePageState extends State ), ], ), - onSubmitted: (String value) => setState(() { - me = fetchTetrioPlayer(value); + onSubmitted: (String value) { _searchFor = value; - }), + me = null; + _tabController.animateTo(0, duration: Duration(milliseconds: 300)); + setState(() { + me = fetchTetrioPlayer(value); + }); + }, ); } @override void initState() { _scrollController = ScrollController(); - //_scrollController.addListener(_scrollListener); _tabController = TabController(length: 4, vsync: this); - //_tabController.addListener(_smoothScrollToTop); me = fetchTetrioPlayer("dan63047"); super.initState(); } @@ -117,10 +117,6 @@ class _MyHomePageState extends State duration: const Duration(microseconds: 300), curve: Curves.ease, ); - - setState(() { - fixedScroll = _tabController.index == 2; - }); } @override @@ -171,8 +167,7 @@ class _MyHomePageState extends State tooltip: "Close search", ), PopupMenuButton( - itemBuilder: (BuildContext context) => - >[ + itemBuilder: (BuildContext context) => >[ const PopupMenuItem( value: ThreeDotsItems.compare, child: Text('Compare'), @@ -193,19 +188,27 @@ class _MyHomePageState extends State child: FutureBuilder( future: me, builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator( + color: Colors.white, + )); + } if (snapshot.hasData) { bool bigScreen = MediaQuery.of(context).size.width > 768; return NestedScrollView( controller: _scrollController, headerSliverBuilder: (context, value) { return [ - SliverToBoxAdapter( - child: _UserThingy(player: snapshot.data!)), + SliverToBoxAdapter(child: _UserThingy(player: snapshot.data!)), SliverToBoxAdapter( child: TabBar( controller: _tabController, isScrollable: true, tabs: myTabs, + onTap: (int sus) { + setState(() {}); + }, ), ), ]; @@ -220,22 +223,15 @@ class _MyHomePageState extends State return Column( children: (snapshot.data!.tlSeason1.gamesPlayed > 0) ? [ - Text("Tetra League", - style: TextStyle( - fontFamily: - "Eurostile Round Extended", - fontSize: bigScreen ? 42 : 28)), - if (snapshot.data!.tlSeason1.gamesPlayed >= - 10) + Text("Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + if (snapshot.data!.tlSeason1.gamesPlayed >= 10) Wrap( direction: Axis.horizontal, alignment: WrapAlignment.spaceAround, - crossAxisAlignment: - WrapCrossAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, clipBehavior: Clip.hardEdge, children: [ - snapshot.data!.userId == - "5e32fc85ab319c2ab1beb07c" + snapshot.data!.userId == "5e32fc85ab319c2ab1beb07c" ? Image.asset( "res/icons/kagari.png", height: 128, @@ -246,13 +242,8 @@ class _MyHomePageState extends State ), Column( children: [ - Text( - "${snapshot.data!.tlSeason1.rating.toStringAsFixed(2)} TR", - style: TextStyle( - fontFamily: - "Eurostile Round Extended", - fontSize: - bigScreen ? 42 : 28)), + Text("${snapshot.data!.tlSeason1.rating.toStringAsFixed(2)} TR", + style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), Text( "Top ${(snapshot.data!.tlSeason1.percentile * 100).toStringAsFixed(2)}% (${snapshot.data!.tlSeason1.percentileRank.toUpperCase()}) • Top Rank: ${snapshot.data!.tlSeason1.bestRank.toUpperCase()} • Glicko: ${snapshot.data!.tlSeason1.glicko?.toStringAsFixed(2)}±${snapshot.data!.tlSeason1.rd?.toStringAsFixed(2)}${snapshot.data!.tlSeason1.decaying ? ' • Decaying' : ''}", textAlign: TextAlign.center, @@ -263,179 +254,122 @@ class _MyHomePageState extends State ) else Row( - mainAxisAlignment: - MainAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, children: [ Column( children: [ - Text( - "${10 - snapshot.data!.tlSeason1.gamesPlayed} games until being ranked", + Text("${10 - snapshot.data!.tlSeason1.gamesPlayed} games until being ranked", softWrap: true, style: TextStyle( - fontFamily: - "Eurostile Round Extended", + fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28, - overflow: - TextOverflow.visible, + overflow: TextOverflow.visible, )), ], ) ], ), Padding( - padding: - const EdgeInsets.fromLTRB(0, 16, 0, 48), + padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), child: Wrap( direction: Axis.horizontal, alignment: WrapAlignment.center, spacing: 25, - crossAxisAlignment: - WrapCrossAlignment.start, + crossAxisAlignment: WrapCrossAlignment.start, clipBehavior: Clip.hardEdge, children: [ - if (snapshot.data!.tlSeason1.apm != - null) + if (snapshot.data!.tlSeason1.apm != null) _StatCellNum( - playerStat: - snapshot.data!.tlSeason1.apm!, + playerStat: snapshot.data!.tlSeason1.apm!, isScreenBig: bigScreen, fractionDigits: 2, - playerStatLabel: - "Attack\nPer Minute"), - if (snapshot.data!.tlSeason1.pps != - null) + playerStatLabel: "Attack\nPer Minute"), + if (snapshot.data!.tlSeason1.pps != null) _StatCellNum( - playerStat: - snapshot.data!.tlSeason1.pps!, + playerStat: snapshot.data!.tlSeason1.pps!, isScreenBig: bigScreen, fractionDigits: 2, - playerStatLabel: - "Pieces\nPer Second"), - if (snapshot.data!.tlSeason1.apm != - null) + playerStatLabel: "Pieces\nPer Second"), + if (snapshot.data!.tlSeason1.apm != null) _StatCellNum( - playerStat: - snapshot.data!.tlSeason1.vs!, + playerStat: snapshot.data!.tlSeason1.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Versus\nScore"), - if (snapshot.data!.tlSeason1.standing > - 0) + if (snapshot.data!.tlSeason1.standing > 0) _StatCellNum( - playerStat: snapshot - .data!.tlSeason1.standing, - isScreenBig: bigScreen, - playerStatLabel: - "Leaderboard\nplacement"), - if (snapshot - .data!.tlSeason1.standingLocal > - 0) + playerStat: snapshot.data!.tlSeason1.standing, isScreenBig: bigScreen, playerStatLabel: "Leaderboard\nplacement"), + if (snapshot.data!.tlSeason1.standingLocal > 0) _StatCellNum( - playerStat: snapshot.data! - .tlSeason1.standingLocal, + playerStat: snapshot.data!.tlSeason1.standingLocal, isScreenBig: bigScreen, - playerStatLabel: - "Country LB\nplacement"), + playerStatLabel: "Country LB\nplacement"), _StatCellNum( - playerStat: snapshot - .data!.tlSeason1.gamesPlayed, - isScreenBig: bigScreen, - playerStatLabel: "Games\nplayed"), + playerStat: snapshot.data!.tlSeason1.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Games\nplayed"), + _StatCellNum(playerStat: snapshot.data!.tlSeason1.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nwon"), _StatCellNum( - playerStat: snapshot - .data!.tlSeason1.gamesWon, - isScreenBig: bigScreen, - playerStatLabel: "Games\nwon"), - _StatCellNum( - playerStat: snapshot - .data!.tlSeason1.winrate * - 100, + playerStat: snapshot.data!.tlSeason1.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, - playerStatLabel: - "Winrate\nprecentage"), + playerStatLabel: "Winrate\nprecentage"), ], ), ), - if (snapshot.data!.tlSeason1.apm != null && - snapshot.data!.tlSeason1.pps != null && - snapshot.data!.tlSeason1.apm != null) + if (snapshot.data!.tlSeason1.nerdStats != null) Column( children: [ - Text("Nerd Stats", - style: TextStyle( - fontFamily: - "Eurostile Round Extended", - fontSize: bigScreen ? 42 : 28)), + Text("Nerd Stats", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), Padding( - padding: const EdgeInsets.fromLTRB( - 0, 16, 0, 48), + padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), child: Wrap( direction: Axis.horizontal, alignment: WrapAlignment.center, spacing: 25, - crossAxisAlignment: - WrapCrossAlignment.start, + crossAxisAlignment: WrapCrossAlignment.start, clipBehavior: Clip.hardEdge, children: [ _StatCellNum( - playerStat: snapshot - .data!.tlSeason1.app!, + playerStat: snapshot.data!.tlSeason1.nerdStats!.app, isScreenBig: bigScreen, fractionDigits: 3, - playerStatLabel: - "Attack\nPer Piece"), + playerStatLabel: "Attack\nPer Piece"), _StatCellNum( - playerStat: snapshot - .data!.tlSeason1.vsapm!, + playerStat: snapshot.data!.tlSeason1.nerdStats!.vsapm, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "VS/APM"), _StatCellNum( - playerStat: snapshot - .data!.tlSeason1.dss!, + playerStat: snapshot.data!.tlSeason1.nerdStats!.dss, isScreenBig: bigScreen, fractionDigits: 3, - playerStatLabel: - "Downstack\nPer Second"), + playerStatLabel: "Downstack\nPer Second"), _StatCellNum( - playerStat: snapshot - .data!.tlSeason1.dsp!, + playerStat: snapshot.data!.tlSeason1.nerdStats!.dsp, isScreenBig: bigScreen, fractionDigits: 3, - playerStatLabel: - "Downstack\nPer Piece"), + playerStatLabel: "Downstack\nPer Piece"), _StatCellNum( - playerStat: snapshot.data! - .tlSeason1.appdsp!, + playerStat: snapshot.data!.tlSeason1.nerdStats!.appdsp, isScreenBig: bigScreen, fractionDigits: 3, - playerStatLabel: - "APP + DS/P"), + playerStatLabel: "APP + DS/P"), _StatCellNum( - playerStat: snapshot.data! - .tlSeason1.cheese!, + playerStat: snapshot.data!.tlSeason1.nerdStats!.cheese, isScreenBig: bigScreen, fractionDigits: 2, - playerStatLabel: - "Cheese\nIndex"), + playerStatLabel: "Cheese\nIndex"), _StatCellNum( - playerStat: snapshot - .data!.tlSeason1.gbe!, + playerStat: snapshot.data!.tlSeason1.nerdStats!.gbe, isScreenBig: bigScreen, fractionDigits: 3, - playerStatLabel: - "Garbage\nEfficiency"), + playerStatLabel: "Garbage\nEfficiency"), _StatCellNum( - playerStat: snapshot.data! - .tlSeason1.nyaapp!, + playerStat: snapshot.data!.tlSeason1.nerdStats!.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, - playerStatLabel: - "Weighted\nAPP"), + playerStatLabel: "Weighted\nAPP"), _StatCellNum( - playerStat: snapshot - .data!.tlSeason1.area!, + playerStat: snapshot.data!.tlSeason1.nerdStats!.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: "Area") @@ -443,233 +377,713 @@ class _MyHomePageState extends State ) ], ), - Padding( - padding: - const EdgeInsets.fromLTRB(0, 16, 0, 48), - child: SizedBox( - width: bigScreen - ? MediaQuery.of(context).size.width * - 0.4 - : MediaQuery.of(context).size.width * - 0.85, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - const Text( - "Est. of TR:", - style: TextStyle(fontSize: 24), - ), - Text( - snapshot.data!.tlSeason1.esttr! - .toStringAsFixed(2), - style: const TextStyle( - fontSize: 24), - ), - ], - ), - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - const Text( - "Accuracy of TR Est.:", - style: TextStyle(fontSize: 24), - ), - Text( - (snapshot.data!.tlSeason1 - .esttracc!) - .toStringAsFixed(2), - style: const TextStyle( - fontSize: 24), - ), - ], - ), - ], - ), - ), - ), - Padding( - padding: - const EdgeInsets.fromLTRB(0, 0, 0, 48), - child: SizedBox( - height: 300, - child: RadarChart( - RadarChartData( - radarShape: RadarShape.polygon, - tickCount: 4, - ticksTextStyle: const TextStyle( - color: Colors.transparent, - fontSize: 10), - radarBorderData: BorderSide( - color: Colors.transparent, - width: 1), - gridBorderData: BorderSide( - color: Colors.white24, width: 1), - tickBorderData: BorderSide( - color: Colors.transparent, - width: 1), - getTitle: (index, angle) { - switch (index) { - case 0: - return RadarChartTitle( - text: 'APM', - angle: angle, - ); - case 1: - return RadarChartTitle( - text: 'PPS', - angle: angle, - ); - case 2: - return RadarChartTitle( - text: 'VS', angle: angle); - case 3: - return RadarChartTitle( - text: 'APP', angle: angle); - case 4: - return RadarChartTitle( - text: 'DS/S', angle: angle); - case 5: - return RadarChartTitle( - text: 'DS/P', angle: angle); - case 6: - return RadarChartTitle( - text: 'APP+DS/P', - angle: angle); - case 7: - return RadarChartTitle( - text: 'VS/APM', - angle: angle); - case 8: - return RadarChartTitle( - text: 'Cheese', - angle: angle); - case 9: - return RadarChartTitle( - text: 'Gb Eff.', - angle: angle); - default: - return const RadarChartTitle( - text: ''); - } - }, - dataSets: [ - RadarDataSet( - dataEntries: [ - RadarEntry( - value: snapshot.data! - .tlSeason1.apm! * - 1), - RadarEntry( - value: snapshot.data! - .tlSeason1.pps! * - 45), - RadarEntry( - value: snapshot.data! - .tlSeason1.vs! * - 0.444), - RadarEntry( - value: snapshot.data! - .tlSeason1.app! * - 185), - RadarEntry( - value: snapshot.data! - .tlSeason1.dss! * - 175), - RadarEntry( - value: snapshot.data! - .tlSeason1.dsp! * - 450), - RadarEntry( - value: snapshot.data! - .tlSeason1.appdsp! * - 140), - RadarEntry( - value: snapshot.data! - .tlSeason1.vsapm! * - 60), - RadarEntry( - value: snapshot.data! - .tlSeason1.cheese! * - 1.25), - RadarEntry( - value: snapshot.data! - .tlSeason1.gbe! * - 315), + if (snapshot.data!.tlSeason1.estTr != null) + Padding( + padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), + child: SizedBox( + width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + "Est. of TR:", + style: TextStyle(fontSize: 24), + ), + Text( + snapshot.data!.tlSeason1.estTr!.esttr.toStringAsFixed(2), + style: const TextStyle(fontSize: 24), + ), ], ), - RadarDataSet( - fillColor: Colors.transparent, - borderColor: Colors.transparent, - dataEntries: [ - RadarEntry(value: 0), - RadarEntry(value: 0), - RadarEntry(value: 0), - RadarEntry(value: 0), - RadarEntry(value: 0), - RadarEntry(value: 0), - RadarEntry(value: 0), - RadarEntry(value: 0), - RadarEntry(value: 0), - RadarEntry(value: 0), - ], - ) + if (snapshot.data!.tlSeason1.rating >= 0) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + "Accuracy:", + style: TextStyle(fontSize: 24), + ), + Text( + snapshot.data!.tlSeason1.esttracc!.toStringAsFixed(2), + style: const TextStyle(fontSize: 24), + ), + ], + ), ], ), - swapAnimationDuration: const Duration( - milliseconds: 150), // Optional - swapAnimationCurve: - Curves.linear, // Optional ), ), - ) + if (snapshot.data!.tlSeason1.nerdStats != null) + Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.spaceAround, + spacing: 25, + crossAxisAlignment: WrapCrossAlignment.start, + clipBehavior: Clip.hardEdge, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 48), + child: SizedBox( + height: 300, + width: 300, + child: RadarChart( + RadarChartData( + radarShape: RadarShape.polygon, + tickCount: 4, + ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10), + radarBorderData: BorderSide(color: Colors.transparent, width: 1), + gridBorderData: BorderSide(color: Colors.white24, width: 1), + tickBorderData: BorderSide(color: Colors.transparent, width: 1), + getTitle: (index, angle) { + switch (index) { + case 0: + return RadarChartTitle( + text: 'APM', + angle: angle, + ); + case 1: + return RadarChartTitle( + text: 'PPS', + angle: angle, + ); + case 2: + return RadarChartTitle(text: 'VS', angle: angle); + case 3: + return RadarChartTitle(text: 'APP', angle: angle + 180); + case 4: + return RadarChartTitle(text: 'DS/S', angle: angle + 180); + case 5: + return RadarChartTitle(text: 'DS/P', angle: angle + 180); + case 6: + return RadarChartTitle(text: 'APP+DS/P', angle: angle + 180); + case 7: + return RadarChartTitle(text: 'VS/APM', angle: angle + 180); + case 8: + return RadarChartTitle(text: 'Cheese', angle: angle); + case 9: + return RadarChartTitle(text: 'Gb Eff.', angle: angle); + default: + return const RadarChartTitle(text: ''); + } + }, + dataSets: [ + RadarDataSet( + dataEntries: [ + RadarEntry(value: snapshot.data!.tlSeason1.apm! * 1), + RadarEntry(value: snapshot.data!.tlSeason1.pps! * 45), + RadarEntry(value: snapshot.data!.tlSeason1.vs! * 0.444), + RadarEntry(value: snapshot.data!.tlSeason1.nerdStats!.app * 185), + RadarEntry(value: snapshot.data!.tlSeason1.nerdStats!.dss * 175), + RadarEntry(value: snapshot.data!.tlSeason1.nerdStats!.dsp * 450), + RadarEntry(value: snapshot.data!.tlSeason1.nerdStats!.appdsp * 140), + RadarEntry(value: snapshot.data!.tlSeason1.nerdStats!.vsapm * 60), + RadarEntry(value: snapshot.data!.tlSeason1.nerdStats!.cheese * 1.25), + RadarEntry(value: snapshot.data!.tlSeason1.nerdStats!.gbe * 315), + ], + ), + RadarDataSet( + fillColor: Colors.transparent, + borderColor: Colors.transparent, + dataEntries: [ + RadarEntry(value: 0), + RadarEntry(value: 0), + RadarEntry(value: 0), + RadarEntry(value: 0), + RadarEntry(value: 0), + RadarEntry(value: 0), + RadarEntry(value: 0), + RadarEntry(value: 0), + RadarEntry(value: 0), + RadarEntry(value: 0), + ], + ) + ], + ), + swapAnimationDuration: const Duration(milliseconds: 150), // Optional + swapAnimationCurve: Curves.linear, // Optional + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 48), + child: SizedBox( + height: 300, + width: 300, + child: RadarChart( + RadarChartData( + radarShape: RadarShape.polygon, + tickCount: 4, + ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10), + radarBorderData: BorderSide(color: Colors.transparent, width: 1), + gridBorderData: BorderSide(color: Colors.white24, width: 1), + tickBorderData: BorderSide(color: Colors.transparent, width: 1), + getTitle: (index, angle) { + switch (index) { + case 0: + return RadarChartTitle( + text: 'Opener', + angle: angle, + ); + case 1: + return RadarChartTitle( + text: 'Stride', + angle: angle, + ); + case 2: + return RadarChartTitle(text: 'Inf Ds', angle: angle + 180); + case 3: + return RadarChartTitle(text: 'Plonk', angle: angle); + default: + return const RadarChartTitle(text: ''); + } + }, + dataSets: [ + RadarDataSet( + dataEntries: [ + RadarEntry(value: snapshot.data!.tlSeason1.playstyle!.opener), + RadarEntry(value: snapshot.data!.tlSeason1.playstyle!.stride), + RadarEntry(value: snapshot.data!.tlSeason1.playstyle!.infds), + RadarEntry(value: snapshot.data!.tlSeason1.playstyle!.plonk), + ], + ), + RadarDataSet( + fillColor: Colors.transparent, + borderColor: Colors.transparent, + dataEntries: [ + RadarEntry(value: 0), + RadarEntry(value: 0), + RadarEntry(value: 0), + RadarEntry(value: 0), + ], + ), + RadarDataSet( + fillColor: Colors.transparent, + borderColor: Colors.transparent, + dataEntries: [ + RadarEntry(value: 1), + RadarEntry(value: 1), + RadarEntry(value: 1), + RadarEntry(value: 1), + ], + ) + ], + ), + swapAnimationDuration: const Duration(milliseconds: 150), // Optional + swapAnimationCurve: Curves.linear, // Optional + ), + ), + ), + ], + ) ] : [ Text("That user never played Tetra League", - style: TextStyle( - fontFamily: - "Eurostile Round Extended", - fontSize: bigScreen ? 42 : 28)), + style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), ], ); }, ), - Column( - children: (snapshot.data!.sprint.isNotEmpty) - ? [] - : [ - Text("That user never played 40 Lines", - style: TextStyle( - fontFamily: "Eurostile Round Extended", - fontSize: bigScreen ? 42 : 28)) + ListView.builder( + physics: const ClampingScrollPhysics(), + itemCount: 1, + itemBuilder: (BuildContext context, int index) { + return Column( + children: (snapshot.data!.sprint.isNotEmpty) + ? [ + Text("40 Lines", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + Text(snapshot.data!.sprint[0]!.endContext!.finalTime.toString(), + style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + if (snapshot.data!.sprint[0]!.rank != null) + _StatCellNum( + playerStat: snapshot.data!.sprint[0]!.rank!, playerStatLabel: "Leaderboard Placement", isScreenBig: bigScreen), + Padding( + padding: const EdgeInsets.fromLTRB(0, 48, 0, 48), + child: Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.spaceAround, + crossAxisAlignment: WrapCrossAlignment.center, + clipBehavior: Clip.hardEdge, + spacing: 25, + children: [ + _StatCellNum( + playerStat: snapshot.data!.sprint[0]!.endContext!.piecesPlaced, + playerStatLabel: "Pieces\nPlaced", + isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.sprint[0]!.endContext!.pps, + playerStatLabel: "Pieces\nPer Second", + fractionDigits: 2, + isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.sprint[0]!.endContext!.finesse.faults, + playerStatLabel: "Finesse\nFaults", + isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.sprint[0]!.endContext!.finessePercentage * 100, + playerStatLabel: "Finesse\nPercentage", + fractionDigits: 2, + isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.sprint[0]!.endContext!.inputs, + playerStatLabel: "Key\nPresses", + isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.sprint[0]!.endContext!.kpp, + playerStatLabel: "KP Per\nPiece", + fractionDigits: 2, + isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.sprint[0]!.endContext!.kps, + playerStatLabel: "KP Per\nSecond", + fractionDigits: 2, + isScreenBig: bigScreen), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), + child: SizedBox( + width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("All Clears:", style: TextStyle(fontSize: 24)), + Text( + snapshot.data!.sprint[0]!.endContext!.clears.allClears.toString(), + style: const TextStyle(fontSize: 24), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("Holds:", style: TextStyle(fontSize: 24)), + Text( + snapshot.data!.sprint[0]!.endContext!.holds.toString(), + style: const TextStyle(fontSize: 24), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("T-spins total:", style: TextStyle(fontSize: 24)), + Text( + snapshot.data!.sprint[0]!.endContext!.tSpins.toString(), + style: const TextStyle(fontSize: 24), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin zero:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.sprint[0]!.endContext!.clears.tSpinZeros.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin singles:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.sprint[0]!.endContext!.clears.tSpinSingles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin doubles:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.sprint[0]!.endContext!.clears.tSpinDoubles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin triples:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.sprint[0]!.endContext!.clears.tSpinTriples.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin mini zero:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.sprint[0]!.endContext!.clears.tSpinMiniZeros.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin mini singles:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.sprint[0]!.endContext!.clears.tSpinMiniSingles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin mini doubles:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.sprint[0]!.endContext!.clears.tSpinMiniDoubles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("Line clears:", style: TextStyle(fontSize: 24)), + Text( + snapshot.data!.sprint[0]!.endContext!.lines.toString(), + style: const TextStyle(fontSize: 24), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - Singles:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.sprint[0]!.endContext!.clears.singles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - Doubles:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.sprint[0]!.endContext!.clears.doubles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - Triples:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.sprint[0]!.endContext!.clears.triples.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - Quads:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.sprint[0]!.endContext!.clears.quads.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + ], + ), + ), + ), + ] + : [ + Text("That user never played 40 Lines", + style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)) + ], + ); + }), + ListView.builder( + physics: const ClampingScrollPhysics(), + itemCount: 1, + itemBuilder: (BuildContext context, int index) { + return Column( + children: (snapshot.data!.blitz.isNotEmpty) + ? [ + Text("Blitz", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + Text(snapshot.data!.blitz[0]!.endContext!.score.toString(), + style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + if (snapshot.data!.blitz[0]!.rank != null) + _StatCellNum( + playerStat: snapshot.data!.blitz[0]!.rank!, playerStatLabel: "Leaderboard Placement", isScreenBig: bigScreen), + Padding( + padding: const EdgeInsets.fromLTRB(0, 48, 0, 48), + child: Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.spaceAround, + crossAxisAlignment: WrapCrossAlignment.start, + clipBehavior: Clip.hardEdge, + spacing: 25, + children: [ + _StatCellNum( + playerStat: snapshot.data!.blitz[0]!.endContext!.level, playerStatLabel: "Level", isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.blitz[0]!.endContext!.spp, + playerStatLabel: "Score\nPer Piece", + fractionDigits: 2, + isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.blitz[0]!.endContext!.piecesPlaced, + playerStatLabel: "Pieces\nPlaced", + isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.blitz[0]!.endContext!.pps, + playerStatLabel: "Pieces\nPer Second", + fractionDigits: 2, + isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.blitz[0]!.endContext!.finesse.faults, + playerStatLabel: "Finesse\nFaults", + isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.blitz[0]!.endContext!.finessePercentage * 100, + playerStatLabel: "Finesse\nPercentage", + fractionDigits: 2, + isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.blitz[0]!.endContext!.inputs, playerStatLabel: "Key\nPresses", isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.blitz[0]!.endContext!.kpp, + playerStatLabel: "KP Per\nPiece", + fractionDigits: 2, + isScreenBig: bigScreen), + _StatCellNum( + playerStat: snapshot.data!.blitz[0]!.endContext!.kps, + playerStatLabel: "KP Per\nSecond", + fractionDigits: 2, + isScreenBig: bigScreen), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), + child: SizedBox( + width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("All Clears:", style: TextStyle(fontSize: 24)), + Text( + snapshot.data!.blitz[0]!.endContext!.clears.allClears.toString(), + style: const TextStyle(fontSize: 24), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("Holds:", style: TextStyle(fontSize: 24)), + Text( + snapshot.data!.blitz[0]!.endContext!.holds.toString(), + style: const TextStyle(fontSize: 24), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("T-spins total:", style: TextStyle(fontSize: 24)), + Text( + snapshot.data!.blitz[0]!.endContext!.tSpins.toString(), + style: const TextStyle(fontSize: 24), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin zero:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.blitz[0]!.endContext!.clears.tSpinZeros.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin singles:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.blitz[0]!.endContext!.clears.tSpinSingles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin doubles:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.blitz[0]!.endContext!.clears.tSpinDoubles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin triples:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.blitz[0]!.endContext!.clears.tSpinTriples.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin mini zero:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.blitz[0]!.endContext!.clears.tSpinMiniZeros.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin mini singles:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.blitz[0]!.endContext!.clears.tSpinMiniSingles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin mini doubles:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.blitz[0]!.endContext!.clears.tSpinMiniDoubles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("Line clears:", style: TextStyle(fontSize: 24)), + Text( + snapshot.data!.blitz[0]!.endContext!.lines.toString(), + style: const TextStyle(fontSize: 24), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - Singles:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.blitz[0]!.endContext!.clears.singles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - Doubles:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.blitz[0]!.endContext!.clears.doubles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - Triples:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.blitz[0]!.endContext!.clears.triples.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - Quads:", style: TextStyle(fontSize: 18)), + Text( + snapshot.data!.blitz[0]!.endContext!.clears.quads.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + ], + ), + ), + ), + ] + : [ + Text("That user never played Blitz", + style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)) + ], + ); + }), + ListView.builder( + physics: const ClampingScrollPhysics(), + itemCount: 1, + itemBuilder: (BuildContext context, int index) { + return Column( + children: [ + Text("Other info", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + if (snapshot.data!.zen != null) + Padding( + padding: const EdgeInsets.fromLTRB(0, 48, 0, 48), + child: Column( + children: [ + Text("Zen", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + Text("Level ${snapshot.data!.zen!.level}", + style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + Text( + "Score ${snapshot.data!.zen!.score}", + style: const TextStyle(fontSize: 18), + ), + ], + ), + ), + if (snapshot.data!.bio != null) + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 48), + child: Column( + children: [ + Text("Bio", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + Text( + snapshot.data!.bio!, + style: const TextStyle(fontSize: 18), + ), + ], + ), + ), ], - ), - Container( - child: Text("Blitz", - style: TextStyle( - fontFamily: "Eurostile Round Extended", - fontSize: bigScreen ? 42 : 28)), - ), - Container( - child: Text("Other info", - style: TextStyle( - fontFamily: "Eurostile Round Extended", - fontSize: bigScreen ? 42 : 28)), - ), + ); + }) ], ), ); } else if (snapshot.hasError) { - return Center( - child: Text('${snapshot.error}', - style: const TextStyle( - fontFamily: "Eurostile Round Extended", - fontSize: 42))); + return Center(child: Text('${snapshot.error}', style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42))); } return const Center( child: CircularProgressIndicator( @@ -691,83 +1105,17 @@ class NavDrawer extends StatelessWidget { children: [ const DrawerHeader( child: Text( - 'Side menu', + 'Players you track', style: TextStyle(color: Colors.white, fontSize: 25), )), - ListTile( - leading: const Icon(Icons.input), - title: const Text('Welcome'), - onTap: () => {}, - ), ListTile( leading: const Icon(Icons.verified_user), - title: const Text('Profile'), - onTap: () => {Navigator.of(context).pop()}, - ), - ListTile( - leading: const Icon(Icons.settings), - title: const Text('Settings'), - onTap: () => {Navigator.of(context).pop()}, - ), - ListTile( - leading: const Icon(Icons.border_color), - title: const Text('Feedback'), - onTap: () => {Navigator.of(context).pop()}, - ), - ListTile( - leading: const Icon(Icons.exit_to_app), - title: const Text('Logout'), - onTap: () => {Navigator.of(context).pop()}, - ), - ListTile( - leading: const Icon(Icons.input), - title: const Text('Welcome'), - onTap: () => {}, - ), - ListTile( - leading: const Icon(Icons.verified_user), - title: const Text('Profile'), - onTap: () => {Navigator.of(context).pop()}, - ), - ListTile( - leading: const Icon(Icons.settings), - title: const Text('Settings'), - onTap: () => {Navigator.of(context).pop()}, - ), - ListTile( - leading: const Icon(Icons.border_color), - title: const Text('Feedback'), - onTap: () => {Navigator.of(context).pop()}, - ), - ListTile( - leading: const Icon(Icons.exit_to_app), - title: const Text('Logout'), - onTap: () => {Navigator.of(context).pop()}, - ), - ListTile( - leading: const Icon(Icons.input), - title: const Text('Welcome'), - onTap: () => {}, - ), - ListTile( - leading: const Icon(Icons.verified_user), - title: const Text('Profile'), - onTap: () => {Navigator.of(context).pop()}, - ), - ListTile( - leading: const Icon(Icons.settings), - title: const Text('Settings'), - onTap: () => {Navigator.of(context).pop()}, - ), - ListTile( - leading: const Icon(Icons.border_color), - title: const Text('Feedback'), - onTap: () => {Navigator.of(context).pop()}, - ), - ListTile( - leading: const Icon(Icons.exit_to_app), - title: const Text('Logout'), - onTap: () => {Navigator.of(context).pop()}, + title: const Text('dan63047'), + onTap: () { + me = fetchTetrioPlayer("dan63047"); + Navigator.of(context).pop(); + Navigator.of(context).initState(); + }, ), ], ), @@ -776,12 +1124,7 @@ class NavDrawer extends StatelessWidget { } class _StatCellNum extends StatelessWidget { - const _StatCellNum( - {required this.playerStat, - required this.playerStatLabel, - required this.isScreenBig, - this.snackBar, - this.fractionDigits}); + const _StatCellNum({required this.playerStat, required this.playerStatLabel, required this.isScreenBig, this.snackBar, this.fractionDigits}); final num playerStat; final String playerStatLabel; @@ -794,9 +1137,7 @@ class _StatCellNum extends StatelessWidget { return Column( children: [ Text( - fractionDigits != null - ? playerStat.toStringAsFixed(fractionDigits!) - : playerStat.floor().toString(), + fractionDigits != null ? playerStat.toStringAsFixed(fractionDigits!) : playerStat.floor().toString(), style: TextStyle( fontFamily: "Eurostile Round Extended", fontSize: isScreenBig ? 32 : 24, @@ -813,11 +1154,9 @@ class _StatCellNum extends StatelessWidget { ) : TextButton( onPressed: () { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text(snackBar!))); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(snackBar!))); }, - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero)), + style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.zero)), child: Text( playerStatLabel, textAlign: TextAlign.center, @@ -858,13 +1197,7 @@ class _UserThingy extends StatelessWidget { height: bannerHeight, ), Container( - padding: EdgeInsets.fromLTRB( - 0, - player.bannerRevision != null - ? bannerHeight / 1.4 - : pfpHeight, - 0, - 0), + padding: EdgeInsets.fromLTRB(0, player.bannerRevision != null ? bannerHeight / 1.4 : pfpHeight, 0, 0), child: ClipRRect( borderRadius: BorderRadius.circular(1000), child: player.role == "banned" @@ -877,8 +1210,7 @@ class _UserThingy extends StatelessWidget { "https://tetr.io/user-content/avatars/${player.userId}.jpg?rv=${player.avatarRevision}", fit: BoxFit.fitHeight, height: 128, - errorBuilder: (context, error, stackTrace) => - Image.asset( + errorBuilder: (context, error, stackTrace) => Image.asset( "res/avatars/tetrio_anon.png", fit: BoxFit.fitHeight, height: 128, @@ -891,15 +1223,10 @@ class _UserThingy extends StatelessWidget { Flexible( child: Column( children: [ - Text(player.username, - style: TextStyle( - fontFamily: "Eurostile Round Extended", - fontSize: bigScreen ? 42 : 28)), + Text(player.username, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), Text( player.userId, - style: const TextStyle( - fontFamily: "Eurostile Round Condensed", - fontSize: 14), + style: const TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: 14), ), ], ), @@ -918,8 +1245,7 @@ class _UserThingy extends StatelessWidget { playerStat: player.level, playerStatLabel: "XP Level", isScreenBig: bigScreen, - snackBar: - "${player.xp.floor().toString()} XP, ${((player.level - player.level.floor()) * 100).toStringAsFixed(2)} % until next level", + snackBar: "${player.xp.floor().toString()} XP, ${((player.level - player.level.floor()) * 100).toStringAsFixed(2)} % until next level", ), if (player.gameTime >= Duration.zero) _StatCellNum( @@ -928,21 +1254,9 @@ class _UserThingy extends StatelessWidget { isScreenBig: bigScreen, snackBar: player.gameTime.toString(), ), - if (player.gamesPlayed >= 0) - _StatCellNum( - playerStat: player.gamesPlayed, - isScreenBig: bigScreen, - playerStatLabel: "Online\nGames"), - if (player.gamesWon >= 0) - _StatCellNum( - playerStat: player.gamesWon, - isScreenBig: bigScreen, - playerStatLabel: "Games\nWon"), - if (player.friendCount > 0) - _StatCellNum( - playerStat: player.friendCount, - isScreenBig: bigScreen, - playerStatLabel: "Friends"), + if (player.gamesPlayed >= 0) _StatCellNum(playerStat: player.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Online\nGames"), + if (player.gamesWon >= 0) _StatCellNum(playerStat: player.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nWon"), + if (player.friendCount > 0) _StatCellNum(playerStat: player.friendCount, isScreenBig: bigScreen, playerStatLabel: "Friends"), ], ) : Text( @@ -996,8 +1310,7 @@ class _UserThingy extends StatelessWidget { return AlertDialog( title: Text( badge.label, - style: const TextStyle( - fontFamily: "Eurostile Round Extended"), + style: const TextStyle(fontFamily: "Eurostile Round Extended"), ), content: SingleChildScrollView( child: ListBody( @@ -1005,15 +1318,11 @@ class _UserThingy extends StatelessWidget { Wrap( direction: Axis.horizontal, alignment: WrapAlignment.center, - crossAxisAlignment: - WrapCrossAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, spacing: 25, children: [ - Image.asset( - "res/tetrio_badges/${badge.badgeId}.png"), - Text(badge.ts != null - ? "Obtained ${badge.ts}" - : "That badge was assigned manualy by TETR.IO admins"), + Image.asset("res/tetrio_badges/${badge.badgeId}.png"), + Text(badge.ts != null ? "Obtained ${badge.ts}" : "That badge was assigned manualy by TETR.IO admins"), ], ) ], diff --git a/pubspec.yaml b/pubspec.yaml index 9cdc6d0..51ec94d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -109,6 +109,7 @@ flutter: - res/tetrio_badges/sfu_raccoon_1.png - res/tetrio_badges/superlobby.png - res/tetrio_badges/superlobby2.png + - res/tetrio_badges/taws_u50_1.png - res/tetrio_badges/tawshdsl_uncapped.png - res/tetrio_badges/tawsignite_expert.png - res/tetrio_badges/tawslg.png diff --git a/res/tetrio_badges/taws_u50_1.png b/res/tetrio_badges/taws_u50_1.png new file mode 100644 index 0000000..ae1137f Binary files /dev/null and b/res/tetrio_badges/taws_u50_1.png differ