diff --git a/lib/data_objects/tetrio.dart b/lib/data_objects/tetrio.dart index 2bdbce6..5f9c140 100644 --- a/lib/data_objects/tetrio.dart +++ b/lib/data_objects/tetrio.dart @@ -67,6 +67,35 @@ enum Stats { openerMinusInfDS } +const Map chartsShortTitles = { + Stats.tr: "TR", + Stats.glicko: "Glicko", + Stats.rd: "RD", + Stats.gp: "GP", + Stats.gw: "GW", + Stats.wr: "WR%", + Stats.apm: "APM", + Stats.pps: "PPS", + Stats.vs: "VS", + Stats.app: "APP", + Stats.dss: "DS/S", + Stats.dsp: "DS/P", + Stats.appdsp: "APP + DS/P", + Stats.vsapm: "VS/APM", + Stats.cheese: "Cheese", + Stats.gbe: "GbE", + Stats.nyaapp: "wAPP", + Stats.area: "Area", + Stats.eTR: "eTR", + Stats.acceTR: "±eTR", + Stats.opener: "Opener", + Stats.plonk: "Plonk", + Stats.infDS: "Inf. DS", + Stats.stride: "Stride", + Stats.stridemMinusPlonk: "Stride - Plonk", + Stats.openerMinusInfDS: "Opener - Inf. DS" + }; + const Map rankColors = { // thanks osk for const rankColors at https://ch.tetr.io/res/js/base.js:418 'x': Color(0xFFFF45FF), 'u': Color(0xFFFF3813), @@ -1009,10 +1038,21 @@ class TetrioPlayersLeaderboard { TetrioPlayersLeaderboard(this.type, this.leaderboard); - List getStatRanking(List leaderboard, Stats stat){ - var lb = leaderboard.map((e) => e.getStatByEnum(stat)).toList(); - lb.sort(); - return lb.reversed.toList(); + List getStatRanking(List leaderboard, Stats stat, {bool reversed = false, String country = ""}){ + List lb = List.from(leaderboard); + if (country.isNotEmpty){ + lb.removeWhere((element) => element.country != country); + } + lb.sort(((a, b) { + if (a.getStatByEnum(stat) > b.getStatByEnum(stat)){ + return reversed ? 1 : -1; + }else if (a.getStatByEnum(stat) == b.getStatByEnum(stat)){ + return 0; + }else{ + return reversed ? -1 : 1; + } + })); + return lb; } List getAverageOfRank(String rank){ // i tried to refactor it and that's was terrible @@ -1689,7 +1729,7 @@ class TetrioPlayerFromLeaderboard { username = json['username']; role = json['role']; xp = json['xp'].toDouble(); - country = json['country ']; + country = json['country']; supporter = json['supporter']; verified = json['verified']; timestamp = ts; @@ -1722,7 +1762,7 @@ class TetrioPlayerFromLeaderboard { case Stats.gw: return gamesWon; case Stats.wr: - return winrate; + return winrate*100; case Stats.apm: return apm; case Stats.pps: diff --git a/lib/gen/strings.g.dart b/lib/gen/strings.g.dart index 559f23c..4532a09 100644 --- a/lib/gen/strings.g.dart +++ b/lib/gen/strings.g.dart @@ -4,9 +4,9 @@ /// To regenerate, run: `dart run slang` /// /// Locales: 2 -/// Strings: 898 (449 per locale) +/// Strings: 906 (453 per locale) /// -/// Built on 2023-08-21 at 09:52 UTC +/// Built on 2023-09-02 at 21:37 UTC // coverage:ignore-file // ignore_for_file: type=lint @@ -251,10 +251,13 @@ class _StringsEn implements BaseTranslations { String get calcViewNoValues => 'Enter values to calculate the stats'; String get rankAveragesViewTitle => 'Ranks cutoff and average stats'; String get averages => 'Averages'; - String get lbViewZeroEntrys => 'Empty list. Looks like something is wrong...'; - String get lbViewOneEntry => 'There is only one player... What?'; - String lbViewManyEntrys({required Object numberOfPlayers}) => 'There are ${numberOfPlayers}.'; + String get lbViewZeroEntrys => 'Empty list'; + String get lbViewOneEntry => 'There is only one player'; + String lbViewManyEntrys({required Object numberOfPlayers}) => 'There are ${numberOfPlayers}'; String get everyoneAverages => 'Values for leaderboard'; + String get sortBy => 'Sort by'; + String get reversed => 'Reversed'; + String get country => 'Country'; String rankAverages({required Object rank}) => 'Values for ${rank} rank'; String players({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n, zero: '${n} players', @@ -290,6 +293,7 @@ class _StringsEn implements BaseTranslations { late final _StringsPopupActionsEn popupActions = _StringsPopupActionsEn._(_root); late final _StringsErrorsEn errors = _StringsErrorsEn._(_root); Map get countries => { + '': 'Not selected', 'AF': 'Afghanistan', 'AX': 'Åland Islands', 'AL': 'Albania', @@ -775,10 +779,13 @@ class _StringsRu implements _StringsEn { @override String get calcViewNoValues => 'Введите значения, чтобы посчитать статистику'; @override String get rankAveragesViewTitle => 'Требования рангов и средние значения'; @override String get averages => 'Средние значения'; - @override String get lbViewZeroEntrys => 'Рейтинговая таблица пуста. Похоже, что-то здесь не так...'; - @override String get lbViewOneEntry => 'В рейтинговой таблице всего один игрок... Чего?'; - @override String lbViewManyEntrys({required Object numberOfPlayers}) => 'В рейтинговой таблице находится ${numberOfPlayers}.'; + @override String get lbViewZeroEntrys => 'Рейтинговая таблица пуста'; + @override String get lbViewOneEntry => 'В рейтинговой таблице всего один игрок'; + @override String lbViewManyEntrys({required Object numberOfPlayers}) => 'В рейтинговой таблице находится ${numberOfPlayers}'; @override String get everyoneAverages => 'Значения таблицы'; + @override String get sortBy => 'Cортировать по'; + @override String get reversed => 'Наоборот'; + @override String get country => 'Страна'; @override String rankAverages({required Object rank}) => 'Значения для ${rank} ранга'; @override String players({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n, zero: '${n} игроков', @@ -814,6 +821,7 @@ class _StringsRu implements _StringsEn { @override late final _StringsPopupActionsRu popupActions = _StringsPopupActionsRu._(_root); @override late final _StringsErrorsRu errors = _StringsErrorsRu._(_root); @override Map get countries => { + '': 'Не выбрана', 'AF': 'Афганистан', 'AX': 'Аландские острова', 'AL': 'Албания', @@ -1278,10 +1286,13 @@ extension on _StringsEn { case 'calcViewNoValues': return 'Enter values to calculate the stats'; case 'rankAveragesViewTitle': return 'Ranks cutoff and average stats'; case 'averages': return 'Averages'; - case 'lbViewZeroEntrys': return 'Empty list. Looks like something is wrong...'; - case 'lbViewOneEntry': return 'There is only one player... What?'; - case 'lbViewManyEntrys': return ({required Object numberOfPlayers}) => 'There are ${numberOfPlayers}.'; + case 'lbViewZeroEntrys': return 'Empty list'; + case 'lbViewOneEntry': return 'There is only one player'; + case 'lbViewManyEntrys': return ({required Object numberOfPlayers}) => 'There are ${numberOfPlayers}'; case 'everyoneAverages': return 'Values for leaderboard'; + case 'sortBy': return 'Sort by'; + case 'reversed': return 'Reversed'; + case 'country': return 'Country'; case 'rankAverages': return ({required Object rank}) => 'Values for ${rank} rank'; case 'players': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n, zero: '${n} players', @@ -1372,6 +1383,7 @@ extension on _StringsEn { case 'errors.connection': return ({required Object code, required Object message}) => 'Some issue with connection: ${code} ${message}'; case 'errors.noSuchUser': return 'No such user'; case 'errors.socketException': return ({required Object host, required Object message}) => 'Can\'t connect with ${host}: ${message}'; + case 'countries.': return 'Not selected'; case 'countries.AF': return 'Afghanistan'; case 'countries.AX': return 'Åland Islands'; case 'countries.AL': return 'Albania'; @@ -1737,10 +1749,13 @@ extension on _StringsRu { case 'calcViewNoValues': return 'Введите значения, чтобы посчитать статистику'; case 'rankAveragesViewTitle': return 'Требования рангов и средние значения'; case 'averages': return 'Средние значения'; - case 'lbViewZeroEntrys': return 'Рейтинговая таблица пуста. Похоже, что-то здесь не так...'; - case 'lbViewOneEntry': return 'В рейтинговой таблице всего один игрок... Чего?'; - case 'lbViewManyEntrys': return ({required Object numberOfPlayers}) => 'В рейтинговой таблице находится ${numberOfPlayers}.'; + case 'lbViewZeroEntrys': return 'Рейтинговая таблица пуста'; + case 'lbViewOneEntry': return 'В рейтинговой таблице всего один игрок'; + case 'lbViewManyEntrys': return ({required Object numberOfPlayers}) => 'В рейтинговой таблице находится ${numberOfPlayers}'; case 'everyoneAverages': return 'Значения таблицы'; + case 'sortBy': return 'Cортировать по'; + case 'reversed': return 'Наоборот'; + case 'country': return 'Страна'; case 'rankAverages': return ({required Object rank}) => 'Значения для ${rank} ранга'; case 'players': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n, zero: '${n} игроков', @@ -1831,6 +1846,7 @@ extension on _StringsRu { case 'errors.connection': return ({required Object code, required Object message}) => 'Проблема с подключением: ${code} ${message}'; case 'errors.noSuchUser': return 'Нет такого пользователя'; case 'errors.socketException': return ({required Object host, required Object message}) => 'Невозможно подключиться к ${host}: ${message}'; + case 'countries.': return 'Не выбрана'; case 'countries.AF': return 'Афганистан'; case 'countries.AX': return 'Аландские острова'; case 'countries.AL': return 'Албания'; diff --git a/lib/views/calc_view.dart b/lib/views/calc_view.dart index 2920ff4..0d0eea4 100644 --- a/lib/views/calc_view.dart +++ b/lib/views/calc_view.dart @@ -10,6 +10,7 @@ double? vs; NerdStats? nerdStats; EstTr? estTr; Playstyle? playstyle; +final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2); class CalcView extends StatefulWidget { const CalcView({Key? key}) : super(key: key); @@ -129,10 +130,10 @@ class CalcState extends State { clipBehavior: Clip.hardEdge, children: [ Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 48), + padding: const EdgeInsets.fromLTRB(18, 0, 18, 44), child: SizedBox( - height: 300, - width: 300, + height: 310, + width: 310, child: RadarChart( RadarChartData( radarShape: RadarShape.polygon, @@ -154,21 +155,21 @@ class CalcState extends State { angle: angle, ); case 2: - return RadarChartTitle(text: 'VS', angle: angle); + return RadarChartTitle(text: 'VS', angle: angle, positionPercentageOffset: 0.05); case 3: - return RadarChartTitle(text: 'APP', angle: angle + 180); + return RadarChartTitle(text: 'APP', angle: angle + 180, positionPercentageOffset: 0.05); case 4: - return RadarChartTitle(text: 'DS/S', angle: angle + 180); + return RadarChartTitle(text: 'DS/S', angle: angle + 180, positionPercentageOffset: 0.05); case 5: - return RadarChartTitle(text: 'DS/P', angle: angle + 180); + return RadarChartTitle(text: 'DS/P', angle: angle + 180, positionPercentageOffset: 0.05); case 6: - return RadarChartTitle(text: 'APP+DS/P', angle: angle + 180); + return RadarChartTitle(text: 'APP+DS/P', angle: angle + 180, positionPercentageOffset: 0.05); case 7: - return RadarChartTitle(text: 'VS/APM', angle: angle + 180); + return RadarChartTitle(text: 'VS/APM', angle: angle + 180, positionPercentageOffset: 0.05); case 8: - return RadarChartTitle(text: 'Cheese', angle: angle); + return RadarChartTitle(text: 'Cheese', angle: angle, positionPercentageOffset: 0.05); case 9: - return RadarChartTitle(text: 'Gb Eff.', angle: angle); + return RadarChartTitle(text: 'Gb Eff.', angle: angle, positionPercentageOffset: 0.05); default: return const RadarChartTitle(text: ''); } @@ -212,34 +213,30 @@ class CalcState extends State { ), ), Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 48), + padding: const EdgeInsets.fromLTRB(18, 0, 18, 44), child: SizedBox( - height: 300, - width: 300, + height: 310, + width: 310, child: RadarChart( RadarChartData( radarShape: RadarShape.polygon, tickCount: 4, - ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10), + ticksTextStyle: const TextStyle(color: Colors.white24, 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), + titleTextStyle: const TextStyle(height: 1.1), + radarTouchData: RadarTouchData(), getTitle: (index, angle) { switch (index) { case 0: - return RadarChartTitle( - text: 'Opener', - angle: angle, - ); + return RadarChartTitle(text: 'Opener\n${f2.format(playstyle!.opener)}', angle: 0, positionPercentageOffset: 0.05); case 1: - return RadarChartTitle( - text: 'Stride', - angle: angle, - ); + return RadarChartTitle(text: 'Stride\n${f2.format(playstyle!.stride)}', angle: 0, positionPercentageOffset: 0.05); case 2: - return RadarChartTitle(text: 'Inf Ds', angle: angle + 180); + return RadarChartTitle(text: 'Inf Ds\n${f2.format(playstyle!.infds)}', angle: angle + 180, positionPercentageOffset: 0.05); case 3: - return RadarChartTitle(text: 'Plonk', angle: angle); + return RadarChartTitle(text: 'Plonk\n${f2.format(playstyle!.plonk)}', angle: 0, positionPercentageOffset: 0.05); default: return const RadarChartTitle(text: ''); } diff --git a/lib/views/compare_view.dart b/lib/views/compare_view.dart index c553adb..5db4584 100644 --- a/lib/views/compare_view.dart +++ b/lib/views/compare_view.dart @@ -620,49 +620,51 @@ class CompareState extends State { clipBehavior: Clip.hardEdge, children: [ Padding( - padding: - const EdgeInsets.fromLTRB(20, 20, 20, 20), - 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: ''); - } + padding: const EdgeInsets.fromLTRB(18, 0, 18, 44), + child: SizedBox( + height: 310, + width: 310, + 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, + positionPercentageOffset: 0.05 + ); + case 1: + return RadarChartTitle( + text: 'PPS', + angle: angle, + positionPercentageOffset: 0.05 + ); + case 2: + return RadarChartTitle(text: 'VS', angle: angle, positionPercentageOffset: 0.05); + case 3: + return RadarChartTitle(text: 'APP', angle: angle + 180, positionPercentageOffset: 0.05); + case 4: + return RadarChartTitle(text: 'DS/S', angle: angle + 180, positionPercentageOffset: 0.05); + case 5: + return RadarChartTitle(text: 'DS/P', angle: angle + 180, positionPercentageOffset: 0.05); + case 6: + return RadarChartTitle(text: 'APP+DS/P', angle: angle + 180, positionPercentageOffset: 0.05); + case 7: + return RadarChartTitle(text: 'VS/APM', angle: angle + 180, positionPercentageOffset: 0.05); + case 8: + return RadarChartTitle(text: 'Cheese', angle: angle, positionPercentageOffset: 0.05); + case 9: + return RadarChartTitle(text: 'Gb Eff.', angle: angle, positionPercentageOffset: 0.05); + default: + return const RadarChartTitle(text: ''); + } }, dataSets: [ RadarDataSet( @@ -721,34 +723,30 @@ class CompareState extends State { ), ), Padding( - padding: - const EdgeInsets.fromLTRB(20, 20, 20, 20), - 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), + padding: const EdgeInsets.fromLTRB(18, 0, 18, 44), + child: SizedBox( + height: 310, + width: 310, + child: RadarChart( + RadarChartData( + radarShape: RadarShape.polygon, + tickCount: 4, + ticksTextStyle: const TextStyle(color: Colors.white24, 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), + titleTextStyle: const TextStyle(height: 1.1), + radarTouchData: RadarTouchData(), getTitle: (index, angle) { switch (index) { case 0: - return RadarChartTitle(text: 'Opener',angle: angle); + return RadarChartTitle(text: 'Opener',angle: angle, positionPercentageOffset: 0.05); case 1: - return RadarChartTitle(text: 'Stride', angle: angle); + return RadarChartTitle(text: 'Stride', angle: angle, positionPercentageOffset: 0.05); case 2: - return RadarChartTitle(text: 'Inf Ds', angle: angle + 180); + return RadarChartTitle(text: 'Inf Ds', angle: angle + 180, positionPercentageOffset: 0.05); case 3: - return RadarChartTitle(text: 'Plonk', angle: angle); + return RadarChartTitle(text: 'Plonk', angle: angle, positionPercentageOffset: 0.05); default: return const RadarChartTitle(text: ''); } @@ -849,7 +847,7 @@ class CompareState extends State { ) ], ) - ] : [Text(t.compareViewNoValues(avgR: "\$avdR"))], // This is so fucked up holy shit + ] : [Text(t.compareViewNoValues(avgR: "\$avgR"))], // This is so fucked up holy shit ) ), ), diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 5e8ccf6..110731a 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -23,7 +23,7 @@ String _titleNickname = "dan63047"; final TetrioService teto = TetrioService(); late SharedPreferences prefs; var chartsData = >>[]; -List chartsShortTitles = ["TR", "Glicko", "RD", "APM", "PPS", "VS", "APP", "DS/S", "DS/P", "APP + DS/P", "VS/APM", "Cheese", "GbE", "wAPP", "Area", "eTR", "±eTR"]; +List historyShortTitles = ["TR", "Glicko", "RD", "APM", "PPS", "VS", "APP", "DS/S", "DS/P", "APP + DS/P", "VS/APM", "Cheese", "GbE", "wAPP", "Area", "eTR", "±eTR"]; int chartsIndex = 0; const allowedHeightForPlayerIdInPixels = 40.0; const allowedHeightForPlayerBioInPixels = 30.0; @@ -508,12 +508,18 @@ class _TLRecords extends StatelessWidget { fontSize: 28,)), title: Text("vs. ${value.endContext.firstWhere((element) => element.userId != userID).username}"), subtitle: Text(dateFormat.format(value.timestamp)), - trailing: Column(mainAxisAlignment: MainAxisAlignment.center, + trailing: Table(defaultColumnWidth: IntrinsicColumnWidth(), + defaultVerticalAlignment: TableCellVerticalAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + columnWidths: { + 0: FixedColumnWidth(50), + 2: FixedColumnWidth(50), + }, children: [ - Text("${f2.format(value.endContext.firstWhere((element) => element.userId == userID).secondary)} : ${f2.format(value.endContext.firstWhere((element) => element.userId != userID).secondary)} APM", style: const TextStyle(height: 1.1)), - Text("${f2.format(value.endContext.firstWhere((element) => element.userId == userID).tertiary)} : ${f2.format(value.endContext.firstWhere((element) => element.userId != userID).tertiary)} PPS", style: const TextStyle(height: 1.1)), - Text("${f2.format(value.endContext.firstWhere((element) => element.userId == userID).extra)} : ${f2.format(value.endContext.firstWhere((element) => element.userId != userID).extra)} VS", style: const TextStyle(height: 1.1)), - ]), + TableRow(children: [Text(f2.format(value.endContext.firstWhere((element) => element.userId == userID).secondary), textAlign: TextAlign.right, style: const TextStyle(height: 1.1)), const Text(" :", style: const TextStyle(height: 1.1)), Text(f2.format(value.endContext.firstWhere((element) => element.userId != userID).secondary), textAlign: TextAlign.right, style: const TextStyle(height: 1.1)), const Text(" APM", textAlign: TextAlign.right, style: const TextStyle(height: 1.1))]), + TableRow(children: [Text(f2.format(value.endContext.firstWhere((element) => element.userId == userID).tertiary), textAlign: TextAlign.right, style: const TextStyle(height: 1.1)), const Text(" :", style: const TextStyle(height: 1.1)), Text(f2.format(value.endContext.firstWhere((element) => element.userId != userID).tertiary), textAlign: TextAlign.right, style: const TextStyle(height: 1.1)), const Text(" PPS", textAlign: TextAlign.right, style: const TextStyle(height: 1.1))]), + TableRow(children: [Text(f2.format(value.endContext.firstWhere((element) => element.userId == userID).extra), textAlign: TextAlign.right, style: const TextStyle(height: 1.1)), const Text(" :", style: const TextStyle(height: 1.1)), Text(f2.format(value.endContext.firstWhere((element) => element.userId != userID).extra), textAlign: TextAlign.right, style: const TextStyle(height: 1.1)), const Text(" VS", textAlign: TextAlign.right, style: const TextStyle(height: 1.1))]), + ],), onTap: (){Navigator.push( context, MaterialPageRoute( @@ -546,7 +552,7 @@ class _History extends StatelessWidget{ update(); } ), - if(chartsData[chartsIndex].value!.length > 1) _HistoryChartThigy(data: chartsData[chartsIndex].value!, title: "ss", yAxisTitle: chartsShortTitles[chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(),) + if(chartsData[chartsIndex].value!.length > 1) _HistoryChartThigy(data: chartsData[chartsIndex].value!, title: "ss", yAxisTitle: historyShortTitles[chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(),) else Center(child: Text(t.notEnoughData, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28))) ], ), diff --git a/lib/views/rank_averages_view.dart b/lib/views/rank_averages_view.dart index 9b0c1ff..11a13fb 100644 --- a/lib/views/rank_averages_view.dart +++ b/lib/views/rank_averages_view.dart @@ -6,39 +6,9 @@ import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/views/main_view.dart' show MainView, f4, f2; -const List chartsShortTitles = [ - "TR", - "Glicko", - "RD", - "GP", - "GW", - "WR%", - "APM", - "PPS", - "VS", - "APP", - "DS/S", - "DS/P", - "APP + DS/P", - "VS/APM", - "Cheese", - "GbE", - "wAPP", - "Area", - "eTR", - "±eTR", - "Opener", - "Plonk", - "Inf. DS", - "Stride", - "Stride - Plonk", - "Opener - Inf. DS" - ]; -var chartsShortTitlesDropdowns = [for (String e in chartsShortTitles) DropdownMenuItem(value: e,child: Text(e),)]; -int chartsIndexX = 0; -int chartsIndexY = 6; -//final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms(); -double pfpHeight = 128; +var chartsShortTitlesDropdowns = [for (MapEntry e in chartsShortTitles.entries) DropdownMenuItem(value: e.key, child: Text(e.value),)]; +Stats chartsX = Stats.tr; +Stats chartsY = Stats.apm; class RankView extends StatefulWidget { final List rank; @@ -154,9 +124,9 @@ class RankState extends State with SingleTickerProviderStateMixin { child: Text(t.currentAxis(axis: "X"), style: const TextStyle(fontSize: 22))), DropdownButton( items: chartsShortTitlesDropdowns, - value: chartsShortTitlesDropdowns[chartsIndexX].value, + value: chartsX, onChanged: (value) { - chartsIndexX = chartsShortTitlesDropdowns.indexWhere((element) => element.value == value); + chartsX = value; _justUpdate(); } ), @@ -174,9 +144,9 @@ class RankState extends State with SingleTickerProviderStateMixin { ), DropdownButton( items: chartsShortTitlesDropdowns, - value: chartsShortTitlesDropdowns[chartsIndexY].value, + value: chartsY, onChanged: (value) { - chartsIndexY = chartsShortTitlesDropdowns.indexWhere((element) => element.value == value); + chartsY = value; _justUpdate(); } ), @@ -195,11 +165,11 @@ class RankState extends State with SingleTickerProviderStateMixin { : const EdgeInsets.fromLTRB(0, 40, 16, 48), child: ScatterChart( ScatterChartData( - scatterSpots: [ for (TetrioPlayerFromLeaderboard entry in widget.rank[1]["entries"]) _MyScatterSpot(takeStat(entry, chartsShortTitles[chartsIndexX]), takeStat(entry, chartsShortTitles[chartsIndexY]), entry.userId, entry.username, color: rankColors[entry.rank])], + scatterSpots: [ for (TetrioPlayerFromLeaderboard entry in widget.rank[1]["entries"]) _MyScatterSpot(entry.getStatByEnum(chartsX) as double, entry.getStatByEnum(chartsY) as double, entry.userId, entry.username, color: rankColors[entry.rank])], scatterTouchData: ScatterTouchData(touchTooltipData: ScatterTouchTooltipData( fitInsideHorizontally: true, fitInsideVertically: true, getTooltipItems: (touchedSpot) { touchedSpot as _MyScatterSpot; - return ScatterTooltipItem("${touchedSpot.nickname}\n", textStyle: const TextStyle(fontFamily: "Eurostile Round Extended"), children: [TextSpan(text: "${f4.format(touchedSpot.x)} ${chartsShortTitles[chartsIndexX]}\n${f4.format(touchedSpot.y)} ${chartsShortTitles[chartsIndexY]}", style: const TextStyle(fontFamily: "Eurostile Round"))]); + return ScatterTooltipItem("${touchedSpot.nickname}\n", textStyle: const TextStyle(fontFamily: "Eurostile Round Extended"), children: [TextSpan(text: "${f4.format(touchedSpot.x)} ${chartsShortTitles[chartsX]}\n${f4.format(touchedSpot.y)} ${chartsShortTitles[chartsY]}", style: const TextStyle(fontFamily: "Eurostile Round"))]); }), touchCallback:(event, response) { if (event.runtimeType == FlTapDownEvent && response?.touchedSpot?.spot != null){ @@ -956,66 +926,6 @@ class _ListEntry extends StatelessWidget { ); } } - -double takeStat(TetrioPlayerFromLeaderboard entry, String stat) { - switch (stat) { - case "TR": - return entry.rating; - case "Glicko": - return entry.glicko; - case "RD": - return entry.rd; - case "GP": - return entry.gamesPlayed.toDouble(); - case "GW": - return entry.gamesWon.toDouble(); - case "WR%": - return entry.winrate*100; - case "APM": - return entry.apm; - case "PPS": - return entry.pps; - case "VS": - return entry.vs; - case "APP": - return entry.nerdStats.app; - case "DS/S": - return entry.nerdStats.dss; - case "DS/P": - return entry.nerdStats.dsp; - case "APP + DS/P": - return entry.nerdStats.appdsp; - case "VS/APM": - return entry.nerdStats.vsapm; - case "Cheese": - return entry.nerdStats.cheese; - case "GbE": - return entry.nerdStats.gbe; - case "wAPP": - return entry.nerdStats.nyaapp; - case "Area": - return entry.nerdStats.area; - case "eTR": - return entry.estTr.esttr; - case "±eTR": - return entry.esttracc; - case "Opener": - return entry.playstyle.opener; - case "Plonk": - return entry.playstyle.plonk; - case "Inf. DS": - return entry.playstyle.infds; - case "Stride": - return entry.playstyle.stride; - case "Stride - Plonk": - return entry.playstyle.stride - entry.playstyle.plonk; - case "Opener - Inf. DS": - return entry.playstyle.opener - entry.playstyle.infds; - default: - throw ArgumentError.value(stat, "Incorrect stat", "We don't have that stat"); - } -} - class _MyScatterSpot extends ScatterSpot{ String id; String nickname; diff --git a/lib/views/tl_leaderboard_view.dart b/lib/views/tl_leaderboard_view.dart index ca131fe..540d368 100644 --- a/lib/views/tl_leaderboard_view.dart +++ b/lib/views/tl_leaderboard_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/services/tetrio_crud.dart'; import 'package:tetra_stats/views/main_view.dart'; @@ -7,6 +8,11 @@ import 'package:tetra_stats/views/rank_averages_view.dart'; import 'package:tetra_stats/views/ranks_averages_view.dart'; final TetrioService teto = TetrioService(); +List itemStats = [for (MapEntry e in chartsShortTitles.entries) DropdownMenuItem(value: e.key, child: Text(e.value))]; +Stats sortBy = Stats.tr; +bool reversed = false; +List itemCountries = [for (MapEntry e in t.countries.entries) DropdownMenuItem(value: e.key, child: Text(e.value))]; +String country = ""; class TLLeaderboardView extends StatefulWidget { const TLLeaderboardView({Key? key}) : super(key: key); @@ -51,7 +57,7 @@ class TLLeaderboardState extends State { case ConnectionState.active: return const Center(child: Text('Fetching...')); case ConnectionState.done: - final allPlayers = snapshot.data?.leaderboard; + final allPlayers = snapshot.data?.getStatRanking(snapshot.data!.leaderboard, sortBy, reversed: reversed, country: country); return NestedScrollView( headerSliverBuilder: (context, value) { String howManyPlayers(int numberOfPlayers) => Intl.plural( @@ -87,6 +93,61 @@ class TLLeaderboardState extends State { style: const TextStyle(fontSize: 25))) ],) )), + SliverToBoxAdapter(child: Padding( + padding: const EdgeInsets.only(left: 16), + child: Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 16, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + children: [ + Text("${t.sortBy}: ", + style: const TextStyle(color: Colors.white, fontSize: 25)), + DropdownButton(items: itemStats, value: sortBy, onChanged: ((value) { + sortBy = value; + setState(() {}); + }),), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + children: [ + Text("${t.reversed}: ", + style: const TextStyle(color: Colors.white, fontSize: 25)), + Padding( + padding: const EdgeInsets.fromLTRB(0, 5.5, 0, 7.5), + child: Checkbox(value: reversed, + checkColor: Colors.black, + onChanged: ((value) { + reversed = value!; + setState(() {}); + }),), + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + children: [ + Text("${t.country}: ", + style: const TextStyle(color: Colors.white, fontSize: 25)), + DropdownButton(items: itemCountries, value: country, onChanged: ((value) { + country = value; + setState(() {}); + }),), + ], + ), + ], + ), + ),), const SliverToBoxAdapter(child: Divider()) ]; }, @@ -98,7 +159,7 @@ class TLLeaderboardState extends State { leading: Text((index+1).toString(), style: bigScreen ? const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28) : null), title: Text(allPlayers[index].username, style: const TextStyle(fontFamily: "Eurostile Round Extended")), subtitle: Text( - "${f2.format(allPlayers[index].apm)} APM, ${f2.format(allPlayers[index].pps)} PPS, ${f2.format(allPlayers[index].vs)} VS, ${f2.format(allPlayers[index].nerdStats.app)} APP, ${f2.format(allPlayers[index].nerdStats.vsapm)} VS/APM"), + sortBy == Stats.tr ? "${f2.format(allPlayers[index].apm)} APM, ${f2.format(allPlayers[index].pps)} PPS, ${f2.format(allPlayers[index].vs)} VS, ${f2.format(allPlayers[index].nerdStats.app)} APP, ${f2.format(allPlayers[index].nerdStats.vsapm)} VS/APM" : "${f4.format(allPlayers[index].getStatByEnum(sortBy))} ${chartsShortTitles[sortBy]}"), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/widgets/tl_thingy.dart b/lib/widgets/tl_thingy.dart index cf9e21e..8a7072f 100644 --- a/lib/widgets/tl_thingy.dart +++ b/lib/widgets/tl_thingy.dart @@ -69,7 +69,8 @@ class TLThingy extends StatelessWidget { maximum: tl.prevAt.toDouble(), interval: tl.prevAt.toDouble() - tl.nextAt.toDouble(), ranges: [LinearGaugeRange(startValue: tl.standing.toDouble() <= tl.prevAt.toDouble() ? tl.standing.toDouble() : tl.prevAt.toDouble(), endValue: tl.prevAt.toDouble(), color: Colors.cyanAccent,)], - //barPointers: [LinearBarPointer(value: 80)], + markerPointers: [LinearShapePointer(value: tl.standing.toDouble() <= tl.prevAt.toDouble() ? tl.standing.toDouble() : tl.prevAt.toDouble(), position: LinearElementPosition.inside, shapeType: LinearShapePointerType.triangle, color: Colors.white, height: 20), + LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: tl.standing.toDouble() <= tl.prevAt.toDouble() ? tl.standing.toDouble() : tl.prevAt.toDouble(), child: Text(NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(tl.standing)))], isAxisInversed: true, isMirrored: true, showTicks: true, @@ -97,7 +98,6 @@ class TLThingy extends StatelessWidget { if (tl.apm != null) StatCellNum(playerStat: tl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.apm, higherIsBetter: true, oldPlayerStat: oldTl?.apm), if (tl.pps != null) StatCellNum(playerStat: tl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.pps, higherIsBetter: true, oldPlayerStat: oldTl?.pps), if (tl.vs != null) StatCellNum(playerStat: tl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.vs, higherIsBetter: true, oldPlayerStat: oldTl?.vs), - if (tl.standing > 0) StatCellNum(playerStat: tl.standing, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.lbp, higherIsBetter: false, oldPlayerStat: oldTl?.standing), if (tl.standingLocal > 0) StatCellNum(playerStat: tl.standingLocal, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.lbpc, higherIsBetter: false, oldPlayerStat: oldTl?.standingLocal), StatCellNum(playerStat: tl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.gamesPlayed, higherIsBetter: true, oldPlayerStat: oldTl?.gamesPlayed), StatCellNum(playerStat: tl.gamesWon, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.gamesWonTL, higherIsBetter: true, oldPlayerStat: oldTl?.gamesWon), @@ -352,10 +352,10 @@ class TLThingy extends StatelessWidget { clipBehavior: Clip.hardEdge, children: [ Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 48), + padding: const EdgeInsets.fromLTRB(18, 0, 18, 44), child: SizedBox( - height: 300, - width: 300, + height: 310, + width: 310, child: RadarChart( RadarChartData( radarShape: RadarShape.polygon, @@ -377,21 +377,21 @@ class TLThingy extends StatelessWidget { angle: angle, ); case 2: - return RadarChartTitle(text: 'VS', angle: angle); + return RadarChartTitle(text: 'VS', angle: angle, positionPercentageOffset: 0.05); case 3: - return RadarChartTitle(text: 'APP', angle: angle + 180); + return RadarChartTitle(text: 'APP', angle: angle + 180, positionPercentageOffset: 0.05); case 4: - return RadarChartTitle(text: 'DS/S', angle: angle + 180); + return RadarChartTitle(text: 'DS/S', angle: angle + 180, positionPercentageOffset: 0.05); case 5: - return RadarChartTitle(text: 'DS/P', angle: angle + 180); + return RadarChartTitle(text: 'DS/P', angle: angle + 180, positionPercentageOffset: 0.05); case 6: - return RadarChartTitle(text: 'APP+DS/P', angle: angle + 180); + return RadarChartTitle(text: 'APP+DS/P', angle: angle + 180, positionPercentageOffset: 0.05); case 7: - return RadarChartTitle(text: 'VS/APM', angle: angle + 180); + return RadarChartTitle(text: 'VS/APM', angle: angle + 180, positionPercentageOffset: 0.05); case 8: - return RadarChartTitle(text: 'Cheese', angle: angle); + return RadarChartTitle(text: 'Cheese', angle: angle, positionPercentageOffset: 0.05); case 9: - return RadarChartTitle(text: 'Gb Eff.', angle: angle); + return RadarChartTitle(text: 'Gb Eff.', angle: angle, positionPercentageOffset: 0.05); default: return const RadarChartTitle(text: ''); } @@ -435,34 +435,30 @@ class TLThingy extends StatelessWidget { ), ), Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 48), + padding: const EdgeInsets.fromLTRB(18, 0, 18, 44), child: SizedBox( - height: 300, - width: 300, + height: 310, + width: 310, child: RadarChart( RadarChartData( radarShape: RadarShape.polygon, tickCount: 4, - ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10), + ticksTextStyle: const TextStyle(color: Colors.white24, 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), + titleTextStyle: const TextStyle(height: 1.1), + radarTouchData: RadarTouchData(), getTitle: (index, angle) { switch (index) { case 0: - return RadarChartTitle( - text: 'Opener', - angle: angle, - ); + return RadarChartTitle(text: 'Opener\n${f2.format(tl.playstyle!.opener)}', angle: 0, positionPercentageOffset: 0.05); case 1: - return RadarChartTitle( - text: 'Stride', - angle: angle, - ); + return RadarChartTitle(text: 'Stride\n${f2.format(tl.playstyle!.stride)}', angle: 0, positionPercentageOffset: 0.05); case 2: - return RadarChartTitle(text: 'Inf Ds', angle: angle + 180); + return RadarChartTitle(text: 'Inf Ds\n${f2.format(tl.playstyle!.infds)}', angle: angle + 180, positionPercentageOffset: 0.05); case 3: - return RadarChartTitle(text: 'Plonk', angle: angle); + return RadarChartTitle(text: 'Plonk\n${f2.format(tl.playstyle!.plonk)}', angle: 0, positionPercentageOffset: 0.05); default: return const RadarChartTitle(text: ''); } diff --git a/res/i18n/strings.i18n.json b/res/i18n/strings.i18n.json index 35af696..97b5e9f 100644 --- a/res/i18n/strings.i18n.json +++ b/res/i18n/strings.i18n.json @@ -100,10 +100,13 @@ "calcViewNoValues": "Enter values to calculate the stats", "rankAveragesViewTitle": "Ranks cutoff and average stats", "averages": "Averages", - "lbViewZeroEntrys": "Empty list. Looks like something is wrong...", - "lbViewOneEntry": "There is only one player... What?", - "lbViewManyEntrys": "There are ${numberOfPlayers}.", + "lbViewZeroEntrys": "Empty list", + "lbViewOneEntry": "There is only one player", + "lbViewManyEntrys": "There are ${numberOfPlayers}", "everyoneAverages": "Values for leaderboard", + "sortBy": "Sort by", + "reversed": "Reversed", + "country": "Country", "rankAverages": "Values for $rank rank", "players":{ "zero": "$n players", @@ -205,6 +208,8 @@ "socketException": "Can't connect with ${host}: ${message}" }, "countries(map)": { + "": "Not selected", + "AF": "Afghanistan", "AX": "\u00c5land Islands", "AL": "Albania", diff --git a/res/i18n/strings_ru.i18n.json b/res/i18n/strings_ru.i18n.json index 1eadca7..85bd40d 100644 --- a/res/i18n/strings_ru.i18n.json +++ b/res/i18n/strings_ru.i18n.json @@ -100,10 +100,13 @@ "calcViewNoValues": "Введите значения, чтобы посчитать статистику", "rankAveragesViewTitle": "Требования рангов и средние значения", "averages": "Средние значения", - "lbViewZeroEntrys": "Рейтинговая таблица пуста. Похоже, что-то здесь не так...", - "lbViewOneEntry": "В рейтинговой таблице всего один игрок... Чего?", - "lbViewManyEntrys": "В рейтинговой таблице находится ${numberOfPlayers}.", + "lbViewZeroEntrys": "Рейтинговая таблица пуста", + "lbViewOneEntry": "В рейтинговой таблице всего один игрок", + "lbViewManyEntrys": "В рейтинговой таблице находится ${numberOfPlayers}", "everyoneAverages": "Значения таблицы", + "sortBy": "Cортировать по", + "reversed": "Наоборот", + "country": "Страна", "rankAverages": "Значения для $rank ранга", "players":{ "zero": "$n игроков", @@ -205,6 +208,8 @@ "socketException": "Невозможно подключиться к ${host}: ${message}" }, "countries(map)": { + "": "Не выбрана", + "AF": "Афганистан", "AX": "Аландские острова", "AL": "Албания",