diff --git a/lib/data_objects/tetrio.dart b/lib/data_objects/tetrio.dart index 78022fe..51d2d1b 100644 --- a/lib/data_objects/tetrio.dart +++ b/lib/data_objects/tetrio.dart @@ -457,8 +457,10 @@ class TetrioPlayer { class Summaries{ late String id; - late RecordSingle sprint; - late RecordSingle blitz; + RecordSingle? sprint; + RecordSingle? blitz; + RecordSingle? zenith; + RecordSingle? zenithEx; late TetraLeagueAlpha league; late TetrioZen zen; @@ -466,8 +468,10 @@ class Summaries{ Summaries.fromJson(Map json, String i){ id = i; - sprint = RecordSingle.fromJson(json['40l']['record'], json['40l']['rank']); - blitz = RecordSingle.fromJson(json['blitz']['record'], json['blitz']['rank']); + if (json['40l']['record'] != null) sprint = RecordSingle.fromJson(json['40l']['record'], json['40l']['rank'], json['40l']['rank_local']); + if (json['blitz']['record'] != null) blitz = RecordSingle.fromJson(json['blitz']['record'], json['blitz']['rank'], json['40l']['rank_local']); + if (json['zenith']['record'] != null) zenith = RecordSingle.fromJson(json['zenith']['record'], json['zenith']['rank'], json['zenith']['rank_local']); + if (json['zenithex']['record'] != null) zenithEx = RecordSingle.fromJson(json['zenithex']['record'], json['zenithex']['rank'], json['zenithex']['rank_local']); league = TetraLeagueAlpha.fromJson(json['league'], DateTime.now()); zen = TetrioZen.fromJson(json['zen']); } @@ -676,11 +680,13 @@ class ResultsStats { late int piecesPlaced; late int lines; late int score; - late int seed; + int? seed; late Duration finalTime; late int tSpins; late Clears clears; - late Finesse? finesse; + late int kills; + Finesse? finesse; + ZenithResults? zenith; double get pps => piecesPlaced / (finalTime.inMicroseconds / 1000000); double get kpp => inputs / piecesPlaced; @@ -717,7 +723,9 @@ class ResultsStats { tSpins = json['tspins']; piecesPlaced = json['piecesplaced']; clears = Clears.fromJson(json['clears']); - finesse = json.containsKey("finesse") ? Finesse.fromJson(json['finesse']) : null; + kills = json['kills']; + if (json.containsKey("finesse")) finesse = Finesse.fromJson(json['finesse']); + if (json.containsKey("zenith")) zenith = ZenithResults.fromJson(json['zenith']); } Map toJson() { @@ -739,6 +747,39 @@ class ResultsStats { } } +class ZenithResults{ + late double altitude; + late double rank; + late double peakrank; + late double avgrankpts; + late int floor; + late double targetingfactor; + late double targetinggrace; + late double totalbonus; + late int revives; + late int revivesTotal; + late bool speedrun; + late bool speedrunSeen; + late List splits; + + ZenithResults.fromJson(Map json){ + altitude = json['altitude'].toDouble(); + rank = json['rank'].toDouble(); + peakrank = json['peakrank'].toDouble(); + avgrankpts = json['avgrankpts'].toDouble(); + floor = json['floor']; + targetingfactor = json['targetingfactor'].toDouble(); + targetinggrace = json['targetinggrace'].toDouble(); + totalbonus = json['totalbonus'].toDouble(); + revives = json['revives']; + revivesTotal = json['revivesTotal']; + speedrun = json['speedrun']; + speedrunSeen = json['speedrun_seen']; + splits = []; + for (int ms in json['splits']) splits.add(Duration(milliseconds: ms)); + } +} + class Handling { late num arr; late num das; @@ -997,7 +1038,7 @@ class SingleplayerStream{ userId = userID; type = tp; records = []; - for (var value in json) {records.add(RecordSingle.fromJson(value, null));} + for (var value in json) {records.add(RecordSingle.fromJson(value, -1, -1));} } } @@ -1079,9 +1120,9 @@ class BetaLeagueStats{ } BetaLeagueStats.fromJson(Map json){ - apm = json['apm'].toDouble(); - pps = json['pps'].toDouble(); - vs = json['vsscore'].toDouble(); + apm = json['apm'] != null ? json['apm'].toDouble() : 0.00; + pps = json['apm'] != null ? json['pps'].toDouble() : 0.00; + vs = json['apm'] != null ? json['vsscore'].toDouble() : 0.00; garbageSent = json['garbagesent']; garbageReceived = json['garbagereceived']; kills = json['kills']; @@ -1364,11 +1405,13 @@ class RecordSingle { late String gamemode; late DateTime timestamp; late ResultsStats stats; - int? rank; + late int rank; + late int countryRank; + late AggregateStats aggregateStats; - RecordSingle({required this.userId, required this.replayId, required this.ownId, required this.timestamp, required this.stats, this.rank}); + RecordSingle({required this.userId, required this.replayId, required this.ownId, required this.timestamp, required this.stats, required this.rank, required this.countryRank, required this.aggregateStats}); - RecordSingle.fromJson(Map json, int? ran) { + RecordSingle.fromJson(Map json, int ran, int cran) { //developer.log("RecordSingle.fromJson: $json", name: "data_objects/tetrio"); ownId = json['_id']; gamemode = json['gamemode']; @@ -1377,6 +1420,8 @@ class RecordSingle { timestamp = DateTime.parse(json['ts']); userId = json['user']['id']; rank = ran; + countryRank = cran; + aggregateStats = AggregateStats.fromJson(json['results']['aggregatestats']); } Map toJson() { @@ -1391,12 +1436,38 @@ class RecordSingle { } } +class AggregateStats{ + late double apm; + late double pps; + late double vs; + late NerdStats nerdStats; + late EstTr estTr; + late Playstyle playstyle; + + AggregateStats(this.apm, this.pps, this.vs){ + nerdStats = NerdStats(apm, pps, vs); + estTr = EstTr(apm, pps, vs, nerdStats.app, nerdStats.dss, nerdStats.dsp, nerdStats.gbe); + playstyle = Playstyle(apm, pps, nerdStats.app, nerdStats.vsapm, nerdStats.dsp, nerdStats.gbe, estTr.srarea, estTr.statrank); + } + + AggregateStats.fromJson(Map json){ + apm = json['apm'] != null ? json['apm'].toDouble() : 0.00; + pps = json['apm'] != null ? json['pps'].toDouble() : 0.00; + vs = json['apm'] != null ? json['vsscore'].toDouble() : 0.00; + nerdStats = NerdStats(apm, pps, vs); + estTr = EstTr(apm, pps, vs, nerdStats.app, nerdStats.dss, nerdStats.dsp, nerdStats.gbe); + playstyle = Playstyle(apm, pps, nerdStats.app, nerdStats.vsapm, nerdStats.dsp, nerdStats.gbe, estTr.srarea, estTr.statrank); + } +} + class TetrioZen { late int level; late int score; TetrioZen({required this.level, required this.score}); + double get scoreRequirement => (10000 + 10000 * ((log(level + 1) / log(2)) - 1)); + TetrioZen.fromJson(Map json) { level = json['level']; score = json['score']; diff --git a/lib/services/tetrio_crud.dart b/lib/services/tetrio_crud.dart index 04f5a31..382201b 100644 --- a/lib/services/tetrio_crud.dart +++ b/lib/services/tetrio_crud.dart @@ -904,10 +904,10 @@ class TetrioService extends DB { if (jsonDecode(response.body)['success']) { Map jsonRecords = jsonDecode(response.body); var sprint = jsonRecords['data']['records']['40l']['record'] != null - ? RecordSingle.fromJson(jsonRecords['data']['records']['40l']['record'], jsonRecords['data']['records']['40l']['rank']) + ? RecordSingle.fromJson(jsonRecords['data']['records']['40l']['record'], jsonRecords['data']['records']['40l']['rank'], jsonRecords['data']['records']['40l']['rank_local']) : null; var blitz = jsonRecords['data']['records']['blitz']['record'] != null - ? RecordSingle.fromJson(jsonRecords['data']['records']['blitz']['record'], jsonRecords['data']['records']['blitz']['rank']) + ? RecordSingle.fromJson(jsonRecords['data']['records']['blitz']['record'], jsonRecords['data']['records']['blitz']['rank'], jsonRecords['data']['records']['blitz']['rank_local']) : null; var zen = TetrioZen.fromJson(jsonRecords['data']['zen']); UserRecords result = UserRecords(userID, sprint, blitz, zen); diff --git a/lib/utils/relative_timestamps.dart b/lib/utils/relative_timestamps.dart index f73a267..7425441 100644 --- a/lib/utils/relative_timestamps.dart +++ b/lib/utils/relative_timestamps.dart @@ -3,6 +3,7 @@ import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/utils/numers_formats.dart'; final NumberFormat secs = NumberFormat("00.###", LocaleSettings.currentLocale.languageCode); +final NumberFormat fixedSecs = NumberFormat("00.000", LocaleSettings.currentLocale.languageCode); final NumberFormat nonsecs = NumberFormat("00", LocaleSettings.currentLocale.languageCode); final NumberFormat nonsecs3 = NumberFormat("000", LocaleSettings.currentLocale.languageCode); final NumberFormat _timeInSec = NumberFormat("#,###.###s.", LocaleSettings.currentLocale.languageCode); @@ -70,6 +71,10 @@ String get40lTime(int microseconds){ return microseconds > 60000000 ? "${(microseconds/1000000/60).floor()}:${(secs.format(microseconds /1000000 % 60))}" : _timeInSec.format(microseconds / 1000000); } +String getMoreNormalTime(Duration time){ + return "${nonsecs.format(time.inMinutes)}:${(fixedSecs.format(time.inMilliseconds/1000%60))}"; +} + /// Readable [a] - [b], without sign String readableTimeDifference(Duration a, Duration b){ Duration result = a - b; diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 861b37e..8f3fb1b 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -11,6 +11,7 @@ import 'package:intl/intl.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter/services.dart'; import 'package:syncfusion_flutter_charts/charts.dart'; +import 'package:syncfusion_flutter_gauges/gauges.dart'; import 'package:tetra_stats/data_objects/tetra_stats.dart'; import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/gen/strings.g.dart'; @@ -23,6 +24,8 @@ import 'package:tetra_stats/utils/text_shadow.dart'; import 'package:tetra_stats/views/singleplayer_record_view.dart'; import 'package:tetra_stats/views/tl_match_view.dart' show TlMatchResultView; import 'package:tetra_stats/widgets/finesse_thingy.dart'; +import 'package:tetra_stats/widgets/gauget_num.dart'; +import 'package:tetra_stats/widgets/graphs.dart'; import 'package:tetra_stats/widgets/lineclears_thingy.dart'; import 'package:tetra_stats/widgets/list_tile_trailing_stats.dart'; import 'package:tetra_stats/widgets/recent_sp_games.dart'; @@ -430,12 +433,14 @@ class _MainState extends State with TickerProviderStateMixin { tabs: bigScreen ? [ Tab(text: t.tetraLeague,), Tab(text: t.history), + Tab(text: "Quick Play"), Tab(text: "${t.sprint} & ${t.blitz}"), Tab(text: t.other), ] : [ Tab(text: t.tetraLeague), Tab(text: t.tlRecords), Tab(text: t.history), + Tab(text: "Quick Play"), Tab(text: t.sprint), Tab(text: t.blitz), Tab(text: t.recentRuns), @@ -478,6 +483,7 @@ class _MainState extends State with TickerProviderStateMixin { ), ],), _History(chartsData: chartsData, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![1].league.gamesPlayed > 0), + _ZenithThingy(record: snapshot.data![1].zenith, recordEX: snapshot.data![1].zenithEx), _TwoRecordsThingy(sprint: snapshot.data![1].sprint, blitz: snapshot.data![1].blitz, rank: snapshot.data![1].league.percentileRank, recent: SingleplayerStream(userId: "userId", records: [], type: "recent"), sprintStream: SingleplayerStream(userId: "userId", records: [], type: "40l"), blitzStream: SingleplayerStream(userId: "userId", records: [], type: "blitz")), _OtherThingy(zen: snapshot.data![1].zen, bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![2]) ] : [ @@ -500,6 +506,7 @@ class _MainState extends State with TickerProviderStateMixin { ), _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3].records, wasActiveInTL: true, oldMathcesHere: _TLHistoryWasFetched, separateScrollController: true), _History(chartsData: chartsData, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![1].league.gamesPlayed > 0), + _ZenithThingy(record: snapshot.data![1].zenith, recordEX: snapshot.data![1].zenithEx), SingleplayerRecord(record: snapshot.data![1].sprint, rank: snapshot.data![1].league.percentileRank, stream: SingleplayerStream(userId: "userId", records: [], type: "40l")), SingleplayerRecord(record: snapshot.data![1].blitz, rank: snapshot.data![1].league.percentileRank, stream: SingleplayerStream(userId: "userId", records: [], type: "Blitz")), _RecentSingleplayersThingy(SingleplayerStream(userId: "userId", records: [], type: "recent")), @@ -1218,7 +1225,230 @@ class _RecentSingleplayersThingy extends StatelessWidget { child: RecentSingleplayerGames(recent: recent, hideTitle: true) ); } +} +class _ZenithThingy extends StatefulWidget{ + final RecordSingle? record; + final RecordSingle? recordEX; + + _ZenithThingy({this.record, this.recordEX}); + + @override + State<_ZenithThingy> createState() => _ZenithThingyState(); +} + +class _ZenithThingyState extends State<_ZenithThingy> { + late RecordSingle? record; + bool ex = false; + + @override + void initState(){ + super.initState(); + record = ex ? widget.recordEX : widget.record; + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints){ + bool bigScreen = constraints.maxWidth > 768; + if (record == null) { + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Column( + children: [ + Text("Quick Play${ex ? " Expert" : ""}", style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)), + RichText(text: TextSpan( + text: "--- m", + style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 36 : 32, fontWeight: FontWeight.w500, color: Colors.grey), + ), + ), + TextButton(onPressed: (){ + if (ex){ + ex = false; + }else{ + ex = true; + } + setState(() { + record = ex ? widget.recordEX : widget.record; + }); + }, child: Text(ex ? "Switch to normal" : "Switch to Expert")), + ], + ), + ); + } + return SingleChildScrollView( + child: Padding(padding: const EdgeInsets.only(top: 8.0), + child: Column( + children: [ + Text("Quick Play${ex ? " Expert" : ""}", style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)), + RichText(text: TextSpan( + text: "${f2.format(record!.stats.zenith!.altitude)} m", + style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 36 : 32, fontWeight: FontWeight.w500, color: Colors.white), + ), + ), + RichText( + text: TextSpan( + text: "", + style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey), + children: [ + if (record!.rank != -1) TextSpan(text: "№${record!.rank}"), + if (record!.rank != -1) const TextSpan(text: " • "), + if (record!.countryRank != -1) TextSpan(text: "№${record!.countryRank} local"), + if (record!.countryRank != -1) const TextSpan(text: " • "), + TextSpan(text: timestamp(widget.record!.timestamp)), + ] + ), + ), + TextButton(onPressed: (){ + if (ex){ + ex = false; + }else{ + ex = true; + } + setState(() { + record = ex ? widget.recordEX : widget.record; + }); + }, child: Text(ex ? "Switch to normal" : "Switch to Expert")), + Wrap( + alignment: WrapAlignment.spaceBetween, + crossAxisAlignment: WrapCrossAlignment.start, + spacing: 20, + children: [ + StatCellNum(playerStat: record!.aggregateStats.apm, playerStatLabel: t.statCellNum.apm, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: true), + StatCellNum(playerStat: record!.aggregateStats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false), + StatCellNum(playerStat: record!.aggregateStats.vs, playerStatLabel: t.statCellNum.vs, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: true), + StatCellNum(playerStat: record!.stats.kills, playerStatLabel: "Kills", isScreenBig: bigScreen, higherIsBetter: true) + ], + ), + FinesseThingy(record?.stats.finesse, record?.stats.finessePercentage), + LineclearsThingy(record!.stats.clears, record!.stats.lines, record!.stats.holds, record!.stats.tSpins), + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: SizedBox( + width: 300, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text("Total time: ${getMoreNormalTime(record!.stats.finalTime)}", style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center), + Table( + children: [ + TableRow( + children: [ + Text("Floor"), + Text("Split"), + Text("Total"), + ] + ), + for (int i = 0; i < record!.stats.zenith!.splits.length; i++) TableRow( + children: [ + Text((i+1).toString()), + Text(record!.stats.zenith!.splits[i] != Duration.zero ? getMoreNormalTime(record!.stats.zenith!.splits[i]-(i-1 != -1 ? record!.stats.zenith!.splits[i-1] : Duration.zero)) : "--:--.---"), + Text(record!.stats.zenith!.splits[i] != Duration.zero ? getMoreNormalTime(record!.stats.zenith!.splits[i]) : "--:--.---"), + ] + ) + ], + ), + ], + ), + ), + ), + Column( + children: [ + Text(t.nerdStats, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + Padding( + padding: const EdgeInsets.fromLTRB(0, 40, 0, 0), + child: Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.center, + spacing: 35, + crossAxisAlignment: WrapCrossAlignment.start, + clipBehavior: Clip.hardEdge, + children: [ + GaugetNum(playerStat: record!.aggregateStats.nerdStats.app, playerStatLabel: t.statCellNum.app, higherIsBetter: true, minimum: 0, maximum: 1, ranges: [ + GaugeRange(startValue: 0, endValue: 0.2, color: Colors.red), + GaugeRange(startValue: 0.2, endValue: 0.4, color: Colors.yellow), + GaugeRange(startValue: 0.4, endValue: 0.6, color: Colors.green), + GaugeRange(startValue: 0.6, endValue: 0.8, color: Colors.blue), + GaugeRange(startValue: 0.8, endValue: 1, color: Colors.purple), + ], alertWidgets: [ + Text(t.statCellNum.appDescription), + Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.app}") + ]), + GaugetNum(playerStat: record!.aggregateStats.nerdStats.vsapm, playerStatLabel: "VS / APM", higherIsBetter: true, minimum: 1.8, maximum: 2.4, ranges: [ + GaugeRange(startValue: 1.8, endValue: 2.0, color: Colors.green), + GaugeRange(startValue: 2.0, endValue: 2.2, color: Colors.blue), + GaugeRange(startValue: 2.2, endValue: 2.4, color: Colors.purple), + ], alertWidgets: [ + Text(t.statCellNum.vsapmDescription), + Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.vsapm}") + ]) + ]), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), + child: Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.center, + spacing: 25, + crossAxisAlignment: WrapCrossAlignment.start, + clipBehavior: Clip.hardEdge, + children: [ + StatCellNum(playerStat: record!.aggregateStats.nerdStats.dss, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dss, + alertWidgets: [Text(t.statCellNum.dssDescription), + Text("${t.formula}: (VS / 100) - (APM / 60)"), + Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.dss}"),], + okText: t.popupActions.ok, + higherIsBetter: true,), + StatCellNum(playerStat: record!.aggregateStats.nerdStats.dsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dsp, + alertWidgets: [Text(t.statCellNum.dspDescription), + Text("${t.formula}: DS/S / PPS"), + Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.dsp}"),], + okText: t.popupActions.ok, + higherIsBetter: true), + StatCellNum(playerStat: record!.aggregateStats.nerdStats.appdsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.appdsp, + alertWidgets: [Text(t.statCellNum.appdspDescription), + Text("${t.formula}: APP + DS/P"), + Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.appdsp}"),], + okText: t.popupActions.ok, + higherIsBetter: true), + StatCellNum(playerStat: record!.aggregateStats.nerdStats.cheese, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.cheese, + alertWidgets: [Text(t.statCellNum.cheeseDescription), + Text("${t.formula}: (DS/P * 150) + ((VS/APM - 2) * 50) + (0.6 - APP) * 125"), + Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.cheese}"),], + okText: t.popupActions.ok, + higherIsBetter: false), + StatCellNum(playerStat: record!.aggregateStats.nerdStats.gbe, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.gbe, + alertWidgets: [Text(t.statCellNum.gbeDescription), + Text("${t.formula}: APP * DS/P * 2"), + Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.gbe}"),], + okText: t.popupActions.ok, + higherIsBetter: true), + StatCellNum(playerStat: record!.aggregateStats.nerdStats.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.nyaapp, + alertWidgets: [Text(t.statCellNum.nyaappDescription), + Text("${t.formula}: APP - 5 * tan(radians((Cheese Index / -30) + 1))"), + Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.nyaapp}")], + okText: t.popupActions.ok, + higherIsBetter: true), + StatCellNum(playerStat: record!.aggregateStats.nerdStats.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: t.statCellNum.area, + alertWidgets: [Text(t.statCellNum.areaDescription), + Text("${t.formula}: APM * 1 + PPS * 45 + VS * 0.444 + APP * 185 + DS/S * 175 + DS/P * 450 + Garbage Effi * 315"), + Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.area}"),], + okText: t.popupActions.ok, + higherIsBetter: true) + ]), + ) + ], + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: Graphs(record!.aggregateStats.apm, record!.aggregateStats.pps, record!.aggregateStats.vs, record!.aggregateStats.nerdStats, record!.aggregateStats.playstyle), + ) + ], + ) + ), + ); + }); + } } class _OtherThingy extends StatelessWidget { @@ -1314,6 +1544,7 @@ class _OtherThingy extends StatelessWidget { "40l" => get40lTime((news.data["result"]*1000).floor()), "5mblast" => get40lTime((news.data["result"]*1000).floor()), "zenith" => "${f2.format(news.data["result"])} m.", + "zenithex" => "${f2.format(news.data["result"])} m.", _ => "unknown" }, style: const TextStyle(fontWeight: FontWeight.bold) @@ -1463,6 +1694,14 @@ class _OtherThingy extends StatelessWidget { Text(t.zen, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), Text("${t.statCellNum.level} ${NumberFormat.decimalPattern().format(zen!.level)}", style: const TextStyle(fontSize: 28, fontWeight: FontWeight.bold)), Text("${t.statCellNum.score} ${NumberFormat.decimalPattern().format(zen!.score)}", style: const TextStyle(fontSize: 18)), + Container( + constraints: BoxConstraints(maxWidth: 300.0), + child: Row(children: [ + Text("Score requirement to level up:"), + Spacer(), + Text(intf.format(zen!.scoreRequirement)) + ],), + ) ], ), ), diff --git a/lib/views/main_view_tiles.dart b/lib/views/main_view_tiles.dart index 4ea415b..65e90d9 100644 --- a/lib/views/main_view_tiles.dart +++ b/lib/views/main_view_tiles.dart @@ -68,8 +68,21 @@ News testNews = News("6098518e3d5155e6ec429cdc", [ NewsEntry(type: "personalbest", data: {"gametype": "blitz", "result": 23.232}, timestamp: DateTime(2002, 2, 25, 10, 30, 02)), NewsEntry(type: "personalbest", data: {"gametype": "5mblast", "result": 23.232}, timestamp: DateTime(2002, 2, 25, 10, 30, 03)), ]); +late ScrollController controller; class _MainState extends State with TickerProviderStateMixin { + @override + void initState() { + controller = ScrollController(); + super.initState(); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold(body: Row( @@ -94,138 +107,185 @@ class _MainState extends State with TickerProviderStateMixin { ], selectedIndex: 0 ), - SizedBox( - width: 450.0, - child: Column( - children: [ - NewUserThingy(player: testPlayer, showStateTimestamp: false, setState: setState), - Padding( - padding: const EdgeInsets.fromLTRB(4.0, 0.0, 4.0, 0.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded(child: ElevatedButton.icon(onPressed: (){print("ok, and?");}, icon: Icon(Icons.person_add), label: Text(t.track), style: ButtonStyle(shape: MaterialStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(12.0), right: Radius.zero)))))), - Expanded(child: ElevatedButton.icon(onPressed: (){print("ok, and?");}, icon: Icon(Icons.balance), label: Text(t.compare), style: ButtonStyle(shape: MaterialStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.zero, right: Radius.circular(12.0))))))) - ], - ), - ), - Card( - surfaceTintColor: theme.colorScheme.surface, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 0.0), - child: Row( - children: [ - Text("Badges", style: TextStyle(fontFamily: "Eurostile Round Extended")), - Spacer(), - Text(intf.format(testPlayer.badges.length)) - ], - ), - ), - SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: [ - for (var badge in testPlayer.badges) - IconButton( - onPressed: () => showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text(badge.label, style: const TextStyle(fontFamily: "Eurostile Round Extended")), - content: SingleChildScrollView( - child: ListBody( - children: [ - Wrap( - direction: Axis.horizontal, - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - spacing: 25, - children: [ - Image.asset("res/tetrio_badges/${badge.badgeId}.png"), - Text(badge.ts != null - ? t.obtainDate(date: timestamp(badge.ts!)) - : t.assignedManualy), - ], - ) - ], - ), - ), - actions: [ - TextButton( - child: Text(t.popupActions.ok), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); - }, - ), - tooltip: badge.label, - icon: Image.asset( - "res/tetrio_badges/${badge.badgeId}.png", - height: 32, - width: 32, - errorBuilder: (context, error, stackTrace) { - return Image.network( - kIsWeb ? "https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioBadge&badge=${badge.badgeId}" : "https://tetr.io/res/badges/${badge.badgeId}.png", - height: 32, - width: 32, - errorBuilder:(context, error, stackTrace) { - return Image.asset("res/icons/kagari.png", height: 32, width: 32); - } - ); - }, - ) - ) - ], - ), - ) - ], - ), - ), - if (testPlayer.distinguishment != null) DistinguishmentThingy(testPlayer.distinguishment!), - if (testPlayer.bio != null) Card( - surfaceTintColor: theme.colorScheme.surface, - child: Column( - children: [ - Row( + Expanded( + child: Scrollbar( + controller: controller, + thumbVisibility: true, + child: SingleChildScrollView( + controller: controller, + scrollDirection: Axis.horizontal, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 450.0, + child: Column( children: [ - Spacer(), - Text(t.bio, style: TextStyle(fontFamily: "Eurostile Round Extended")), - Spacer() + NewUserThingy(player: testPlayer, showStateTimestamp: false, setState: setState), + Padding( + padding: const EdgeInsets.fromLTRB(4.0, 0.0, 4.0, 0.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded(child: ElevatedButton.icon(onPressed: (){print("ok, and?");}, icon: Icon(Icons.person_add), label: Text(t.track), style: ButtonStyle(shape: MaterialStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(12.0), right: Radius.zero)))))), + Expanded(child: ElevatedButton.icon(onPressed: (){print("ok, and?");}, icon: Icon(Icons.balance), label: Text(t.compare), style: ButtonStyle(shape: MaterialStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.zero, right: Radius.circular(12.0))))))) + ], + ), + ), + Card( + surfaceTintColor: theme.colorScheme.surface, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 0.0), + child: Row( + children: [ + Text("Badges", style: TextStyle(fontFamily: "Eurostile Round Extended")), + Spacer(), + Text(intf.format(testPlayer.badges.length)) + ], + ), + ), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + for (var badge in testPlayer.badges) + IconButton( + onPressed: () => showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text(badge.label, style: const TextStyle(fontFamily: "Eurostile Round Extended")), + content: SingleChildScrollView( + child: ListBody( + children: [ + Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 25, + children: [ + Image.asset("res/tetrio_badges/${badge.badgeId}.png"), + Text(badge.ts != null + ? t.obtainDate(date: timestamp(badge.ts!)) + : t.assignedManualy), + ], + ) + ], + ), + ), + actions: [ + TextButton( + child: Text(t.popupActions.ok), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ), + tooltip: badge.label, + icon: Image.asset( + "res/tetrio_badges/${badge.badgeId}.png", + height: 32, + width: 32, + errorBuilder: (context, error, stackTrace) { + return Image.network( + kIsWeb ? "https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioBadge&badge=${badge.badgeId}" : "https://tetr.io/res/badges/${badge.badgeId}.png", + height: 32, + width: 32, + errorBuilder:(context, error, stackTrace) { + return Image.asset("res/icons/kagari.png", height: 32, width: 32); + } + ); + }, + ) + ) + ], + ), + ) + ], + ), + ), + if (testPlayer.distinguishment != null) DistinguishmentThingy(testPlayer.distinguishment!), + if (testPlayer.bio != null) Card( + surfaceTintColor: theme.colorScheme.surface, + child: Column( + children: [ + Row( + children: [ + Spacer(), + Text(t.bio, style: TextStyle(fontFamily: "Eurostile Round Extended")), + Spacer() + ], + ), + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: MarkdownBody(data: testPlayer.bio!, styleSheet: MarkdownStyleSheet(textAlign: WrapAlignment.center)), + ) + ], + ), + ), + //if (testNews != null && testNews!.news.isNotEmpty) + Expanded(child: NewsThingy(testNews)) + ], + ) + ), + SizedBox( + width: 450.0, + child: Column( + children: [ + Card( + child: Row( + children: [ + Spacer(), + Text("test card"), + Spacer() + ], + ), + ) ], ), - Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: MarkdownBody(data: testPlayer.bio!, styleSheet: MarkdownStyleSheet(textAlign: WrapAlignment.center)), - ) - ], - ), + ), + SizedBox( + width: 450.0, + child: Column( + children: [ + Card( + child: Row( + children: [ + Spacer(), + Text("test card"), + Spacer() + ], + ), + ) + ], + ), + ), + SizedBox( + width: 450.0, + child: Column( + children: [ + Card( + child: Row( + children: [ + Spacer(), + Text("test card"), + Spacer() + ], + ), + ) + ], + ), + ), + ], ), - //if (testNews != null && testNews!.news.isNotEmpty) - Expanded(child: NewsThingy(testNews)) - ], - ) - ), - SizedBox( - width: 450.0, - child: Column( - children: [ - Card( - child: Row( - children: [ - Spacer(), - Text("test card"), - Spacer() - ], - ), - ) - ], + ), ), - ) + ), ], )); } diff --git a/lib/widgets/stat_sell_num.dart b/lib/widgets/stat_sell_num.dart index 6e415ed..44ced86 100644 --- a/lib/widgets/stat_sell_num.dart +++ b/lib/widgets/stat_sell_num.dart @@ -11,7 +11,7 @@ class StatCellNum extends StatelessWidget { required this.playerStat, required this.playerStatLabel, required this.isScreenBig, - this.smallDecimal = true, + this.smallDecimal = false, this.alertWidgets, this.fractionDigits, this.oldPlayerStat,