From 18f02a9c54259398e61c5a492f523a371d4ee8ae Mon Sep 17 00:00:00 2001 From: dan63047 Date: Thu, 8 Jun 2023 00:12:21 +0300 Subject: [PATCH] Thinking about how to use TetrioService --- lib/data_objects/tetrio.dart | 81 +- lib/main.dart | 8 +- lib/services/sqlite_db_controller.dart | 8 + lib/services/tetrio_crud.dart | 40 +- lib/views/calc_view.dart | 21 + lib/views/main_view.dart | 1475 ++++++++++-------------- lib/views/settings_view.dart | 2 +- 7 files changed, 725 insertions(+), 910 deletions(-) create mode 100644 lib/views/calc_view.dart diff --git a/lib/data_objects/tetrio.dart b/lib/data_objects/tetrio.dart index ad026ce..b3c9c3f 100644 --- a/lib/data_objects/tetrio.dart +++ b/lib/data_objects/tetrio.dart @@ -33,7 +33,7 @@ class TetrioPlayer { late int supporterTier; late bool verified; bool? badstanding; - bool? bot; + String? botmaster; late Connections connections; late TetraLeagueAlpha tlSeason1; List sprint = []; @@ -60,7 +60,7 @@ class TetrioPlayer { required this.supporterTier, required this.verified, this.badstanding, - this.bot, + this.botmaster, required this.connections, required this.tlSeason1, required this.sprint, @@ -98,6 +98,7 @@ class TetrioPlayer { distinguishment = json['distinguishment'] != null ? Distinguishment.fromJson(json['distinguishment']) : null; friendCount = json['friend_count'] ?? 0; badstanding = json['badstanding']; + botmaster = json['botmaster']; if (fetchRecords) { var url = Uri.https('ch.tetr.io', 'api/users/$userId/records'); Future response = http.get(url); @@ -124,25 +125,25 @@ class TetrioPlayer { data['_id'] = userId; data['username'] = username; data['role'] = role; - data['ts'] = registrationTime?.toString(); + if (registrationTime != null) data['ts'] = registrationTime?.toString(); data['badges'] = badges.map((v) => v.toJson()).toList(); data['xp'] = xp; data['gamesplayed'] = gamesPlayed; data['gameswon'] = gamesWon; data['gametime'] = gameTime.inMicroseconds / 1000000; - data['country'] = country; + if (country != null) data['country'] = country; data['supporter_tier'] = supporterTier; data['verified'] = verified; data['league'] = tlSeason1.toJson(); - data['distinguishment'] = distinguishment?.toJson(); - data['avatar_revision'] = avatarRevision; - data['banner_revision'] = bannerRevision; - data['bio'] = bio; + if (distinguishment != null) data['distinguishment'] = distinguishment?.toJson(); + if (avatarRevision != null) data['avatar_revision'] = avatarRevision; + if (bannerRevision != null) data['banner_revision'] = bannerRevision; + if (data['bio'] != null) data['bio'] = bio; data['connections'] = connections.toJson(); data['friend_count'] = friendCount; - data['badstanding'] = badstanding; - data['bot'] = bot; - developer.log("TetrioPlayer.toJson: $bot", name: "data_objects/tetrio"); + if (badstanding != null) data['badstanding'] = badstanding; + if (botmaster != null) data['botmaster'] = botmaster; + developer.log("TetrioPlayer.toJson: $data", name: "data_objects/tetrio"); return data; } @@ -161,7 +162,7 @@ class TetrioPlayer { if (supporterTier != other.supporterTier) return false; if (verified != other.verified) return false; if (badstanding != other.badstanding) return false; - if (bot != other.bot) return false; + if (botmaster != other.botmaster) return false; if (connections != other.connections) return false; if (tlSeason1 != other.tlSeason1) return false; if (distinguishment != other.distinguishment) return false; @@ -170,7 +171,7 @@ class TetrioPlayer { @override String toString() { - return "$username ($userId)"; + return "$username ($state)"; } @override @@ -678,20 +679,20 @@ class TetraLeagueAlpha { data['gamesplayed'] = gamesPlayed; data['gameswon'] = gamesWon; data['rating'] = rating; - data['glicko'] = glicko; - data['rd'] = rd; + if (glicko != null) data['glicko'] = glicko; + if (rd != null) data['rd'] = rd; data['rank'] = rank; data['bestrank'] = bestRank; - data['apm'] = apm; - data['pps'] = pps; - data['vs'] = vs; + if (apm != null) data['apm'] = apm; + if (pps != null) data['pps'] = pps; + if (vs != null) data['vs'] = vs; data['decaying'] = decaying; data['standing'] = standing; data['percentile'] = percentile; data['standing_local'] = standingLocal; - data['prev_rank'] = prevRank; + if (prevRank != null) data['prev_rank'] = prevRank; data['prev_at'] = prevAt; - data['next_rank'] = nextRank; + if (nextRank != null) data['next_rank'] = nextRank; data['next_at'] = nextAt; data['percentile_rank'] = percentileRank; return data; @@ -710,7 +711,7 @@ class RecordSingle { RecordSingle({required this.userId, required this.replayId, required this.ownId, this.timestamp, this.endContext, this.rank}); RecordSingle.fromJson(Map json, int? ran) { - developer.log("RecordSingle.fromJson: $json", name: "data_objects/tetrio"); + //developer.log("RecordSingle.fromJson: $json", name: "data_objects/tetrio"); ownId = json['_id']; endContext = json['endcontext'] != null ? EndContextSingle.fromJson(json['endcontext']) : null; replayId = json['replayid']; @@ -777,3 +778,41 @@ class Distinguishment { return data; } } + +class TetrioPlayersLeaderboard { + late String type; + late List leaderboard; + + TetrioPlayersLeaderboard(this.type, this.leaderboard); + + TetrioPlayersLeaderboard.fromJson(Map json, String type) { + type = type; + for (Map entry in json['users']) { + leaderboard.add(TetrioPlayerFromLeaderboard.fromJson(entry)); + } + } +} + +class TetrioPlayerFromLeaderboard { + late String userId; + late String username; + late String role; + late double xp; + String? country; + late bool supporter; + late bool verified; + late TetraLeagueAlpha league; + + TetrioPlayerFromLeaderboard(this.userId, this.username, this.role, this.xp, this.country, this.supporter, this.verified, this.league); + + TetrioPlayerFromLeaderboard.fromJson(Map json) { + userId = json['_id']; + username = json['username']; + role = json['role']; + xp = json['xp'].toDouble(); + country = json['country ']; + supporter = json['supporter']; + verified = json['verified']; + league = TetraLeagueAlpha.fromJson(json['league']); + } +} diff --git a/lib/main.dart b/lib/main.dart index 3a7b3c6..3de6c39 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,13 +4,19 @@ import 'package:tetra_stats/views/main_view.dart'; import 'package:tetra_stats/views/compare_view.dart'; import 'package:tetra_stats/views/settings_view.dart'; import 'package:tetra_stats/views/states_view.dart'; +import 'package:tetra_stats/views/calc_view.dart'; void main() { sqfliteFfiInit(); databaseFactory = databaseFactoryFfi; runApp(MaterialApp( home: const MainView(), - routes: {"/settings": (context) => const SettingsView(), "/compare": (context) => const CompareView(), "/states": (context) => const StatesView()}, + routes: { + "/settings": (context) => const SettingsView(), + "/compare": (context) => const CompareView(), + "/states": (context) => const StatesView(), + "/calc": (context) => const CalcView() + }, theme: ThemeData( fontFamily: 'Eurostile Round', colorScheme: const ColorScheme.dark(primary: Colors.cyanAccent, secondary: Colors.purpleAccent), diff --git a/lib/services/sqlite_db_controller.dart b/lib/services/sqlite_db_controller.dart index 059fb28..d59e7b7 100644 --- a/lib/services/sqlite_db_controller.dart +++ b/lib/services/sqlite_db_controller.dart @@ -42,4 +42,12 @@ class DB { return db; } } + + Future _ensureDbIsOpen(DB udb) async { + try { + await udb.open(); + } on DatabaseAlreadyOpen { + // empty + } + } } diff --git a/lib/services/tetrio_crud.dart b/lib/services/tetrio_crud.dart index b23b34d..cf6939f 100644 --- a/lib/services/tetrio_crud.dart +++ b/lib/services/tetrio_crud.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:developer' as developer; +import 'package:http/http.dart' as http; // import 'package:sqflite/sqflite.dart'; // import 'package:path_provider/path_provider.dart' show MissingPlatformDirectoryException, getApplicationDocumentsDirectory; // import 'package:path/path.dart' show join; @@ -31,7 +32,7 @@ class TetrioService { Future _cachePlayers(DB udb) async { final allPlayers = await getAllPlayers(udb: udb); - _players = allPlayers.first; + _players = allPlayers.toList().first; // ??? _tetrioStreamController.add(_players); developer.log("_cachePlayers: $_players", name: "services/tetrio_crud"); } @@ -58,6 +59,7 @@ class TetrioService { // } Future createPlayer({required TetrioPlayer tetrioPlayer, required DB udb}) async { + _ensureDbIsOpen(udb); final db = udb.getDatabaseOrThrow(); final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [tetrioPlayer.userId.toLowerCase()]); if (results.isNotEmpty) { @@ -72,9 +74,16 @@ class TetrioService { } Future storeState(TetrioPlayer tetrioPlayer, DB udb) async { + _ensureDbIsOpen(udb); final db = udb.getDatabaseOrThrow(); - List states = await getPlayer(id: tetrioPlayer.userId, udb: udb); - states.add(tetrioPlayer); + late List states; + try { + states = await getPlayer(id: tetrioPlayer.userId, udb: udb); + } on TetrioPlayerNotExist { + await createPlayer(tetrioPlayer: tetrioPlayer, udb: udb); + states = await getPlayer(id: tetrioPlayer.userId, udb: udb); + } + if (!_players[tetrioPlayer.userId]!.last.isSameState(tetrioPlayer)) states.add(tetrioPlayer); final Map statesJson = {}; for (var e in states) { statesJson.addEntries({e.state.millisecondsSinceEpoch.toString(): e.toJson()}.entries); @@ -86,6 +95,7 @@ class TetrioService { } Future> getPlayer({required String id, required DB udb}) async { + _ensureDbIsOpen(udb); final db = udb.getDatabaseOrThrow(); List states = []; final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]); @@ -102,6 +112,29 @@ class TetrioService { } } + Future fetchPlayer(String user, DB udb, bool addToDB) async { + var url = Uri.https('ch.tetr.io', 'api/users/${user.toLowerCase().trim()}'); + final response = await http.get(url); + + if (response.statusCode == 200) { + if (jsonDecode(response.body)['success']) { + TetrioPlayer player = TetrioPlayer.fromJson( + jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true), true); + if (addToDB) { + _ensureDbIsOpen(udb); + storeState(player, udb); + } + return player; + } else { + developer.log("fetchTetrioPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body); + throw Exception("User doesn't exist"); + } + } else { + developer.log("fetchTetrioPlayer Failed to fetch player", name: "services/tetrio_crud", error: response.statusCode); + throw Exception('Failed to fetch player'); + } + } + Future _ensureDbIsOpen(DB udb) async { try { await udb.open(); @@ -117,6 +150,7 @@ class TetrioService { Map> data = {}; //developer.log("getAllPlayers: $players", name: "services/tetrio_crud"); return players.map((row) { + // what the fuck am i doing here? var test = json.decode(row['jsonStates'] as String); List states = []; test.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k)), false))); diff --git a/lib/views/calc_view.dart b/lib/views/calc_view.dart new file mode 100644 index 0000000..c012a5c --- /dev/null +++ b/lib/views/calc_view.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class CalcView extends StatefulWidget { + const CalcView({Key? key}) : super(key: key); + + @override + State createState() => CalcState(); +} + +class CalcState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Stats Calculator"), + ), + backgroundColor: Colors.black, + body: const SafeArea(child: Text("Maybe next commit idk... or shoud i think about CRUD??? idk idk")), + ); + } +} diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 83e5e11..18f256f 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -18,7 +18,7 @@ extension StringExtension on String { String _searchFor = "dan63047"; Future? me; DB db = DB(); -late TetrioService teto; +late final TetrioService teto; late SharedPreferences prefs; const allowedHeightForPlayerIdInPixels = 40.0; const allowedHeightForPlayerBioInPixels = 30.0; @@ -37,24 +37,6 @@ Future copyToClipboard(String text) async { await Clipboard.setData(ClipboardData(text: text)); } -Future fetchTetrioPlayer(String user) async { - var url = Uri.https('ch.tetr.io', 'api/users/${user.toLowerCase().trim()}'); - final response = await http.get(url); - - if (response.statusCode == 200) { - if (jsonDecode(response.body)['success']) { - return TetrioPlayer.fromJson( - jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true), true); - } else { - developer.log("fetchTetrioPlayer User dosen't exist", name: "main_view", error: response.body); - throw Exception("User doesn't exist"); - } - } else { - developer.log("fetchTetrioPlayer Failed to fetch player", name: "main_view", error: response.statusCode); - throw Exception('Failed to fetch player'); - } -} - class _MainState extends State with SingleTickerProviderStateMixin { final bodyGlobalKey = GlobalKey(); final List myTabs = [ @@ -107,6 +89,7 @@ class _MainState extends State with SingleTickerProviderStateMixin { void dispose() { _tabController.dispose(); _scrollController.dispose(); + db.close(); super.dispose(); developer.log("Main view disposed", name: "main_view"); } @@ -119,7 +102,7 @@ class _MainState extends State with SingleTickerProviderStateMixin { setState(() { _tabController.animateTo(0, duration: const Duration(milliseconds: 300)); _searchFor = player; - me = fetchTetrioPlayer(player); + me = teto.fetchPlayer(player, db, false); }); } @@ -192,6 +175,10 @@ class _MainState extends State with SingleTickerProviderStateMixin { value: "/states", child: Text('States'), ), + const PopupMenuItem( + value: "/calc", + child: Text('Stats Calculator'), + ), const PopupMenuItem( value: "/settings", child: Text('Settings'), @@ -215,7 +202,7 @@ class _MainState extends State with SingleTickerProviderStateMixin { )); } if (snapshot.hasData) { - bool bigScreen = MediaQuery.of(context).size.width > 768; + bool bigScreen = MediaQuery.of(context).size.width > 1024; return NestedScrollView( controller: _scrollController, headerSliverBuilder: (context, value) { @@ -238,869 +225,10 @@ class _MainState extends State with SingleTickerProviderStateMixin { body: TabBarView( controller: _tabController, children: [ - ListView.builder( - physics: const ClampingScrollPhysics(), - itemCount: 1, - itemBuilder: (BuildContext context, int index) { - 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) - Wrap( - direction: Axis.horizontal, - alignment: WrapAlignment.spaceAround, - crossAxisAlignment: WrapCrossAlignment.center, - clipBehavior: Clip.hardEdge, - children: [ - snapshot.data!.userId == "5e32fc85ab319c2ab1beb07c" - ? Image.asset( - "res/icons/kagari.png", - height: 128, - ) - : Image.asset( - "res/tetrio_tl_alpha_ranks/${snapshot.data!.tlSeason1.rank}.png", - height: 128, - ), - Column( - children: [ - 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, - ), - ], - ), - ], - ) - else - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Column( - children: [ - Text("${10 - snapshot.data!.tlSeason1.gamesPlayed} games until being ranked", - softWrap: true, - style: TextStyle( - fontFamily: "Eurostile Round Extended", - fontSize: bigScreen ? 42 : 28, - overflow: TextOverflow.visible, - )), - ], - ) - ], - ), - Padding( - padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), - child: Wrap( - direction: Axis.horizontal, - alignment: WrapAlignment.center, - spacing: 25, - crossAxisAlignment: WrapCrossAlignment.start, - clipBehavior: Clip.hardEdge, - children: [ - if (snapshot.data!.tlSeason1.apm != null) - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.apm!, - isScreenBig: bigScreen, - fractionDigits: 2, - playerStatLabel: "Attack\nPer Minute"), - if (snapshot.data!.tlSeason1.pps != null) - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.pps!, - isScreenBig: bigScreen, - fractionDigits: 2, - playerStatLabel: "Pieces\nPer Second"), - if (snapshot.data!.tlSeason1.apm != null) - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.vs!, - isScreenBig: bigScreen, - fractionDigits: 2, - playerStatLabel: "Versus\nScore"), - if (snapshot.data!.tlSeason1.standing > 0) - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.standing, isScreenBig: bigScreen, playerStatLabel: "Leaderboard\nplacement"), - if (snapshot.data!.tlSeason1.standingLocal > 0) - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.standingLocal, - isScreenBig: bigScreen, - playerStatLabel: "Country LB\nplacement"), - _StatCellNum( - 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.winrate * 100, - isScreenBig: bigScreen, - fractionDigits: 2, - playerStatLabel: "Winrate\nprecentage"), - ], - ), - ), - if (snapshot.data!.tlSeason1.nerdStats != null) - Column( - children: [ - Text("Nerd Stats", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), - Padding( - padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), - child: Wrap( - direction: Axis.horizontal, - alignment: WrapAlignment.center, - spacing: 25, - crossAxisAlignment: WrapCrossAlignment.start, - clipBehavior: Clip.hardEdge, - children: [ - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.nerdStats!.app, - isScreenBig: bigScreen, - fractionDigits: 3, - playerStatLabel: "Attack\nPer Piece"), - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.nerdStats!.vsapm, - isScreenBig: bigScreen, - fractionDigits: 3, - playerStatLabel: "VS/APM"), - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.nerdStats!.dss, - isScreenBig: bigScreen, - fractionDigits: 3, - playerStatLabel: "Downstack\nPer Second"), - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.nerdStats!.dsp, - isScreenBig: bigScreen, - fractionDigits: 3, - playerStatLabel: "Downstack\nPer Piece"), - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.nerdStats!.appdsp, - isScreenBig: bigScreen, - fractionDigits: 3, - playerStatLabel: "APP + DS/P"), - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.nerdStats!.cheese, - isScreenBig: bigScreen, - fractionDigits: 2, - playerStatLabel: "Cheese\nIndex"), - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.nerdStats!.gbe, - isScreenBig: bigScreen, - fractionDigits: 3, - playerStatLabel: "Garbage\nEfficiency"), - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.nerdStats!.nyaapp, - isScreenBig: bigScreen, - fractionDigits: 3, - playerStatLabel: "Weighted\nAPP"), - _StatCellNum( - playerStat: snapshot.data!.tlSeason1.nerdStats!.area, - isScreenBig: bigScreen, - fractionDigits: 1, - playerStatLabel: "Area") - ]), - ) - ], - ), - 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), - ), - ], - ), - 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), - ), - ], - ), - ], - ), - ), - ), - 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: const BorderSide(color: Colors.transparent, width: 1), - gridBorderData: const BorderSide(color: Colors.white24, width: 1), - tickBorderData: const 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: [ - const RadarEntry(value: 0), - const RadarEntry(value: 0), - const RadarEntry(value: 0), - const RadarEntry(value: 0), - const RadarEntry(value: 0), - const RadarEntry(value: 0), - const RadarEntry(value: 0), - const RadarEntry(value: 0), - const RadarEntry(value: 0), - const 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: const BorderSide(color: Colors.transparent, width: 1), - gridBorderData: const BorderSide(color: Colors.white24, width: 1), - tickBorderData: const 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: [ - const RadarEntry(value: 0), - const RadarEntry(value: 0), - const RadarEntry(value: 0), - const RadarEntry(value: 0), - ], - ), - RadarDataSet( - fillColor: Colors.transparent, - borderColor: Colors.transparent, - dataEntries: [ - const RadarEntry(value: 1), - const RadarEntry(value: 1), - const RadarEntry(value: 1), - const 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)), - ], - ); - }, - ), - 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), - ), - ], - ), - ), - ], - ); - }) + _TLThingy(tl: snapshot.data!.tlSeason1, userID: snapshot.data!.userId), + _RecordThingy(record: (snapshot.data!.sprint.isNotEmpty) ? snapshot.data!.sprint[0] : null), + _RecordThingy(record: (snapshot.data!.blitz.isNotEmpty) ? snapshot.data!.blitz[0] : null), + _OtherThingy(zen: snapshot.data!.zen, bio: snapshot.data!.bio) ], ), ); @@ -1394,3 +522,582 @@ class _UserThingy extends StatelessWidget { }); } } + +class _TLThingy extends StatelessWidget { + final TetraLeagueAlpha tl; + final String userID; + const _TLThingy({Key? key, required this.tl, required this.userID}) : super(key: key); + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints) { + bool bigScreen = constraints.maxWidth > 768; + return ListView.builder( + physics: const ClampingScrollPhysics(), + itemCount: 1, + itemBuilder: (BuildContext context, int index) { + return Column( + children: (tl.gamesPlayed > 0) + ? [ + Text("Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + if (tl.gamesPlayed >= 10) + Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.spaceAround, + crossAxisAlignment: WrapCrossAlignment.center, + clipBehavior: Clip.hardEdge, + children: [ + userID == "5e32fc85ab319c2ab1beb07c" // he love her so much, you can't even imagine + ? Image.asset("res/icons/kagari.png", height: 128) // Btw why she wearing Kazamatsuri high school uniform? + : Image.asset("res/tetrio_tl_alpha_ranks/${tl.rank}.png", height: 128), + Column( + children: [ + Text("${tl.rating.toStringAsFixed(2)} TR", + style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + Text( + "Top ${(tl.percentile * 100).toStringAsFixed(2)}% (${tl.percentileRank.toUpperCase()}) • Top Rank: ${tl.bestRank.toUpperCase()} • Glicko: ${tl.glicko?.toStringAsFixed(2)}±${tl.rd?.toStringAsFixed(2)}${tl.decaying ? ' • Decaying' : ''}", + textAlign: TextAlign.center, + ), + ], + ), + ], + ) + else + Text("${10 - tl.gamesPlayed} games until being ranked", + softWrap: true, + style: TextStyle( + fontFamily: "Eurostile Round Extended", + fontSize: bigScreen ? 42 : 28, + overflow: TextOverflow.visible, + )), + Padding( + padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), + child: Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.center, + spacing: 25, + crossAxisAlignment: WrapCrossAlignment.start, + clipBehavior: Clip.hardEdge, + children: [ + if (tl.apm != null) + _StatCellNum(playerStat: tl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Attack\nPer Minute"), + if (tl.pps != null) + _StatCellNum(playerStat: tl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Pieces\nPer Second"), + if (tl.apm != null) _StatCellNum(playerStat: tl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Versus\nScore"), + if (tl.standing > 0) _StatCellNum(playerStat: tl.standing, isScreenBig: bigScreen, playerStatLabel: "Leaderboard\nplacement"), + if (tl.standingLocal > 0) + _StatCellNum(playerStat: tl.standingLocal, isScreenBig: bigScreen, playerStatLabel: "Country LB\nplacement"), + _StatCellNum(playerStat: tl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Games\nplayed"), + _StatCellNum(playerStat: tl.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nwon"), + _StatCellNum(playerStat: tl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Winrate\nprecentage"), + ], + ), + ), + if (tl.nerdStats != null) + Column( + children: [ + Text("Nerd Stats", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + Padding( + padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), + child: Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.center, + spacing: 25, + crossAxisAlignment: WrapCrossAlignment.start, + clipBehavior: Clip.hardEdge, + children: [ + _StatCellNum(playerStat: tl.nerdStats!.app, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Attack\nPer Piece"), + _StatCellNum(playerStat: tl.nerdStats!.vsapm, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "VS/APM"), + _StatCellNum( + playerStat: tl.nerdStats!.dss, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Downstack\nPer Second"), + _StatCellNum( + playerStat: tl.nerdStats!.dsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Downstack\nPer Piece"), + _StatCellNum(playerStat: tl.nerdStats!.appdsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "APP + DS/P"), + _StatCellNum(playerStat: tl.nerdStats!.cheese, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Cheese\nIndex"), + _StatCellNum( + playerStat: tl.nerdStats!.gbe, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Garbage\nEfficiency"), + _StatCellNum(playerStat: tl.nerdStats!.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Weighted\nAPP"), + _StatCellNum(playerStat: tl.nerdStats!.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: "Area") + ]), + ) + ], + ), + if (tl.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( + tl.estTr!.esttr.toStringAsFixed(2), + style: const TextStyle(fontSize: 24), + ), + ], + ), + if (tl.rating >= 0) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + "Accuracy:", + style: TextStyle(fontSize: 24), + ), + Text( + tl.esttracc!.toStringAsFixed(2), + style: const TextStyle(fontSize: 24), + ), + ], + ), + ], + ), + ), + ), + if (tl.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: const BorderSide(color: Colors.transparent, width: 1), + gridBorderData: const BorderSide(color: Colors.white24, width: 1), + tickBorderData: const 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: tl.apm! * 1), + RadarEntry(value: tl.pps! * 45), + RadarEntry(value: tl.vs! * 0.444), + RadarEntry(value: tl.nerdStats!.app * 185), + RadarEntry(value: tl.nerdStats!.dss * 175), + RadarEntry(value: tl.nerdStats!.dsp * 450), + RadarEntry(value: tl.nerdStats!.appdsp * 140), + RadarEntry(value: tl.nerdStats!.vsapm * 60), + RadarEntry(value: tl.nerdStats!.cheese * 1.25), + RadarEntry(value: tl.nerdStats!.gbe * 315), + ], + ), + RadarDataSet( + fillColor: Colors.transparent, + borderColor: Colors.transparent, + dataEntries: [ + const RadarEntry(value: 0), + const RadarEntry(value: 0), + const RadarEntry(value: 0), + const RadarEntry(value: 0), + const RadarEntry(value: 0), + const RadarEntry(value: 0), + const RadarEntry(value: 0), + const RadarEntry(value: 0), + const RadarEntry(value: 0), + const 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: const BorderSide(color: Colors.transparent, width: 1), + gridBorderData: const BorderSide(color: Colors.white24, width: 1), + tickBorderData: const 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: tl.playstyle!.opener), + RadarEntry(value: tl.playstyle!.stride), + RadarEntry(value: tl.playstyle!.infds), + RadarEntry(value: tl.playstyle!.plonk), + ], + ), + RadarDataSet( + fillColor: Colors.transparent, + borderColor: Colors.transparent, + dataEntries: [ + const RadarEntry(value: 0), + const RadarEntry(value: 0), + const RadarEntry(value: 0), + const RadarEntry(value: 0), + ], + ), + RadarDataSet( + fillColor: Colors.transparent, + borderColor: Colors.transparent, + dataEntries: [ + const RadarEntry(value: 1), + const RadarEntry(value: 1), + const RadarEntry(value: 1), + const 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)), + ], + ); + }, + ); + }); + } +} + +class _RecordThingy extends StatelessWidget { + final RecordSingle? record; + const _RecordThingy({Key? key, required this.record}) : super(key: key); + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints) { + bool bigScreen = constraints.maxWidth > 768; + return ListView.builder( + physics: const ClampingScrollPhysics(), + itemCount: 1, + itemBuilder: (BuildContext context, int index) { + return Column( + children: (record != null) + ? [ + if (record!.stream.contains("40l")) + Text("40 Lines", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)) + else if (record!.stream.contains("blitz")) + Text("Blitz", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + if (record!.stream.contains("40l")) + Text(record!.endContext!.finalTime.toString(), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)) + else if (record!.stream.contains("blitz")) + Text(record!.endContext!.score.toString(), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + if (record!.rank != null) _StatCellNum(playerStat: record!.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: [ + if (record!.stream.contains("blitz")) + _StatCellNum(playerStat: record!.endContext!.level, playerStatLabel: "Level", isScreenBig: bigScreen), + if (record!.stream.contains("blitz")) + _StatCellNum(playerStat: record!.endContext!.spp, playerStatLabel: "Score\nPer Piece", fractionDigits: 2, isScreenBig: bigScreen), + _StatCellNum(playerStat: record!.endContext!.piecesPlaced, playerStatLabel: "Pieces\nPlaced", isScreenBig: bigScreen), + _StatCellNum(playerStat: record!.endContext!.pps, playerStatLabel: "Pieces\nPer Second", fractionDigits: 2, isScreenBig: bigScreen), + _StatCellNum(playerStat: record!.endContext!.finesse.faults, playerStatLabel: "Finesse\nFaults", isScreenBig: bigScreen), + _StatCellNum( + playerStat: record!.endContext!.finessePercentage * 100, + playerStatLabel: "Finesse\nPercentage", + fractionDigits: 2, + isScreenBig: bigScreen), + _StatCellNum(playerStat: record!.endContext!.inputs, playerStatLabel: "Key\nPresses", isScreenBig: bigScreen), + _StatCellNum(playerStat: record!.endContext!.kpp, playerStatLabel: "KP Per\nPiece", fractionDigits: 2, isScreenBig: bigScreen), + _StatCellNum(playerStat: record!.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( + record!.endContext!.clears.allClears.toString(), + style: const TextStyle(fontSize: 24), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("Holds:", style: TextStyle(fontSize: 24)), + Text( + record!.endContext!.holds.toString(), + style: const TextStyle(fontSize: 24), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("T-spins total:", style: TextStyle(fontSize: 24)), + Text( + record!.endContext!.tSpins.toString(), + style: const TextStyle(fontSize: 24), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin zero:", style: TextStyle(fontSize: 18)), + Text( + record!.endContext!.clears.tSpinZeros.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin singles:", style: TextStyle(fontSize: 18)), + Text( + record!.endContext!.clears.tSpinSingles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin doubles:", style: TextStyle(fontSize: 18)), + Text( + record!.endContext!.clears.tSpinDoubles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - T-spin triples:", style: TextStyle(fontSize: 18)), + Text( + record!.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( + record!.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( + record!.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( + record!.endContext!.clears.tSpinMiniDoubles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("Line clears:", style: TextStyle(fontSize: 24)), + Text( + record!.endContext!.lines.toString(), + style: const TextStyle(fontSize: 24), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - Singles:", style: TextStyle(fontSize: 18)), + Text( + record!.endContext!.clears.singles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - Doubles:", style: TextStyle(fontSize: 18)), + Text( + record!.endContext!.clears.doubles.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - Triples:", style: TextStyle(fontSize: 18)), + Text( + record!.endContext!.clears.triples.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text(" - Quads:", style: TextStyle(fontSize: 18)), + Text( + record!.endContext!.clears.quads.toString(), + style: const TextStyle(fontSize: 18), + ), + ], + ), + ], + ), + ), + ), + ] + : [Text("No record", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28))], + ); + }); + }); + } +} + +class _OtherThingy extends StatelessWidget { + final TetrioZen? zen; + final String? bio; + const _OtherThingy({Key? key, required this.zen, required this.bio}) : super(key: key); + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints) { + bool bigScreen = constraints.maxWidth > 768; + return 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 (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 ${zen!.level}", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + Text( + "Score ${zen!.score}", + style: const TextStyle(fontSize: 18), + ), + ], + ), + ), + if (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(bio!, style: const TextStyle(fontSize: 18)), + ], + ), + ), + ], + ); + }, + ); + }); + } +} diff --git a/lib/views/settings_view.dart b/lib/views/settings_view.dart index e49845f..9d953ac 100644 --- a/lib/views/settings_view.dart +++ b/lib/views/settings_view.dart @@ -65,7 +65,7 @@ class SettingsState extends State { builder: (BuildContext context) => AlertDialog( title: const Text("Your TETR.IO account nickname or ID", style: TextStyle(fontFamily: "Eurostile Round Extended")), content: SingleChildScrollView( - child: ListBody(children: [TextField(controller: _playertext)]), + child: ListBody(children: [TextField(controller: _playertext, maxLength: 25)]), ), actions: [ TextButton(