diff --git a/lib/gen/strings.g.dart b/lib/gen/strings.g.dart index b28bfe7..3a890e7 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: 1018 (509 per locale) +/// Strings: 1050 (525 per locale) /// -/// Built on 2024-02-08 at 20:30 UTC +/// Built on 2024-03-18 at 17:41 UTC // coverage:ignore-file // ignore_for_file: type=lint @@ -675,8 +675,30 @@ class _StringsNumOfGameActionsEn { // Translations String get pc => 'All Clears'; String get hold => 'Holds'; - String get tspinsTotal => 'T-spins total'; - String get lineClears => 'Line clears'; + String inputs({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n, + zero: '${n} key presses', + one: '${n} key press', + two: '${n} key presses', + few: '${n} key presses', + many: '${n} key presses', + other: '${n} key presses', + ); + String tspinsTotal({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n, + zero: '${n} T-spins total', + one: '${n} T-spin total', + two: '${n} T-spins total', + few: '${n} T-spins total', + many: '${n} T-spins total', + other: '${n} T-spins total', + ); + String lineClears({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n, + zero: '${n} lines cleared', + one: '${n} line cleared', + two: '${n} lines cleared', + few: '${n} lines cleared', + many: '${n} lines cleared', + other: '${n} lines cleared', + ); } // Path: popupActions @@ -1268,8 +1290,30 @@ class _StringsNumOfGameActionsRu implements _StringsNumOfGameActionsEn { // Translations @override String get pc => 'Все чисто'; @override String get hold => 'В запас'; - @override String get tspinsTotal => 'T-spins всего'; - @override String get lineClears => 'Линий очищено'; + @override String inputs({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n, + zero: '${n} нажатий клавиш', + one: '${n} нажатие на клавишу', + two: '${n} нажатия на клавишы', + few: '${n} нажатия на клавишы', + many: '${n} нажатий на клавиш', + other: '${n} нажатий на клавиш', + ); + @override String tspinsTotal({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n, + zero: '${n} T-спинов всего', + one: 'всего ${n} T-спин', + two: '${n} T-спина всего', + few: '${n} T-спина всего', + many: '${n} T-спинов всего', + other: '${n} T-спинов всего', + ); + @override String lineClears({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n, + zero: '${n} линий очищено', + one: '${n} линия очищена', + two: '${n} линии очищено', + few: '${n} линии очищено', + many: '${n} линий очищено', + other: '${n} линий очищено', + ); } // Path: popupActions @@ -1546,8 +1590,30 @@ extension on Translations { case 'playerRole.anon': return 'Anonymous'; case 'numOfGameActions.pc': return 'All Clears'; case 'numOfGameActions.hold': return 'Holds'; - case 'numOfGameActions.tspinsTotal': return 'T-spins total'; - case 'numOfGameActions.lineClears': return 'Line clears'; + case 'numOfGameActions.inputs': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n, + zero: '${n} key presses', + one: '${n} key press', + two: '${n} key presses', + few: '${n} key presses', + many: '${n} key presses', + other: '${n} key presses', + ); + case 'numOfGameActions.tspinsTotal': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n, + zero: '${n} T-spins total', + one: '${n} T-spin total', + two: '${n} T-spins total', + few: '${n} T-spins total', + many: '${n} T-spins total', + other: '${n} T-spins total', + ); + case 'numOfGameActions.lineClears': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n, + zero: '${n} lines cleared', + one: '${n} line cleared', + two: '${n} lines cleared', + few: '${n} lines cleared', + many: '${n} lines cleared', + other: '${n} lines cleared', + ); case 'popupActions.cancel': return 'Cancel'; case 'popupActions.submit': return 'Submit'; case 'popupActions.ok': return 'OK'; @@ -2065,8 +2131,30 @@ extension on _StringsRu { case 'playerRole.anon': return 'Аноним'; case 'numOfGameActions.pc': return 'Все чисто'; case 'numOfGameActions.hold': return 'В запас'; - case 'numOfGameActions.tspinsTotal': return 'T-spins всего'; - case 'numOfGameActions.lineClears': return 'Линий очищено'; + case 'numOfGameActions.inputs': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n, + zero: '${n} нажатий клавиш', + one: '${n} нажатие на клавишу', + two: '${n} нажатия на клавишы', + few: '${n} нажатия на клавишы', + many: '${n} нажатий на клавиш', + other: '${n} нажатий на клавиш', + ); + case 'numOfGameActions.tspinsTotal': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n, + zero: '${n} T-спинов всего', + one: 'всего ${n} T-спин', + two: '${n} T-спина всего', + few: '${n} T-спина всего', + many: '${n} T-спинов всего', + other: '${n} T-спинов всего', + ); + case 'numOfGameActions.lineClears': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n, + zero: '${n} линий очищено', + one: '${n} линия очищена', + two: '${n} линии очищено', + few: '${n} линии очищено', + many: '${n} линий очищено', + other: '${n} линий очищено', + ); case 'popupActions.cancel': return 'Отменить'; case 'popupActions.submit': return 'Подтвердить'; case 'popupActions.ok': return 'OK'; diff --git a/lib/utils/numers_formats.dart b/lib/utils/numers_formats.dart index b437749..af7bf36 100644 --- a/lib/utils/numers_formats.dart +++ b/lib/utils/numers_formats.dart @@ -4,5 +4,6 @@ import 'package:tetra_stats/gen/strings.g.dart'; final NumberFormat comparef = NumberFormat("+#,###.###;-#,###.###")..maximumFractionDigits = 3; final NumberFormat intf = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0); final NumberFormat f4 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 4); -final NumberFormat f3 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3); -final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2); \ No newline at end of file +final NumberFormat f3 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3); +final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2); +final NumberFormat f2l = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2)..minimumFractionDigits = 0; \ No newline at end of file diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 23bb17b..ec0c77f 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -21,6 +21,8 @@ import 'package:tetra_stats/utils/text_shadow.dart'; import 'package:tetra_stats/views/ranks_averages_view.dart' show RankAveragesView; import 'package:tetra_stats/views/tl_leaderboard_view.dart' show TLLeaderboardView; import 'package:tetra_stats/views/tl_match_view.dart' show TlMatchResultView; +import 'package:tetra_stats/widgets/finesse_thingy.dart'; +import 'package:tetra_stats/widgets/lineclears_thingy.dart'; import 'package:tetra_stats/widgets/list_tile_trailing_stats.dart'; import 'package:tetra_stats/widgets/search_box.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart'; @@ -437,7 +439,7 @@ class _MainState extends State with TickerProviderStateMixin { child: _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0) ), ],), - _History(chartsData: chartsData, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0), + _History(chartsData: chartsData, states: snapshot.data![2], changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0), _TwoRecordsThingy(sprint: snapshot.data![1]['sprint'], blitz: snapshot.data![1]['blitz'], rank: snapshot.data![0].tlSeason1.percentileRank,), // Row(children: [ // Container( @@ -464,7 +466,7 @@ class _MainState extends State with TickerProviderStateMixin { lbPositions: meAmongEveryone ), _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0), - _History(chartsData: chartsData, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0), + _History(chartsData: chartsData, states: snapshot.data![2], changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0), _RecordThingy(record: snapshot.data![1]['sprint'], rank: snapshot.data![0].tlSeason1.percentileRank), _RecordThingy(record: snapshot.data![1]['blitz'], rank: snapshot.data![0].tlSeason1.percentileRank), _OtherThingy(zen: snapshot.data![1]['zen'], bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![6],) @@ -696,6 +698,7 @@ class _TLRecords extends StatelessWidget { class _History extends StatelessWidget{ final List>> chartsData; + final List states; final String userID; final Function update; final Function changePlayer; @@ -703,7 +706,7 @@ class _History extends StatelessWidget{ /// Widget, that can show history of some stat of the player on the graph. /// Requires player [states], which is list of states and function [update], which rebuild widgets - const _History({required this.chartsData, required this.userID, required this.changePlayer, required this.update, required this.wasActiveInTL}); + const _History({required this.chartsData, required this.states, required this.userID, required this.changePlayer, required this.update, required this.wasActiveInTL}); @override Widget build(BuildContext context) { @@ -1017,6 +1020,23 @@ class _HistoryChartThigyState extends State<_HistoryChartThigy> { } } +class _HistoryTableThingy extends StatelessWidget{ + final List states; + + const _HistoryTableThingy(this.states); + // :tf: + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints){ + return SingleChildScrollView(child: Table( + children: [ + TableRow(children: [Text("Date & Time"), Text("Tr")]), + ], + )); + }); + } +} + class _TwoRecordsThingy extends StatelessWidget { final RecordSingle? sprint; final RecordSingle? blitz; @@ -1081,6 +1101,9 @@ class _TwoRecordsThingy extends StatelessWidget { children: [ if (rank != null && rank != "z") TextSpan(text: "${readableTimeDifference(sprint!.endContext!.finalTime, sprintAverages[rank]!)} ${sprintBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average\n", style: TextStyle( color: sprintBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent + )) + else TextSpan(text: "${readableTimeDifference(sprint!.endContext!.finalTime, closestAverageSprint.value)} ${sprintBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageSprint.key.toUpperCase()} rank average\n", style: TextStyle( + color: sprintBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent )), if (sprint!.rank != null) TextSpan(text: "№${sprint!.rank}", style: TextStyle(color: getColorOfRank(sprint!.rank!))), if (sprint!.rank != null) const TextSpan(text: " • "), @@ -1096,17 +1119,20 @@ class _TwoRecordsThingy extends StatelessWidget { alignment: WrapAlignment.spaceBetween, spacing: 20, children: [ - StatCellNum(playerStat: sprint!.endContext!.piecesPlaced, playerStatLabel: t.statCellNum.pieces, isScreenBig: true, higherIsBetter: true), - StatCellNum(playerStat: sprint!.endContext!.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: true, higherIsBetter: true), - StatCellNum(playerStat: sprint!.endContext!.kps, playerStatLabel: t.statCellNum.kps, fractionDigits: 2, isScreenBig: true, higherIsBetter: true,) + StatCellNum(playerStat: sprint!.endContext!.piecesPlaced, playerStatLabel: t.statCellNum.pieces, isScreenBig: true, higherIsBetter: true, smallDecimal: false), + StatCellNum(playerStat: sprint!.endContext!.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false), + StatCellNum(playerStat: sprint!.endContext!.kpp, playerStatLabel: t.statCellNum.kpp, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false), ], ), + if (sprint != null) FinesseThingy(sprint?.endContext?.finesse, sprint?.endContext?.finessePercentage), + if (sprint != null) LineclearsThingy(sprint!.endContext!.clears, sprint!.endContext!.lines, sprint!.endContext!.holds, sprint!.endContext!.tSpins), + if (sprint != null) Text("${sprint!.endContext!.inputs} KP • ${f2.format(sprint!.endContext!.kps)} KpS") ] ), Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ Row( mainAxisSize: MainAxisSize.min, @@ -1133,6 +1159,9 @@ class _TwoRecordsThingy extends StatelessWidget { children: [ if (rank != null && rank != "z") TextSpan(text: "${readableIntDifference(blitz!.endContext!.score, blitzAverages[rank]!)} ${blitzBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average\n", style: TextStyle( color: blitzBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent + )) + else TextSpan(text: "${readableIntDifference(blitz!.endContext!.score, closestAverageBlitz.value)} ${blitzBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageBlitz.key!.toUpperCase()} rank average\n", style: TextStyle( + color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent )), TextSpan(text: _dateFormat.format(blitz!.timestamp!)), if (blitz!.rank != null) const TextSpan(text: " • "), @@ -1151,11 +1180,14 @@ class _TwoRecordsThingy extends StatelessWidget { crossAxisAlignment: WrapCrossAlignment.start, spacing: 20, children: [ - StatCellNum(playerStat: blitz!.endContext!.level, playerStatLabel: t.statCellNum.level, isScreenBig: true, higherIsBetter: true), - StatCellNum(playerStat: blitz!.endContext!.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: true, higherIsBetter: true), - StatCellNum(playerStat: blitz!.endContext!.spp, playerStatLabel: t.statCellNum.spp, fractionDigits: 2, isScreenBig: true, higherIsBetter: true,) + StatCellNum(playerStat: blitz!.endContext!.level, playerStatLabel: t.statCellNum.level, isScreenBig: true, higherIsBetter: true, smallDecimal: false), + StatCellNum(playerStat: blitz!.endContext!.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false), + StatCellNum(playerStat: blitz!.endContext!.spp, playerStatLabel: t.statCellNum.spp, fractionDigits: 2, isScreenBig: true, higherIsBetter: true) ], ), + if (blitz != null) FinesseThingy(blitz?.endContext?.finesse, blitz?.endContext?.finessePercentage), + if (blitz != null) LineclearsThingy(blitz!.endContext!.clears, blitz!.endContext!.lines, blitz!.endContext!.holds, blitz!.endContext!.tSpins), + if (blitz != null) Text("${blitz!.endContext!.piecesPlaced} P • ${blitz!.endContext!.inputs} KP • ${f2.format(blitz!.endContext!.kpp)} KpP • ${f2.format(blitz!.endContext!.kps)} KpS") ], ), ]), @@ -1170,6 +1202,15 @@ class _RecordThingy extends StatelessWidget { /// Widget that displays data from [record] const _RecordThingy({required this.record, this.rank}); + Color getColorOfRank(int rank){ + if (rank == 1) return Colors.yellowAccent; + if (rank == 2) return Colors.blueGrey; + if (rank == 3) return Colors.brown[400]!; + if (rank <= 9) return Colors.blueAccent; + if (rank <= 99) return Colors.greenAccent; + return Colors.grey; + } + @override Widget build(BuildContext context) { if (record == null) return Center(child: Text(t.noRecord, textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28))); @@ -1186,219 +1227,89 @@ class _RecordThingy extends StatelessWidget { closestAverageBlitz = blitzAverages.entries.singleWhere((element) => element.value == blitzAverages.values.reduce((a, b) => (a-record!.endContext!.score).abs() < (b -record!.endContext!.score).abs() ? a : b)); blitzBetterThanClosestAverage = record!.endContext!.score > closestAverageBlitz.value; } - return LayoutBuilder(builder: (context, constraints) { - bool bigScreen = constraints.maxWidth > 768; - return ListView.builder( - physics: const AlwaysScrollableScrollPhysics(), - itemCount: 1, - itemBuilder: (BuildContext context, int index) { - return Column( + + return LayoutBuilder( + builder: (context, constraints) { + bool bigScreen = constraints.maxWidth > 768; + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, children: [ - // show mode title - if (record!.stream.contains("40l")) Text(t.sprint, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)) - else if (record!.stream.contains("blitz")) Text(t.blitz, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), - - // show main metric Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.end, - textBaseline: TextBaseline.alphabetic, + mainAxisSize: MainAxisSize.min, children: [ - // Show grade based on closest rank average - if (record!.stream.contains("40l")) Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageSprint.key}.png", height: 48) - else if (record!.stream.contains("blitz")) Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageBlitz.key}.png", height: 48), - - // Show result - if (record!.stream.contains("40l")) Text(get40lTime(record!.endContext!.finalTime.inMicroseconds), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)) - else if (record!.stream.contains("blitz")) Text(NumberFormat.decimalPattern().format(record!.endContext!.score), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + if (record!.stream.contains("40l")) Padding(padding: EdgeInsets.only(right: 8.0), + child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageSprint.key}.png", height: 96) + ), + if (record!.stream.contains("blitz")) Padding(padding: EdgeInsets.only(right: 8.0), + child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageBlitz.key}.png", height: 96) + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (record!.stream.contains("40l")) Text(t.sprint, style: TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)), + if (record!.stream.contains("blitz")) Text(t.blitz, style: TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)), + RichText(text: TextSpan( + text: record!.stream.contains("40l") ? get40lTime(record!.endContext!.finalTime.inMicroseconds) : NumberFormat.decimalPattern().format(record!.endContext!.score), + 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!.stream.contains("40l") && (rank != null && rank != "z")) TextSpan(text: "${readableTimeDifference(record!.endContext!.finalTime, sprintAverages[rank]!)} ${sprintBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average\n", style: TextStyle( + color: sprintBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent + )) + else if (record!.stream.contains("40l") && (rank == null || rank == "z")) TextSpan(text: "${readableTimeDifference(record!.endContext!.finalTime, closestAverageSprint.value)} ${sprintBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageSprint.key.toUpperCase()} rank average\n", style: TextStyle( + color: sprintBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent + )) + else if (record!.stream.contains("blitz") && (rank != null && rank != "z")) TextSpan(text: "${readableIntDifference(record!.endContext!.score, blitzAverages[rank]!)} ${blitzBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average\n", style: TextStyle( + color: blitzBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent + )) + else if (record!.stream.contains("blitz") && (rank == null || rank == "z")) TextSpan(text: "${readableIntDifference(record!.endContext!.score, closestAverageBlitz.value)} ${blitzBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageBlitz.key!.toUpperCase()} rank average\n", style: TextStyle( + color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent + )), + if (record!.rank != null) TextSpan(text: "№${record!.rank}", style: TextStyle(color: getColorOfRank(record!.rank!))), + if (record!.rank != null) const TextSpan(text: " • "), + TextSpan(text: _dateFormat.format(record!.timestamp!)), + ] + ), + ) + ],), ], ), - - // Show difference between rank average - if (record!.stream.contains("40l") && (rank != null && rank != "z")) Text( - "${readableTimeDifference(record!.endContext!.finalTime, sprintAverages[rank]!)} ${sprintBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average", - textAlign: TextAlign.center, - style: TextStyle( - color: sprintBetterThanRankAverage??false ? - Colors.greenAccent : - Colors.redAccent - ) - ) - else if (record!.stream.contains("40l") && (rank == null || rank == "z")) Text( - "${readableTimeDifference(record!.endContext!.finalTime, closestAverageSprint.value)} ${sprintBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageSprint.key!.toUpperCase()} rank average", - textAlign: TextAlign.center, - style: TextStyle( - color: sprintBetterThanClosestAverage ? - Colors.greenAccent : - Colors.redAccent - ) - ) - else if (record!.stream.contains("blitz") && (rank != null && rank != "z")) Text( - "${readableIntDifference(record!.endContext!.score, blitzAverages[rank]!)} ${blitzBetterThanRankAverage??false ? "better" : "worse"} than ${rank!.toUpperCase()} rank average", - textAlign: TextAlign.center, - style: TextStyle( - color: blitzBetterThanRankAverage??false ? - Colors.greenAccent : - Colors.redAccent - ) - ) - else if (record!.stream.contains("blitz") && (rank == null || rank == "z")) Text( - "${readableIntDifference(record!.endContext!.score, closestAverageBlitz.value)} ${blitzBetterThanClosestAverage ? "better" : "worse"} than ${closestAverageBlitz.key!.toUpperCase()} rank average", - textAlign: TextAlign.center, - style: TextStyle( - color: blitzBetterThanClosestAverage ? - Colors.greenAccent : - Colors.redAccent - ) - ), - - // Show rank if presented - if (record!.rank != null) StatCellNum(playerStat: record!.rank!, playerStatLabel: "Leaderboard Placement", isScreenBig: bigScreen, higherIsBetter: false), - - // Show when this record was obtained - Text(t.obtainDate(date: _dateFormat.format(record!.timestamp!)), textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 16)), - - // Show metrics - 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, + if (record!.stream.contains("40l")) Wrap( + alignment: WrapAlignment.spaceBetween, + spacing: 20, children: [ - if (record!.stream.contains("blitz")) StatCellNum(playerStat: record!.endContext!.level, playerStatLabel: t.statCellNum.level, isScreenBig: bigScreen, higherIsBetter: true), - if (record!.stream.contains("blitz")) StatCellNum(playerStat: record!.endContext!.spp, playerStatLabel: t.statCellNum.spp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true), - StatCellNum(playerStat: record!.endContext!.piecesPlaced, playerStatLabel: t.statCellNum.pieces, isScreenBig: bigScreen, higherIsBetter: true), - StatCellNum(playerStat: record!.endContext!.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true), - if (record!.endContext!.finesse != null) StatCellNum(playerStat: record!.endContext!.finesse!.faults, playerStatLabel: t.statCellNum.finesseFaults, isScreenBig: bigScreen, higherIsBetter: false), - if (record!.endContext!.finesse != null) StatCellNum(playerStat: record!.endContext!.finessePercentage * 100, playerStatLabel: t.statCellNum.finessePercentage, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true), - StatCellNum(playerStat: record!.endContext!.inputs, playerStatLabel: t.statCellNum.keys, isScreenBig: bigScreen, higherIsBetter: false), - StatCellNum(playerStat: record!.endContext!.kpp, playerStatLabel: t.statCellNum.kpp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: false), - StatCellNum(playerStat: record!.endContext!.kps, playerStatLabel: t.statCellNum.kps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true,), + StatCellNum(playerStat: record!.endContext!.piecesPlaced, playerStatLabel: t.statCellNum.pieces, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false), + StatCellNum(playerStat: record!.endContext!.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false), + StatCellNum(playerStat: record!.endContext!.kpp, playerStatLabel: t.statCellNum.kpp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false), ], - ), ), - - // List of actions - Padding(padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), - child: Container(width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85, - constraints: BoxConstraints(maxWidth: 452), - child: Column(crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text("${t.numOfGameActions.pc}:", style: const TextStyle(fontSize: 24)), - Text(record!.endContext!.clears.allClears.toString(), style: const TextStyle(fontSize: 24)), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text("${t.numOfGameActions.hold}:", style: const TextStyle(fontSize: 24)), - Text(record!.endContext!.holds.toString(), style: const TextStyle(fontSize: 24)), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text("${t.numOfGameActions.tspinsTotal}:", style: const 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: [ - Text("${t.numOfGameActions.lineClears}:", style: const 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)), - ], - ), - ], - ), - ), + if (record!.stream.contains("blitz")) Wrap( + alignment: WrapAlignment.spaceBetween, + crossAxisAlignment: WrapCrossAlignment.start, + spacing: 20, + children: [ + StatCellNum(playerStat: record!.endContext!.level, playerStatLabel: t.statCellNum.level, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false), + StatCellNum(playerStat: record!.endContext!.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false), + StatCellNum(playerStat: record!.endContext!.spp, playerStatLabel: t.statCellNum.spp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true) + ], ), + FinesseThingy(record?.endContext?.finesse, record?.endContext?.finessePercentage), + LineclearsThingy(record!.endContext!.clears, record!.endContext!.lines, record!.endContext!.holds, record!.endContext!.tSpins), + if (record!.stream.contains("40l")) Text("${record!.endContext!.inputs} KP • ${f2.format(record!.endContext!.kps)} KpS"), + if (record!.stream.contains("blitz")) Text("${record!.endContext!.piecesPlaced} P • ${record!.endContext!.inputs} KP • ${f2.format(record!.endContext!.kpp)} KpP • ${f2.format(record!.endContext!.kps)} KpS") ] - ); - }); - }); + ), + ), + ); + } + ); } } diff --git a/lib/widgets/finesse_thingy.dart b/lib/widgets/finesse_thingy.dart new file mode 100644 index 0000000..e913572 --- /dev/null +++ b/lib/widgets/finesse_thingy.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:tetra_stats/data_objects/tetrio.dart'; +import 'package:tetra_stats/utils/numers_formats.dart'; +import 'package:tetra_stats/utils/text_shadow.dart'; + +class FinesseThingy extends StatelessWidget{ + final Finesse? finesse; + final double? finessePercentage; + + const FinesseThingy(this.finesse, this.finessePercentage, {super.key}); + + Color getFinesseColor(){ + if (finesse == null) return Colors.grey; + if (finesse!.faults == 0) return Colors.purpleAccent; + if (finessePercentage! > 0.4) return Colors.white; + else return Colors.redAccent; + } + + @override + Widget build(BuildContext context) { + return Stack( + alignment: AlignmentDirectional.bottomStart, + children: [ + Text("f", style: TextStyle( + fontStyle: FontStyle.italic, + fontSize: 65, + height: 1.2, + )), + Positioned(child: Text("inesse", style: TextStyle(fontFamily: "Eurostile Round Extended")), left: 25, top: 20), + Positioned( + child: Text("${finesse != null ? finesse!.faults : "---"}F", style: TextStyle( + color: getFinesseColor() + )), right: 0, top: 20), + Padding( + padding: const EdgeInsets.only(left: 10.0), + child: Text("${finesse != null ? f2.format(finessePercentage! * 100) : "---.--"}%", style: TextStyle( + shadows: textShadow, + fontFamily: "Eurostile Round Extended", + fontSize: 36, + fontWeight: FontWeight.w500, + color: getFinesseColor() + )), + ) + ], + ); + } +} \ No newline at end of file diff --git a/lib/widgets/lineclears_thingy.dart b/lib/widgets/lineclears_thingy.dart new file mode 100644 index 0000000..303b607 --- /dev/null +++ b/lib/widgets/lineclears_thingy.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:tetra_stats/data_objects/tetrio.dart'; +import 'package:tetra_stats/gen/strings.g.dart'; + +class LineclearsThingy extends StatelessWidget{ + final Clears clears; + final int lines; + final int holds; + final int tSpins; + + const LineclearsThingy(this.clears, this.lines, this.holds, this.tSpins, {super.key}); + + @override + Widget build(BuildContext context) { + return Wrap( + spacing: 20, + children: [ + Container( + width: 150, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(t.numOfGameActions.lineClears(n: lines), style: TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended")), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("Quads"), Text(clears.quads.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("Triples"), Text(clears.triples.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("Doubles"), Text(clears.doubles.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("Singles"), Text(clears.singles.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("\n${t.numOfGameActions.pc}"), Text("\n${clears.allClears.toString()}")]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text(t.numOfGameActions.hold), Text(holds.toString())]), + ], + ), + ), + Container( + width: 150, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(t.numOfGameActions.tspinsTotal(n: tSpins), style: TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended")), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("T-spins triples"), Text(clears.tSpinTriples.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("T-spins doubles"), Text(clears.tSpinDoubles.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("T-spins singles"), Text(clears.tSpinSingles.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("T-spins zeros"), Text(clears.tSpinZeros.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("Mini T-spins doubles"), Text(clears.tSpinMiniDoubles.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("Mini T-spins singles"), Text(clears.tSpinMiniSingles.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("Mini T-spins zeros"), Text(clears.tSpinMiniZeros.toString())]), + ], + ), + ), + ], + ); + } +} \ No newline at end of file diff --git a/lib/widgets/tl_thingy.dart b/lib/widgets/tl_thingy.dart index afd8bec..5b04061 100644 --- a/lib/widgets/tl_thingy.dart +++ b/lib/widgets/tl_thingy.dart @@ -308,7 +308,7 @@ class _TLThingyState extends State { RichText( text: TextSpan( text: intf.format(currentTl.estTr!.esttr.truncate()), - style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500), + style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white), children: [TextSpan(text: fractionfEstTR.format(currentTl.estTr!.esttr - currentTl.estTr!.esttr.truncate()).substring(1), style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))] ), ), @@ -334,7 +334,7 @@ class _TLThingyState extends State { RichText( text: TextSpan( text: (currentTl.esttracc != null && currentTl.bestRank != "z") ? intFDiff.format(currentTl.esttracc!.truncate()) : "---", - style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 36, fontWeight: FontWeight.w500), + style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white), children: [ TextSpan(text: (currentTl.esttracc != null && currentTl.bestRank != "z") ? fractionfEstTRAcc.format(currentTl.esttracc!.isNegative ? 1 - (currentTl.esttracc! - currentTl.esttracc!.truncate()) : (currentTl.esttracc! - currentTl.esttracc!.truncate())).substring(1) : ".---", style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100)) ] diff --git a/res/i18n/strings.i18n.json b/res/i18n/strings.i18n.json index 4145f76..79ec395 100644 --- a/res/i18n/strings.i18n.json +++ b/res/i18n/strings.i18n.json @@ -238,8 +238,30 @@ "numOfGameActions":{ "pc": "All Clears", "hold": "Holds", - "tspinsTotal": "T-spins total", - "lineClears": "Line clears" + "inputs": { + "zero": "$n key presses", + "one": "$n key press", + "two": "$n key presses", + "few": "$n key presses", + "many": "$n key presses", + "other": "$n key presses" + }, + "tspinsTotal": { + "zero": "$n T-spins total", + "one": "$n T-spin total", + "two": "$n T-spins total", + "few": "$n T-spins total", + "many": "$n T-spins total", + "other": "$n T-spins total" + }, + "lineClears": { + "zero": "$n lines cleared", + "one": "$n line cleared", + "two": "$n lines cleared", + "few": "$n lines cleared", + "many": "$n lines cleared", + "other": "$n lines cleared" + } }, "popupActions":{ "cancel": "Cancel", diff --git a/res/i18n/strings_ru.i18n.json b/res/i18n/strings_ru.i18n.json index f8dba3b..7afcbbd 100644 --- a/res/i18n/strings_ru.i18n.json +++ b/res/i18n/strings_ru.i18n.json @@ -238,8 +238,30 @@ "numOfGameActions":{ "pc": "Все чисто", "hold": "В запас", - "tspinsTotal": "T-spins всего", - "lineClears": "Линий очищено" + "inputs": { + "zero": "$n нажатий клавиш", + "one": "$n нажатие на клавишу", + "two": "$n нажатия на клавишы", + "few": "$n нажатия на клавишы", + "many": "$n нажатий на клавиш", + "other": "$n нажатий на клавиш" + }, + "tspinsTotal": { + "zero": "$n T-спинов всего", + "one": "всего $n T-спин", + "two": "$n T-спина всего", + "few": "$n T-спина всего", + "many": "$n T-спинов всего", + "other": "$n T-спинов всего" + }, + "lineClears": { + "zero": "$n линий очищено", + "one": "$n линия очищена", + "two": "$n линии очищено", + "few": "$n линии очищено", + "many": "$n линий очищено", + "other": "$n линий очищено" + } }, "popupActions":{ "cancel": "Отменить",