diff --git a/lib/data_objects/freyhoe_test.dart b/lib/data_objects/freyhoe_test.dart index 7a6eeb3..5ca2a05 100644 --- a/lib/data_objects/freyhoe_test.dart +++ b/lib/data_objects/freyhoe_test.dart @@ -1,9 +1,9 @@ -import 'dart:convert'; +//import 'dart:convert'; import 'dart:io'; -import 'package:path_provider/path_provider.dart'; +//import 'package:path_provider/path_provider.dart'; -import 'tetrio_multiplayer_replay.dart'; +//import 'tetrio_multiplayer_replay.dart'; /// That thing allows me to test my new staff i'm trying to implement void main() async { @@ -20,6 +20,6 @@ void main() async { // List> board = [for (var i = 0 ; i < 40; i++) [for (var i = 0 ; i < 10; i++) Tetromino.empty]]; // print(replay.rawJson); - print(""); + //print(""); exit(0); } \ No newline at end of file diff --git a/lib/gen/strings.g.dart b/lib/gen/strings.g.dart index 3a890e7..ee03a73 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: 1050 (525 per locale) +/// Strings: 1098 (549 per locale) /// -/// Built on 2024-03-18 at 17:41 UTC +/// Built on 2024-03-20 at 22:41 UTC // coverage:ignore-file // ignore_for_file: type=lint @@ -181,6 +181,14 @@ class Translations implements BaseTranslations { String get stoppedBeingTracked => 'Removed from tracking list!'; String get tlLeaderboard => 'Tetra League leaderboard'; String get noRecords => 'No records'; + String noOldRecords({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n, + zero: 'No records', + one: '${n} record', + two: '${n} records', + few: '${n} records', + many: '${n} records', + other: '${n} records', + ); String get noRecord => 'No record'; String get botRecord => 'Bots are not allowed to set records'; String get anonRecord => 'Guests are not allowed to set records'; @@ -205,6 +213,9 @@ class Translations implements BaseTranslations { String comparingWith({required Object newDate, required Object oldDate}) => 'Data from ${newDate} comparing with ${oldDate}'; String get top => 'Top'; String get topRank => 'Top Rank'; + String verdictGeneral({required Object n, required Object verdict, required Object rank}) => '${n} ${verdict} than ${rank} rank average'; + String get verdictBetter => 'better'; + String get verdictWorse => 'worse'; String gamesUntilRanked({required Object left}) => '${left} games until being ranked'; String get nerdStats => 'Nerd Stats'; String get playersYouTrack => 'Players you track'; @@ -228,8 +239,14 @@ class Translations implements BaseTranslations { String get yourIDAlertTitle => 'Your nickname in TETR.IO'; String get yourIDText => 'When app loads, it will retrieve data for this account'; String get language => 'Language'; + String get customization => 'Customization'; + String get customizationDescription => 'There is only one toggle, planned to add more settings'; + String get lbStats => 'Show leaderboard based stats'; + String get lbStatsDescription => 'That will impact on loading times, but will allow you to see position on LB by stats and comparison with average values'; String get aboutApp => 'About app'; String aboutAppText({required Object appName, required Object packageName, required Object version, required Object buildNumber}) => '${appName} (${packageName}) Version ${version} Build ${buildNumber}\n\nDeveloped by dan63047\nFormulas provided by kerrmunism\nHistory provided by p1nkl0bst3r\nTETR.IO replay grabber API by szy'; + String get oskKagari => 'Osk Kagari gimmick'; + String get oskKagariDescription => 'If on, osk\'s rank on main view will be rendered as :kagari:'; String stateViewTitle({required Object nickname, required Object date}) => '${nickname} account on ${date}'; String statesViewTitle({required Object number, required Object nickname}) => '${number} states of ${nickname} account'; String matchesViewTitle({required Object nickname}) => '${nickname} TL matches'; @@ -722,21 +739,30 @@ class _StringsErrorsEn { // Translations String connection({required Object code, required Object message}) => 'Some issue with connection: ${code} ${message}'; String get noSuchUser => 'No such user'; + String get noSuchUserSub => 'Either you mistyped something, or the account no longer exists'; + String get discordNotAssigned => 'No user assigned to given Discord ID'; + String get discordNotAssignedSub => 'Make sure you provided valid ID'; String get history => 'History for that player is missing'; + String get actionSuggestion => 'Perhaps, you want to'; String get p1nkl0bst3rTLmatches => 'No Tetra League matches was found'; String get clientException => 'No internet connection'; - String get forbidden => 'Your IP address is blocked.\nChange IP address or reach out to osk'; - String get tooManyRequests => 'You have been rate limited. Try again later'; + String get forbidden => 'Your IP address is blocked'; + String forbiddenSub({required Object nickname}) => 'If you are using VPN or Proxy, turn it off. If this does not help, reach out to ${nickname}'; + String get tooManyRequests => 'You have been rate limited.'; + String get tooManyRequestsSub => 'Wait a few moments and try again'; String get internal => 'Something happend on the tetr.io side'; + String get internalSub => 'osk, probably, already aware about it'; String get internalWebVersion => 'Something happend on the tetr.io side (or on oskware_bridge, idk honestly)'; - String get oskwareBridge => 'Something happend with oskware_bridge. Let dan63047 know'; - String get p1nkl0bst3rForbidden => 'Third party API blocked your IP address.\nChange IP address or reach out to p1nkl0bst3r'; + String get internalWebVersionSub => 'If osk status page says that everything is ok, let dan63047 know about this issue'; + String get oskwareBridge => 'Something happend with oskware_bridge'; + String get oskwareBridgeSub => 'Let dan63047 know'; + String get p1nkl0bst3rForbidden => 'Third party API blocked your IP address'; String get p1nkl0bst3rTooManyRequests => 'Too many requests to third party API. Try again later'; String get p1nkl0bst3rinternal => 'Something happend on the p1nkl0bst3r side'; String get p1nkl0bst3rinternalWebVersion => 'Something happend on the p1nkl0bst3r side (or on oskware_bridge, idk honestly)'; String get replayAlreadySaved => 'Replay already saved'; String get replayExpired => 'Replay expired and not available anymore'; - String get replayRejected => 'Third party API blocked your IP address.\nChange IP address or reach out to szy'; + String get replayRejected => 'Third party API blocked your IP address'; } // Path: @@ -796,6 +822,14 @@ class _StringsRu implements Translations { @override String get compare => 'Сравнить'; @override String get tlLeaderboard => 'Рейтинговая таблица'; @override String get noRecords => 'Нет записей'; + @override String noOldRecords({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n, + zero: 'Нет записей', + one: 'Всего один матч', + two: 'Всего ${n} матча', + few: 'Всего ${n} матча', + many: 'Всего ${n} матчей', + other: '${n} матчей', + ); @override String get noRecord => 'Нет рекорда'; @override String get botRecord => 'Ботам нельзя ставить рекорды'; @override String get anonRecord => 'Гостям нельзя ставить рекорды'; @@ -820,6 +854,9 @@ class _StringsRu implements Translations { @override String comparingWith({required Object newDate, required Object oldDate}) => 'Данные от ${newDate} в сравнении с данными от ${oldDate}'; @override String get top => 'Топ'; @override String get topRank => 'Топ Ранг'; + @override String verdictGeneral({required Object verdict, required Object rank, required Object n}) => '${verdict} среднего ${rank} ранга на ${n}'; + @override String get verdictBetter => 'Лучше'; + @override String get verdictWorse => 'Хуже'; @override String gamesUntilRanked({required Object left}) => '${left} матчей до получения рейтинга'; @override String get nerdStats => 'Для задротов'; @override String get playersYouTrack => 'Отслеживаемые игроки'; @@ -843,8 +880,14 @@ class _StringsRu implements Translations { @override String get yourIDAlertTitle => 'Ваш ник в TETR.IO'; @override String get yourIDText => 'При запуске приложения оно будет получать статистику этого игрока.'; @override String get language => 'Язык (Language)'; + @override String get customization => 'Кастомизация'; + @override String get customizationDescription => 'Здесь только один переключатель, в планах добавить больше'; + @override String get lbStats => 'Показывать статистику, основанную на рейтинговой таблице'; + @override String get lbStatsDescription => 'Это повлияет на время загрузки, но позволит видеть положение в рейтинге и сравнение со средними значениями по рангу по каждой стате'; @override String get aboutApp => 'О приложении'; @override String aboutAppText({required Object appName, required Object packageName, required Object version, required Object buildNumber}) => '${appName} (${packageName}) Версия ${version} Сборка ${buildNumber}\n\nРазработал dan63047\nФормулы предоставил kerrmunism\nИсторию предоставляет p1nkl0bst3r\nВозможность скачивать повторы из TETR.IO предоставляет szy'; + @override String get oskKagari => '"Оск Кагари" прикол'; + @override String get oskKagariDescription => 'Если включено, вместо настоящего ранга оска будет рендерится :kagari:'; @override String stateViewTitle({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}'; @override String statesViewTitle({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}'; @override String matchesViewTitle({required Object nickname}) => 'Матчи аккаунта ${nickname}'; @@ -1300,7 +1343,7 @@ class _StringsNumOfGameActionsRu implements _StringsNumOfGameActionsEn { ); @override String tspinsTotal({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n, zero: '${n} T-спинов всего', - one: 'всего ${n} T-спин', + one: 'Всего ${n} T-спин', two: '${n} T-спина всего', few: '${n} T-спина всего', many: '${n} T-спинов всего', @@ -1337,21 +1380,30 @@ class _StringsErrorsRu implements _StringsErrorsEn { // Translations @override String connection({required Object code, required Object message}) => 'Проблема с подключением: ${code} ${message}'; @override String get noSuchUser => 'Нет такого пользователя'; + @override String get noSuchUserSub => 'Либо вы ошиблись при вводе, либо аккаунта больше не существует'; + @override String get discordNotAssigned => 'К данному Discord ID не привязан аккаунт'; + @override String get discordNotAssignedSub => 'Убедитесь в том, что вы вставили правильный ID'; @override String get history => 'История данного игрока отсутствует'; + @override String get actionSuggestion => 'Возможно, вы хотите'; @override String get p1nkl0bst3rTLmatches => 'Старых матчей Тетра Лиги не было найдено'; @override String get clientException => 'Нет соединения с интернетом'; - @override String get forbidden => 'Ваш IP адрес заблокирован.\nСмените IP адрес или свяжитесь с osk-ом'; - @override String get tooManyRequests => 'Слишком много запросов. Попробуйте позже'; + @override String get forbidden => 'Ваш IP адрес заблокирован'; + @override String forbiddenSub({required Object nickname}) => 'Если у вас работает VPN или прокси, выключите его. Если это не помогло, свяжитесь с ${nickname}'; + @override String get tooManyRequests => 'Слишком много запросов'; + @override String get tooManyRequestsSub => 'Подождите немного и попробуйте снова'; @override String get internal => 'Что-то случилось на стороне tetr.io'; + @override String get internalSub => 'Скорее всего, osk уже в курсе об этом'; @override String get internalWebVersion => 'Что-то случилось на стороне tetr.io (или на стороне oskware_bridge, я хз если честно)'; - @override String get oskwareBridge => 'Что-то случилось с oskware_bridge. Дайте dan63047 знать'; - @override String get p1nkl0bst3rForbidden => 'Стороннее API заблокировало ваш IP адрес.\nСмените IP адрес или свяжитесь с p1nkl0bst3r-ом'; + @override String get internalWebVersionSub => 'Если статус страница osk-а говорит, что всё ок - свяжитесь с dan63047'; + @override String get oskwareBridge => 'Что-то случилось с oskware_bridge'; + @override String get oskwareBridgeSub => 'Дайте dan63047 знать'; + @override String get p1nkl0bst3rForbidden => 'Стороннее API заблокировало ваш IP адрес'; @override String get p1nkl0bst3rTooManyRequests => 'Слишком много запросов к стороннему API. Попробуйте позже'; @override String get p1nkl0bst3rinternal => 'Что-то случилось на стороне p1nkl0bst3r-а'; @override String get p1nkl0bst3rinternalWebVersion => 'Что-то случилось на стороне p1nkl0bst3r-а (или на стороне oskware_bridge, я хз если честно)'; @override String get replayAlreadySaved => 'Повтор уже сохранён'; @override String get replayExpired => 'Повтор истёк и больше недоступен'; - @override String get replayRejected => 'Стороннее API заблокировало ваш IP адрес.\nСмените IP адрес или свяжитесь с szy'; + @override String get replayRejected => 'Стороннее API заблокировало ваш IP адрес'; } /// Flat map(s) containing all translations. @@ -1403,6 +1455,14 @@ extension on Translations { case 'stoppedBeingTracked': return 'Removed from tracking list!'; case 'tlLeaderboard': return 'Tetra League leaderboard'; case 'noRecords': return 'No records'; + case 'noOldRecords': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n, + zero: 'No records', + one: '${n} record', + two: '${n} records', + few: '${n} records', + many: '${n} records', + other: '${n} records', + ); case 'noRecord': return 'No record'; case 'botRecord': return 'Bots are not allowed to set records'; case 'anonRecord': return 'Guests are not allowed to set records'; @@ -1427,6 +1487,9 @@ extension on Translations { case 'comparingWith': return ({required Object newDate, required Object oldDate}) => 'Data from ${newDate} comparing with ${oldDate}'; case 'top': return 'Top'; case 'topRank': return 'Top Rank'; + case 'verdictGeneral': return ({required Object n, required Object verdict, required Object rank}) => '${n} ${verdict} than ${rank} rank average'; + case 'verdictBetter': return 'better'; + case 'verdictWorse': return 'worse'; case 'gamesUntilRanked': return ({required Object left}) => '${left} games until being ranked'; case 'nerdStats': return 'Nerd Stats'; case 'playersYouTrack': return 'Players you track'; @@ -1450,8 +1513,14 @@ extension on Translations { case 'yourIDAlertTitle': return 'Your nickname in TETR.IO'; case 'yourIDText': return 'When app loads, it will retrieve data for this account'; case 'language': return 'Language'; + case 'customization': return 'Customization'; + case 'customizationDescription': return 'There is only one toggle, planned to add more settings'; + case 'lbStats': return 'Show leaderboard based stats'; + case 'lbStatsDescription': return 'That will impact on loading times, but will allow you to see position on LB by stats and comparison with average values'; case 'aboutApp': return 'About app'; case 'aboutAppText': return ({required Object appName, required Object packageName, required Object version, required Object buildNumber}) => '${appName} (${packageName}) Version ${version} Build ${buildNumber}\n\nDeveloped by dan63047\nFormulas provided by kerrmunism\nHistory provided by p1nkl0bst3r\nTETR.IO replay grabber API by szy'; + case 'oskKagari': return 'Osk Kagari gimmick'; + case 'oskKagariDescription': return 'If on, osk\'s rank on main view will be rendered as :kagari:'; case 'stateViewTitle': return ({required Object nickname, required Object date}) => '${nickname} account on ${date}'; case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} states of ${nickname} account'; case 'matchesViewTitle': return ({required Object nickname}) => '${nickname} TL matches'; @@ -1619,21 +1688,30 @@ extension on Translations { case 'popupActions.ok': return 'OK'; 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.noSuchUserSub': return 'Either you mistyped something, or the account no longer exists'; + case 'errors.discordNotAssigned': return 'No user assigned to given Discord ID'; + case 'errors.discordNotAssignedSub': return 'Make sure you provided valid ID'; case 'errors.history': return 'History for that player is missing'; + case 'errors.actionSuggestion': return 'Perhaps, you want to'; case 'errors.p1nkl0bst3rTLmatches': return 'No Tetra League matches was found'; case 'errors.clientException': return 'No internet connection'; - case 'errors.forbidden': return 'Your IP address is blocked.\nChange IP address or reach out to osk'; - case 'errors.tooManyRequests': return 'You have been rate limited. Try again later'; + case 'errors.forbidden': return 'Your IP address is blocked'; + case 'errors.forbiddenSub': return ({required Object nickname}) => 'If you are using VPN or Proxy, turn it off. If this does not help, reach out to ${nickname}'; + case 'errors.tooManyRequests': return 'You have been rate limited.'; + case 'errors.tooManyRequestsSub': return 'Wait a few moments and try again'; case 'errors.internal': return 'Something happend on the tetr.io side'; + case 'errors.internalSub': return 'osk, probably, already aware about it'; case 'errors.internalWebVersion': return 'Something happend on the tetr.io side (or on oskware_bridge, idk honestly)'; - case 'errors.oskwareBridge': return 'Something happend with oskware_bridge. Let dan63047 know'; - case 'errors.p1nkl0bst3rForbidden': return 'Third party API blocked your IP address.\nChange IP address or reach out to p1nkl0bst3r'; + case 'errors.internalWebVersionSub': return 'If osk status page says that everything is ok, let dan63047 know about this issue'; + case 'errors.oskwareBridge': return 'Something happend with oskware_bridge'; + case 'errors.oskwareBridgeSub': return 'Let dan63047 know'; + case 'errors.p1nkl0bst3rForbidden': return 'Third party API blocked your IP address'; case 'errors.p1nkl0bst3rTooManyRequests': return 'Too many requests to third party API. Try again later'; case 'errors.p1nkl0bst3rinternal': return 'Something happend on the p1nkl0bst3r side'; case 'errors.p1nkl0bst3rinternalWebVersion': return 'Something happend on the p1nkl0bst3r side (or on oskware_bridge, idk honestly)'; case 'errors.replayAlreadySaved': return 'Replay already saved'; case 'errors.replayExpired': return 'Replay expired and not available anymore'; - case 'errors.replayRejected': return 'Third party API blocked your IP address.\nChange IP address or reach out to szy'; + case 'errors.replayRejected': return 'Third party API blocked your IP address'; case 'countries.': return 'Not selected'; case 'countries.AF': return 'Afghanistan'; case 'countries.AX': return 'Åland Islands'; @@ -1944,6 +2022,14 @@ extension on _StringsRu { case 'compare': return 'Сравнить'; case 'tlLeaderboard': return 'Рейтинговая таблица'; case 'noRecords': return 'Нет записей'; + case 'noOldRecords': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n, + zero: 'Нет записей', + one: 'Всего один матч', + two: 'Всего ${n} матча', + few: 'Всего ${n} матча', + many: 'Всего ${n} матчей', + other: '${n} матчей', + ); case 'noRecord': return 'Нет рекорда'; case 'botRecord': return 'Ботам нельзя ставить рекорды'; case 'anonRecord': return 'Гостям нельзя ставить рекорды'; @@ -1968,6 +2054,9 @@ extension on _StringsRu { case 'comparingWith': return ({required Object newDate, required Object oldDate}) => 'Данные от ${newDate} в сравнении с данными от ${oldDate}'; case 'top': return 'Топ'; case 'topRank': return 'Топ Ранг'; + case 'verdictGeneral': return ({required Object verdict, required Object rank, required Object n}) => '${verdict} среднего ${rank} ранга на ${n}'; + case 'verdictBetter': return 'Лучше'; + case 'verdictWorse': return 'Хуже'; case 'gamesUntilRanked': return ({required Object left}) => '${left} матчей до получения рейтинга'; case 'nerdStats': return 'Для задротов'; case 'playersYouTrack': return 'Отслеживаемые игроки'; @@ -1991,8 +2080,14 @@ extension on _StringsRu { case 'yourIDAlertTitle': return 'Ваш ник в TETR.IO'; case 'yourIDText': return 'При запуске приложения оно будет получать статистику этого игрока.'; case 'language': return 'Язык (Language)'; + case 'customization': return 'Кастомизация'; + case 'customizationDescription': return 'Здесь только один переключатель, в планах добавить больше'; + case 'lbStats': return 'Показывать статистику, основанную на рейтинговой таблице'; + case 'lbStatsDescription': return 'Это повлияет на время загрузки, но позволит видеть положение в рейтинге и сравнение со средними значениями по рангу по каждой стате'; case 'aboutApp': return 'О приложении'; case 'aboutAppText': return ({required Object appName, required Object packageName, required Object version, required Object buildNumber}) => '${appName} (${packageName}) Версия ${version} Сборка ${buildNumber}\n\nРазработал dan63047\nФормулы предоставил kerrmunism\nИсторию предоставляет p1nkl0bst3r\nВозможность скачивать повторы из TETR.IO предоставляет szy'; + case 'oskKagari': return '"Оск Кагари" прикол'; + case 'oskKagariDescription': return 'Если включено, вместо настоящего ранга оска будет рендерится :kagari:'; case 'stateViewTitle': return ({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}'; case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}'; case 'matchesViewTitle': return ({required Object nickname}) => 'Матчи аккаунта ${nickname}'; @@ -2141,7 +2236,7 @@ extension on _StringsRu { ); case 'numOfGameActions.tspinsTotal': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n, zero: '${n} T-спинов всего', - one: 'всего ${n} T-спин', + one: 'Всего ${n} T-спин', two: '${n} T-спина всего', few: '${n} T-спина всего', many: '${n} T-спинов всего', @@ -2160,21 +2255,30 @@ extension on _StringsRu { case 'popupActions.ok': return 'OK'; case 'errors.connection': return ({required Object code, required Object message}) => 'Проблема с подключением: ${code} ${message}'; case 'errors.noSuchUser': return 'Нет такого пользователя'; + case 'errors.noSuchUserSub': return 'Либо вы ошиблись при вводе, либо аккаунта больше не существует'; + case 'errors.discordNotAssigned': return 'К данному Discord ID не привязан аккаунт'; + case 'errors.discordNotAssignedSub': return 'Убедитесь в том, что вы вставили правильный ID'; case 'errors.history': return 'История данного игрока отсутствует'; + case 'errors.actionSuggestion': return 'Возможно, вы хотите'; case 'errors.p1nkl0bst3rTLmatches': return 'Старых матчей Тетра Лиги не было найдено'; case 'errors.clientException': return 'Нет соединения с интернетом'; - case 'errors.forbidden': return 'Ваш IP адрес заблокирован.\nСмените IP адрес или свяжитесь с osk-ом'; - case 'errors.tooManyRequests': return 'Слишком много запросов. Попробуйте позже'; + case 'errors.forbidden': return 'Ваш IP адрес заблокирован'; + case 'errors.forbiddenSub': return ({required Object nickname}) => 'Если у вас работает VPN или прокси, выключите его. Если это не помогло, свяжитесь с ${nickname}'; + case 'errors.tooManyRequests': return 'Слишком много запросов'; + case 'errors.tooManyRequestsSub': return 'Подождите немного и попробуйте снова'; case 'errors.internal': return 'Что-то случилось на стороне tetr.io'; + case 'errors.internalSub': return 'Скорее всего, osk уже в курсе об этом'; case 'errors.internalWebVersion': return 'Что-то случилось на стороне tetr.io (или на стороне oskware_bridge, я хз если честно)'; - case 'errors.oskwareBridge': return 'Что-то случилось с oskware_bridge. Дайте dan63047 знать'; - case 'errors.p1nkl0bst3rForbidden': return 'Стороннее API заблокировало ваш IP адрес.\nСмените IP адрес или свяжитесь с p1nkl0bst3r-ом'; + case 'errors.internalWebVersionSub': return 'Если статус страница osk-а говорит, что всё ок - свяжитесь с dan63047'; + case 'errors.oskwareBridge': return 'Что-то случилось с oskware_bridge'; + case 'errors.oskwareBridgeSub': return 'Дайте dan63047 знать'; + case 'errors.p1nkl0bst3rForbidden': return 'Стороннее API заблокировало ваш IP адрес'; case 'errors.p1nkl0bst3rTooManyRequests': return 'Слишком много запросов к стороннему API. Попробуйте позже'; case 'errors.p1nkl0bst3rinternal': return 'Что-то случилось на стороне p1nkl0bst3r-а'; case 'errors.p1nkl0bst3rinternalWebVersion': return 'Что-то случилось на стороне p1nkl0bst3r-а (или на стороне oskware_bridge, я хз если честно)'; case 'errors.replayAlreadySaved': return 'Повтор уже сохранён'; case 'errors.replayExpired': return 'Повтор истёк и больше недоступен'; - case 'errors.replayRejected': return 'Стороннее API заблокировало ваш IP адрес.\nСмените IP адрес или свяжитесь с szy'; + case 'errors.replayRejected': return 'Стороннее API заблокировало ваш IP адрес'; case 'countries.': return 'Не выбрана'; case 'countries.AF': return 'Афганистан'; case 'countries.AX': return 'Аландские острова'; diff --git a/lib/services/crud_exceptions.dart b/lib/services/crud_exceptions.dart index 303eaa0..3f34f91 100644 --- a/lib/services/crud_exceptions.dart +++ b/lib/services/crud_exceptions.dart @@ -14,6 +14,8 @@ class TetrioPlayerAlreadyExist implements Exception {} class TetrioPlayerNotExist implements Exception {} +class TetrioDiscordNotExist implements Exception {} + class TetrioHistoryNotExist implements Exception {} class TetrioTooManyRequests implements Exception {} diff --git a/lib/services/tetrio_crud.dart b/lib/services/tetrio_crud.dart index 4090a38..947ae12 100644 --- a/lib/services/tetrio_crud.dart +++ b/lib/services/tetrio_crud.dart @@ -974,7 +974,7 @@ class TetrioService extends DB { user = json['data']['user']['_id']; } else { // fail - throw an exception developer.log("fetchPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body); - throw TetrioPlayerNotExist(); + throw TetrioDiscordNotExist(); } break; // more exceptions to god of exceptions diff --git a/lib/utils/numers_formats.dart b/lib/utils/numers_formats.dart index af7bf36..3f626fc 100644 --- a/lib/utils/numers_formats.dart +++ b/lib/utils/numers_formats.dart @@ -6,4 +6,5 @@ final NumberFormat intf = NumberFormat.decimalPatternDigits(locale: LocaleSettin 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); -final NumberFormat f2l = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2)..minimumFractionDigits = 0; \ No newline at end of file +final NumberFormat f2l = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2)..minimumFractionDigits = 0; +final NumberFormat f0 = NumberFormat.decimalPattern(LocaleSettings.currentLocale.languageCode); \ No newline at end of file diff --git a/lib/views/calc_view.dart b/lib/views/calc_view.dart index 5c9334e..7762c13 100644 --- a/lib/views/calc_view.dart +++ b/lib/views/calc_view.dart @@ -69,7 +69,7 @@ class CalcState extends State { body: SafeArea( child: Center( child: Container( - constraints: BoxConstraints(maxWidth: 768), + constraints: const BoxConstraints(maxWidth: 768), child: NestedScrollView( controller: _scrollController, headerSliverBuilder: (context, value) { diff --git a/lib/views/compare_view.dart b/lib/views/compare_view.dart index c3755fb..8ac89d9 100644 --- a/lib/views/compare_view.dart +++ b/lib/views/compare_view.dart @@ -258,7 +258,7 @@ class CompareState extends State { body: SafeArea( child: Center( child: Container( - constraints: BoxConstraints(maxWidth: 768), + constraints: const BoxConstraints(maxWidth: 768), child: NestedScrollView( controller: _scrollController, headerSliverBuilder: (context, value) { @@ -327,7 +327,7 @@ class CompareState extends State { }, body: Center( child: Container( - constraints: BoxConstraints(maxWidth: 768), + constraints: const BoxConstraints(maxWidth: 768), child: ListView( children: !listEquals(theGreenSide, [null, null, null]) && !listEquals(theRedSide, [null, null, null])? [ if (theGreenSide[0] != null && diff --git a/lib/views/customization_view.dart b/lib/views/customization_view.dart index 71806ba..e7c22a4 100644 --- a/lib/views/customization_view.dart +++ b/lib/views/customization_view.dart @@ -20,6 +20,7 @@ class CustomizationView extends StatefulWidget { class CustomizationState extends State { late SharedPreferences prefs; + late bool oskKagariGimmick; void changeColor(Color color) { setState(() => pickerColor = color); @@ -31,7 +32,7 @@ class CustomizationState extends State { windowManager.getTitle().then((value) => oldWindowTitle = value); windowManager.setTitle("Tetra Stats: ${t.settings}"); } - _getPreferences(); + _getPreferences().then((value) => setState((){})); super.initState(); } @@ -43,6 +44,11 @@ class CustomizationState extends State { Future _getPreferences() async { prefs = await SharedPreferences.getInstance(); + if (prefs.getBool("oskKagariGimmick") != null) { + oskKagariGimmick = prefs.getBool("oskKagariGimmick")!; + } else { + oskKagariGimmick = true; + } } ThemeData getTheme(BuildContext context, Color color){ @@ -66,59 +72,48 @@ class CustomizationState extends State { body: SafeArea( child: ListView( children: [ - ListTile( - title: const Text("Accent Color"), - trailing: ColorIndicator(HSVColor.fromColor(Theme.of(context).colorScheme.primary)), - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) => AlertDialog( - title: const Text('Pick a color!'), - content: SingleChildScrollView( - child: ColorPicker( - pickerColor: pickerColor, - onColorChanged: changeColor, - ), - // Use Material color picker: - // - // child: MaterialPicker( - // pickerColor: pickerColor, - // onColorChanged: changeColor, - // showLabel: true, // only on portrait mode - // ), - // - // Use Block color picker: - // - // child: BlockPicker( - // pickerColor: currentColor, - // onColorChanged: changeColor, - // ), - // - // child: MultipleChoiceBlockPicker( - // pickerColors: currentColors, - // onColorsChanged: changeColors, - // ), - ), - actions: [ - ElevatedButton( - child: const Text('Got it'), - onPressed: () { - setState(() { - setAccentColor(pickerColor); - }); - Navigator.of(context).pop(); - }, - ), - ])); - }), - const ListTile( - title: Text("Font"), - subtitle: Text("Not implemented"), - ), - const ListTile( - title: Text("Stats Table in TL mathes list"), - subtitle: Text("Not implemented"), - ), + // ListTile( + // title: const Text("Accent color"), + // trailing: ColorIndicator(HSVColor.fromColor(Theme.of(context).colorScheme.primary)), + // onTap: () { + // showDialog( + // context: context, + // builder: (BuildContext context) => AlertDialog( + // title: const Text('Pick an accent color'), + // content: SingleChildScrollView( + // child: ColorPicker( + // pickerColor: pickerColor, + // onColorChanged: changeColor, + // ), + // ), + // actions: [ + // ElevatedButton( + // child: const Text('Set'), + // onPressed: () { + // setState(() { + // setAccentColor(pickerColor); + // }); + // Navigator.of(context).pop(); + // }, + // ), + // ])); + // }), + // const ListTile( + // title: Text("Font"), + // subtitle: Text("Not implemented"), + // ), + // const ListTile( + // title: Text("Stats Table in TL mathes list"), + // subtitle: Text("Not implemented"), + // ), + ListTile(title: Text(t.oskKagari), + subtitle: Text(t.oskKagariDescription), + trailing: Switch(value: oskKagariGimmick, onChanged: (bool value){ + prefs.setBool("oskKagariGimmick", value); + setState(() { + oskKagariGimmick = value; + }); + }),) ], )), ); diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index ec0c77f..fb890d6 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -34,6 +34,7 @@ import 'package:go_router/go_router.dart'; final TetrioService teto = TetrioService(); // thing, that manadge our local DB int _chartsIndex = 0; +bool _showHistoryAsTable = false; List _historyShortTitles = ["TR", "Glicko", "RD", "APM", "PPS", "VS", "APP", "DS/S", "DS/P", "APP + DS/P", "VS/APM", "Cheese", "GbE", "wAPP", "Area", "eTR", "±eTR", "Opener", "Plonk", "Inf. DS", "Stride"]; late ScrollController _scrollController; final NumberFormat _timeInSec = NumberFormat("#,###.###s.", LocaleSettings.currentLocale.languageCode); @@ -84,8 +85,10 @@ class _MainState extends State with TickerProviderStateMixin { String _titleNickname = "dan63047"; /// Each dropdown menu item contains list of dots for the graph var chartsData = >>[]; + //var tableData = []; final bodyGlobalKey = GlobalKey(); bool _showSearchBar = false; + bool _TLHistoryWasFetched = false; late TabController _tabController; late TabController _wideScreenTabController; late bool fixedScroll; @@ -143,6 +146,7 @@ class _MainState extends State with TickerProviderStateMixin { /// If at least one request to Tetra Channel API fails, whole function will throw an exception. Future fetch(String nickOrID, {bool fetchHistory = false, bool fetchTLmatches = false}) async { TetrioPlayer me; + _TLHistoryWasFetched = false; // If user trying to search with discord id if (nickOrID.startsWith("ds:")){ @@ -211,8 +215,11 @@ class _MainState extends State with TickerProviderStateMixin { if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.errors.p1nkl0bst3rinternal))); }on P1nkl0bst3rTooManyRequests{ if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.errors.p1nkl0bst3rTooManyRequests))); + }finally{ + _TLHistoryWasFetched = true; } - } + } + if (storedRecords.isNotEmpty) _TLHistoryWasFetched = true; for (var match in storedRecords) { // add stored match to list only if it missing from retrived ones if (!tlMatches.contains(match)) tlMatches.add(match); @@ -246,6 +253,11 @@ class _MainState extends State with TickerProviderStateMixin { if (uniqueTL.isEmpty) uniqueTL.add(element.tlSeason1); } // Also i need previous Tetra League State for comparison if avaliable + // tableData = [ + // TableRow(children: [ Text("Date & Time"), Text("Tr"), Text("Glicko"), Text("RD"), Text("GP"), Text("GW"), Text("APM"), Text("PPS"), Text("VS"), Text("APP"), Text("VS/APM"), Text("DS/S"), Text("DS/P"), Text("APP+DS/P"), Text("Cheese"), Text("GbE"), Text("wAPP"), Text("Area"), Text("eTR"), Text("±eTR"), Text("Opener"), Text("Plonk"), Text("Inf. DS"), Text("Stride")], + // decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Colors.white)))), + // for (var state in states) TableRow(children: [Text(dateFormat.format(state.tlSeason1.timestamp)), Text(f4.format(state.tlSeason1.rating)), Text(f4.format(state.tlSeason1.glicko)), Text(f4.format(state.tlSeason1.rd)), Text(f0.format(state.tlSeason1.gamesPlayed)), Text(f0.format(state.tlSeason1.gamesWon)), Text(f2.format(state.tlSeason1.apm)), Text(f2.format(state.tlSeason1.pps)), Text(state.tlSeason1.vs != null ? f2.format(state.tlSeason1.vs) : "---"), Text(state.tlSeason1.nerdStats != null ? f4.format(state.tlSeason1.nerdStats?.app) : "---"), Text(state.tlSeason1.nerdStats != null ? f4.format(state.tlSeason1.nerdStats?.vsapm) : "---"), Text(state.tlSeason1.nerdStats != null ? f4.format(state.tlSeason1.nerdStats?.dss) : "---"), Text(state.tlSeason1.nerdStats != null ? f4.format(state.tlSeason1.nerdStats?.dsp) : "---"), Text(state.tlSeason1.nerdStats != null ? f4.format(state.tlSeason1.nerdStats?.appdsp) : "---"), Text(state.tlSeason1.nerdStats != null ? f4.format(state.tlSeason1.nerdStats?.cheese) : "---"), Text(state.tlSeason1.nerdStats != null ? f4.format(state.tlSeason1.nerdStats?.gbe) : "---"), Text(state.tlSeason1.nerdStats != null ? f4.format(state.tlSeason1.nerdStats?.nyaapp) : "---"), Text(state.tlSeason1.nerdStats != null ? f4.format(state.tlSeason1.nerdStats?.area) : "---"), Text(state.tlSeason1.estTr != null ? f4.format(state.tlSeason1.estTr?.esttr) : "---"), Text(state.tlSeason1.esttracc != null ? f4.format(state.tlSeason1.esttracc) : "---"), Text(state.tlSeason1.playstyle != null ? f4.format(state.tlSeason1.playstyle?.opener) : "---"), Text(state.tlSeason1.playstyle != null ? f4.format(state.tlSeason1.playstyle?.plonk) : "---"), Text(state.tlSeason1.playstyle != null ? f4.format(state.tlSeason1.playstyle?.infds) : "---"), Text(state.tlSeason1.playstyle != null ? f4.format(state.tlSeason1.playstyle?.stride) : "---")]), + // ]; if (uniqueTL.length >= 2){ compareWith = uniqueTL.toList().elementAtOrNull(uniqueTL.length - 2); chartsData = >>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid @@ -381,9 +393,9 @@ class _MainState extends State with TickerProviderStateMixin { return notification.depth == 0; }, child: NestedScrollView( + scrollBehavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), physics: const AlwaysScrollableScrollPhysics(), controller: _scrollController, - scrollBehavior: const MaterialScrollBehavior(), headerSliverBuilder: (context, value) { return [ SliverToBoxAdapter( @@ -422,7 +434,7 @@ class _MainState extends State with TickerProviderStateMixin { children: [ Container( width: MediaQuery.of(context).size.width-450, - constraints: BoxConstraints(maxWidth: 1024), + constraints: const BoxConstraints(maxWidth: 1024), child: TLThingy( tl: snapshot.data![0].tlSeason1, userID: snapshot.data![0].userId, @@ -436,23 +448,11 @@ class _MainState extends State with TickerProviderStateMixin { ), SizedBox( width: 450, - child: _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0) + child: _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, oldMathcesHere: _TLHistoryWasFetched) ), ],), - _History(chartsData: chartsData, states: snapshot.data![2], changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0), + _History(chartsData: chartsData, 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( - // width: MediaQuery.of(context).size.width/2, - // padding: EdgeInsets.only(right: 8), - // child: _RecordThingy(record: snapshot.data![1]['sprint'], rank: snapshot.data![0].tlSeason1.percentileRank) - // ), - // Container( - // width: MediaQuery.of(context).size.width/2, - // padding: EdgeInsets.only(left: 8), - // child: _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],) ] : [ TLThingy( @@ -465,8 +465,8 @@ class _MainState extends State with TickerProviderStateMixin { averages: rankAverages, lbPositions: meAmongEveryone ), - _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], 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), + _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, oldMathcesHere: _TLHistoryWasFetched), + _History(chartsData: chartsData, 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],) @@ -476,28 +476,34 @@ class _MainState extends State with TickerProviderStateMixin { ); } else if (snapshot.hasError) { String errText = ""; + String? subText; switch (snapshot.error.runtimeType){ case TetrioPlayerNotExist: errText = t.errors.noSuchUser; + subText = t.errors.noSuchUserSub; break; + case TetrioDiscordNotExist: + errText = t.errors.discordNotAssigned; + subText = t.errors.discordNotAssignedSub; case ConnectionIssue: var err = snapshot.error as ConnectionIssue; errText = t.errors.connection(code: err.code, message: err.message); break; - case TetrioHistoryNotExist: - errText = t.errors.history; - break; case TetrioForbidden: errText = t.errors.forbidden; + subText = t.errors.forbiddenSub(nickname: 'osk'); break; case TetrioTooManyRequests: errText = t.errors.tooManyRequests; + subText = t.errors.tooManyRequestsSub; break; case TetrioOskwareBridgeProblem: errText = t.errors.oskwareBridge; + subText = t.errors.oskwareBridgeSub; break; case TetrioInternalProblem: errText = kIsWeb ? t.errors.internalWebVersion : t.errors.internal; + subText = kIsWeb ? t.errors.internalWebVersionSub : t.errors.internalSub; break; case ClientException: errText = t.errors.clientException; @@ -505,7 +511,18 @@ class _MainState extends State with TickerProviderStateMixin { default: errText = snapshot.error.toString(); } - return Center(child: Text(errText, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 42, fontWeight: FontWeight.bold), textAlign: TextAlign.center)); + return Center(child: + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(errText, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 42, fontWeight: FontWeight.bold), textAlign: TextAlign.center), + if (subText != null) Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text(subText, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 18)), + ), + ], + ) + ); } break; } @@ -646,27 +663,42 @@ class _TLRecords extends StatelessWidget { final Function changePlayer; final List data; final bool wasActiveInTL; + final bool oldMathcesHere; /// Widget, that displays Tetra League records. /// Accepts list of TL records ([data]) and [userID] of player from the view - const _TLRecords({required this.userID, required this.changePlayer, required this.data, required this.wasActiveInTL}); + const _TLRecords({required this.userID, required this.changePlayer, required this.data, required this.wasActiveInTL, required this.oldMathcesHere}); @override Widget build(BuildContext context) { - if (data.isEmpty) return Center(child: Column( + if (data.isEmpty) { + return Center(child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(t.noRecords, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)), - if (wasActiveInTL) Text("Perhaps, you want to"), + if (wasActiveInTL) Text(t.errors.actionSuggestion), if (wasActiveInTL) TextButton(onPressed: (){changePlayer(userID, fetchTLmatches: true);}, child: Text(t.fetchAndSaveOldTLmatches)) ], )); + } bool bigScreen = MediaQuery.of(context).size.width >= 768; + int length = data.length; return ListView.builder( physics: const AlwaysScrollableScrollPhysics(), controller: ScrollController(), - itemCount: data.length, + itemCount: oldMathcesHere ? length : length + 1, itemBuilder: (BuildContext context, int index) { + if (index == length) { + return Center(child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(t.noOldRecords(n: length), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)), + if (wasActiveInTL) Text(t.errors.actionSuggestion), + if (wasActiveInTL) TextButton(onPressed: (){changePlayer(userID, fetchTLmatches: true);}, child: Text(t.fetchAndSaveOldTLmatches)) + ], + )); + } + var accentColor = data[index].endContext.firstWhere((element) => element.userId == userID).success ? Colors.green : Colors.red; return Container( decoration: BoxDecoration( @@ -698,7 +730,6 @@ class _TLRecords extends StatelessWidget { class _History extends StatelessWidget{ final List>> chartsData; - final List states; final String userID; final Function update; final Function changePlayer; @@ -706,41 +737,67 @@ 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.states, required this.userID, required this.changePlayer, required this.update, required this.wasActiveInTL}); + const _History({required this.chartsData, required this.userID, required this.changePlayer, required this.update, required this.wasActiveInTL}); @override Widget build(BuildContext context) { - bool bigScreen = MediaQuery.of(context).size.width > 768; - return chartsData.isNotEmpty ? - Column( - children: [ - DropdownButton( - items: chartsData, - value: chartsData[_chartsIndex].value, - onChanged: (value) { - _chartsIndex = chartsData.indexWhere((element) => element.value == value); - update(); - } - ), - if(chartsData[_chartsIndex].value!.length > 1) _HistoryChartThigy(data: chartsData[_chartsIndex].value!, yAxisTitle: _historyShortTitles[_chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(),) - else Center(child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text(t.notEnoughData, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)), - if (wasActiveInTL) Text("Perhaps, you want"), - if (wasActiveInTL)TextButton(onPressed: (){changePlayer(userID, fetchHistory: true);}, child: Text(t.fetchAndsaveTLHistory)) - ], - )) - ], - ) - : Center(child: Column( + if (chartsData.isEmpty) { + return Center(child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(t.noHistorySaved, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)), - if (wasActiveInTL) Text("Perhaps, you want"), - if (wasActiveInTL)TextButton(onPressed: (){changePlayer(userID, fetchHistory: true);}, child: Text(t.fetchAndsaveTLHistory)) + if (wasActiveInTL) Text(t.errors.actionSuggestion), + if (wasActiveInTL) TextButton(onPressed: (){changePlayer(userID, fetchHistory: true);}, child: Text(t.fetchAndsaveTLHistory)) ], )); + } + bool bigScreen = MediaQuery.of(context).size.width > 768; + return SingleChildScrollView( + scrollDirection: Axis.vertical, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + primary: true, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Wrap( + spacing: 20, + children: [ + // DropdownButton( + // items: [DropdownMenuItem(child: Text("Chart"), value: false), DropdownMenuItem(child: Text("Table"), value: true)], + // value: _showHistoryAsTable, + // onChanged: (value) { + // _showHistoryAsTable = value!; + // update(); + // } + // ), + DropdownButton( + items: chartsData, + value: chartsData[_chartsIndex].value, + onChanged: (value) { + _chartsIndex = chartsData.indexWhere((element) => element.value == value); + update(); + } + ), + ], + ), + if(chartsData[_chartsIndex].value!.length > 1 && !_showHistoryAsTable) _HistoryChartThigy(data: chartsData[_chartsIndex].value!, yAxisTitle: _historyShortTitles[_chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(),) + else if (chartsData[_chartsIndex].value!.length <= 1 && !_showHistoryAsTable) Center(child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(t.notEnoughData, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)), + if (wasActiveInTL) Text(t.errors.actionSuggestion), + if (wasActiveInTL) TextButton(onPressed: (){changePlayer(userID, fetchHistory: true);}, child: Text(t.fetchAndsaveTLHistory)) + ], + )) + // else if (_showHistoryAsTable) Padding( + // padding: const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 0.0), + // child: _HistoryTableThingy(tableData), + // ) + ], + ), + ), + ); } } @@ -1020,22 +1077,28 @@ class _HistoryChartThigyState extends State<_HistoryChartThigy> { } } -class _HistoryTableThingy extends StatelessWidget{ - final List states; +// class _HistoryTableThingy extends StatelessWidget{ +// final List tableData; - 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")]), - ], - )); - }); - } -} +// const _HistoryTableThingy(this.tableData); +// // :tf: +// @override +// Widget build(BuildContext context) { +// return LayoutBuilder(builder: (context, constraints){ +// return Table( +// defaultColumnWidth: FixedColumnWidth(75), +// columnWidths: { +// 0: FixedColumnWidth(170), +// 1: FixedColumnWidth(100), +// 2: FixedColumnWidth(90), +// 18: FixedColumnWidth(100), +// 19: FixedColumnWidth(90), +// }, +// children: tableData, +// ); +// }); +// } +// } class _TwoRecordsThingy extends StatelessWidget { final RecordSingle? sprint; @@ -1082,13 +1145,13 @@ class _TwoRecordsThingy extends StatelessWidget { Row( mainAxisSize: MainAxisSize.min, children: [ - Padding(padding: EdgeInsets.only(right: 8.0), + Padding(padding: const EdgeInsets.only(right: 8.0), child: sprint != null ? Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageSprint.key}.png", height: 96) : Image.asset("res/tetrio_tl_alpha_ranks/z.png", height: 96) ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(t.sprint, style: TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)), + Text(t.sprint, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)), RichText(text: TextSpan( text: sprint != null ? get40lTime(sprint!.endContext!.finalTime.inMicroseconds) : "---", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500, color: sprint != null ? Colors.white : Colors.grey), @@ -1099,11 +1162,11 @@ class _TwoRecordsThingy extends StatelessWidget { text: "", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey), 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( + if (rank != null && rank != "z") TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(sprint!.endContext!.finalTime, sprintAverages[rank]!), verdict: sprintBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\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 + else TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(sprint!.endContext!.finalTime, closestAverageSprint.value), verdict: sprintBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageSprint.key.toUpperCase())}\n", style: TextStyle( + color: sprintBetterThanClosestAverage ? 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: " • "), @@ -1157,10 +1220,10 @@ class _TwoRecordsThingy extends StatelessWidget { text: "", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey), 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( + if (rank != null && rank != "z") TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(blitz!.endContext!.score, blitzAverages[rank]!), verdict: blitzBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\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( + else TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(blitz!.endContext!.score, closestAverageBlitz.value), verdict: blitzBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageBlitz.key.toUpperCase())}\n", style: TextStyle( color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent )), TextSpan(text: _dateFormat.format(blitz!.timestamp!)), @@ -1170,7 +1233,7 @@ class _TwoRecordsThingy extends StatelessWidget { ), ), ],), - Padding(padding: EdgeInsets.only(left: 8.0), + Padding(padding: const EdgeInsets.only(left: 8.0), child: blitz != null ? Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageBlitz.key}.png", height: 96) : Image.asset("res/tetrio_tl_alpha_ranks/z.png", height: 96)), ], ), @@ -1240,17 +1303,17 @@ class _RecordThingy extends StatelessWidget { Row( mainAxisSize: MainAxisSize.min, children: [ - if (record!.stream.contains("40l")) Padding(padding: EdgeInsets.only(right: 8.0), + if (record!.stream.contains("40l")) Padding(padding: const 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), + if (record!.stream.contains("blitz")) Padding(padding: const 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)), + if (record!.stream.contains("40l")) Text(t.sprint, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)), + if (record!.stream.contains("blitz")) Text(t.blitz, style: const 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), @@ -1260,16 +1323,16 @@ class _RecordThingy extends StatelessWidget { 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( + if (record!.stream.contains("40l") && (rank != null && rank != "z")) TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(record!.endContext!.finalTime, sprintAverages[rank]!), verdict: sprintBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\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("40l") && (rank == null || rank == "z")) TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(record!.endContext!.finalTime, closestAverageSprint.value), verdict: sprintBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageSprint.key.toUpperCase())}\n", style: TextStyle( + color: sprintBetterThanClosestAverage ? 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( + else if (record!.stream.contains("blitz") && (rank != null && rank != "z")) TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(record!.endContext!.score, blitzAverages[rank]!), verdict: blitzBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\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( + else if (record!.stream.contains("blitz") && (rank == null || rank == "z")) TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(record!.endContext!.score, closestAverageBlitz.value), verdict: blitzBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageBlitz.key.toUpperCase())}\n", style: TextStyle( color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent )), if (record!.rank != null) TextSpan(text: "№${record!.rank}", style: TextStyle(color: getColorOfRank(record!.rank!))), @@ -1566,7 +1629,7 @@ class _OtherThingy extends StatelessWidget { physics: const AlwaysScrollableScrollPhysics(), itemCount: newsletter!.length+1, itemBuilder: (BuildContext context, int index) { - return index == 0 ? Center(child: Text(t.news, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42))) : getNewsTile(newsletter![index-1]); + return index == 0 ? Center(child: Text(t.news, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42))) : getNewsTile(newsletter![index-1]); } )) ] diff --git a/lib/views/settings_view.dart b/lib/views/settings_view.dart index 3aa1c11..979293e 100644 --- a/lib/views/settings_view.dart +++ b/lib/views/settings_view.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'package:go_router/go_router.dart'; import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/main.dart' show packageInfo; import 'package:file_selector/file_selector.dart'; @@ -260,14 +261,14 @@ class SettingsState extends State { }, ), ), - ListTile(title: const Text("Customization"), - subtitle: const Text("I don't want to implement this"), + ListTile(title: Text(t.customization), + subtitle: Text(t.customizationDescription), trailing: const Icon(Icons.arrow_right), onTap: () { - Navigator.pushNamed(context, "/customization"); + context.go("/customization"); },), - ListTile(title: Text("Show leaderboard based stats"), - subtitle: Text("That will impact on loading times, but will allow you to see position on LB by stats and comparison with average values"), + ListTile(title: Text(t.lbStats), + subtitle: Text(t.lbStatsDescription), trailing: Switch(value: showPositions, onChanged: (bool value){ prefs.setBool("showPositions", value); setState(() { @@ -281,6 +282,7 @@ class SettingsState extends State { }, title: Text(t.aboutApp), subtitle: Text(t.aboutAppText(appName: packageInfo.appName, packageName: packageInfo.packageName, version: packageInfo.version, buildNumber: packageInfo.buildNumber)), + trailing: const Icon(Icons.arrow_right) ), ], )), diff --git a/lib/widgets/gauget_num.dart b/lib/widgets/gauget_num.dart index ef83515..b33362d 100644 --- a/lib/widgets/gauget_num.dart +++ b/lib/widgets/gauget_num.dart @@ -98,7 +98,7 @@ class GaugetNum extends StatelessWidget { oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent ),), if ((oldTl != null && oldTl!.gamesPlayed > 0) && pos != null) const TextSpan(text: " • "), - if (pos != null) TextSpan(text: pos!.position >= 1000 ? "Top ${f2.format(pos!.percentage*100)}%" : "№${pos!.position}") + if (pos != null) TextSpan(text: pos!.position >= 1000 ? "${t.top} ${f2.format(pos!.percentage*100)}%" : "№${pos!.position}") ] ), ), diff --git a/lib/widgets/lineclears_thingy.dart b/lib/widgets/lineclears_thingy.dart index 303b607..35b8d3f 100644 --- a/lib/widgets/lineclears_thingy.dart +++ b/lib/widgets/lineclears_thingy.dart @@ -20,11 +20,11 @@ class LineclearsThingy extends StatelessWidget{ 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())]), + Text(t.numOfGameActions.lineClears(n: lines), style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Quads"), Text(clears.quads.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Triples"), Text(clears.triples.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Doubles"), Text(clears.doubles.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const 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())]), ], @@ -35,14 +35,14 @@ class LineclearsThingy extends StatelessWidget{ 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())]), + Text(t.numOfGameActions.tspinsTotal(n: tSpins), style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins triples"), Text(clears.tSpinTriples.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins doubles"), Text(clears.tSpinDoubles.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins singles"), Text(clears.tSpinSingles.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins zeros"), Text(clears.tSpinZeros.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins doubles"), Text(clears.tSpinMiniDoubles.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins singles"), Text(clears.tSpinMiniSingles.toString())]), + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins zeros"), Text(clears.tSpinMiniZeros.toString())]), ], ), ), diff --git a/lib/widgets/stat_sell_num.dart b/lib/widgets/stat_sell_num.dart index 06b215d..8ea21a6 100644 --- a/lib/widgets/stat_sell_num.dart +++ b/lib/widgets/stat_sell_num.dart @@ -70,7 +70,7 @@ class StatCellNum extends StatelessWidget { oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent ),), if (oldPlayerStat != null && pos != null) const TextSpan(text: " • "), - if (pos != null) TextSpan(text: pos!.position >= 1000 ? "Top ${f2.format(pos!.percentage*100)}%" : "№${pos!.position}") + if (pos != null) TextSpan(text: pos!.position >= 1000 ? "${t.top} ${f2.format(pos!.percentage*100)}%" : "№${pos!.position}") ] ), ), diff --git a/lib/widgets/tl_thingy.dart b/lib/widgets/tl_thingy.dart index 5b04061..fbcda33 100644 --- a/lib/widgets/tl_thingy.dart +++ b/lib/widgets/tl_thingy.dart @@ -3,8 +3,8 @@ import 'package:intl/intl.dart'; import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:syncfusion_flutter_gauges/gauges.dart'; import 'package:tetra_stats/gen/strings.g.dart'; +import 'package:tetra_stats/main.dart'; import 'package:tetra_stats/utils/numers_formats.dart'; -import 'package:tetra_stats/views/main_view.dart'; import 'package:tetra_stats/widgets/gauget_num.dart'; import 'package:tetra_stats/widgets/graphs.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart'; @@ -38,11 +38,13 @@ class TLThingy extends StatefulWidget { } class _TLThingyState extends State { + late bool oskKagariGimmick; @override void initState() { _currentRangeValues = const RangeValues(0, 1); sortedStates = widget.states.reversed.toList(); + oskKagariGimmick = prefs.getBool("oskKagariGimmick")??true; try{ oldTl = sortedStates[1].tlSeason1; }on RangeError{ @@ -97,7 +99,7 @@ class _TLThingyState extends State { crossAxisAlignment: WrapCrossAlignment.center, clipBehavior: Clip.hardEdge, children: [ - widget.userID == "5e32fc85ab319c2ab1beb07c" // he love her so much, you can't even imagine + (widget.userID == "5e32fc85ab319c2ab1beb07c" && oskKagariGimmick) // he love her so much, you can't even imagine ? Image.asset("res/icons/kagari.png", height: 128) // Btw why she wearing Kazamatsuri high school uniform? : Image.asset("res/tetrio_tl_alpha_ranks/${currentTl.rank}.png", height: 128), Column( @@ -292,27 +294,28 @@ class _TLThingyState extends State { ), if (currentTl.estTr != null) Padding( - padding: const EdgeInsets.fromLTRB(0, 48, 0, 48), + padding: const EdgeInsets.fromLTRB(0, 20, 0, 20), child: Container( //alignment: Alignment.center, width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85, + height: 70, constraints: BoxConstraints(maxWidth: 768), - child: Wrap( - alignment: WrapAlignment.spaceBetween, - spacing: 20, + child: Stack( children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(t.statCellNum.estOfTR, style: TextStyle(height: 0.1),), - RichText( - text: TextSpan( - text: intf.format(currentTl.estTr!.esttr.truncate()), - 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))] + Positioned( + left: 0, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(t.statCellNum.estOfTR, style: TextStyle(height: 0.1),), + RichText( + text: TextSpan( + text: intf.format(currentTl.estTr!.esttr.truncate()), + style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 36 : 30, 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))] + ), ), - ), - if (oldTl?.estTr?.esttr != null || widget.lbPositions != null) RichText(text: TextSpan( + RichText(text: TextSpan( text: "", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey, height: 0.5), children: [ @@ -320,78 +323,46 @@ class _TLThingyState extends State { color: oldTl!.estTr!.esttr > currentTl.estTr!.esttr ? Colors.redAccent : Colors.greenAccent ),), if (oldTl?.estTr?.esttr != null && widget.lbPositions?.estTr != null) const TextSpan(text: " • "), - if (widget.lbPositions?.estTr != null) TextSpan(text: widget.lbPositions!.estTr!.position >= 1000 ? "Top ${f2.format(widget.lbPositions!.estTr!.percentage*100)}%" : "№${widget.lbPositions!.estTr!.position}"), + if (widget.lbPositions?.estTr != null) TextSpan(text: widget.lbPositions!.estTr!.position >= 1000 ? "${t.top} ${f2.format(widget.lbPositions!.estTr!.percentage*100)}%" : "№${widget.lbPositions!.estTr!.position}"), if (widget.lbPositions?.estTr != null) const TextSpan(text: " • "), TextSpan(text: "Glicko: ${f2.format(currentTl.estTr!.estglicko)}") ] ), ), - ],), - Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text(t.statCellNum.accOfEst, style: const TextStyle(height: 0.1),), - 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, color: Colors.white), + ],), + ), + Positioned( + right: 0, + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text(t.statCellNum.accOfEst, style: const TextStyle(height: 0.1),), + RichText( + text: TextSpan( + text: (currentTl.esttracc != null && currentTl.bestRank != "z") ? intFDiff.format(currentTl.esttracc!.truncate()) : "---", + style: TextStyle(fontFamily: "Eurostile Round", fontSize: bigScreen ? 36 : 30, 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)) + ] + ), + ), + if ((oldTl?.esttracc != null || widget.lbPositions != null) && currentTl.bestRank != "z") RichText(text: TextSpan( + text: "", + style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey, height: 0.5), 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)) + if (oldTl?.esttracc != null) TextSpan(text: comparef.format(currentTl.esttracc! - oldTl!.esttracc!), style: TextStyle( + color: oldTl!.esttracc! > currentTl.esttracc! ? Colors.redAccent : Colors.greenAccent + ),), + if (oldTl?.esttracc != null && widget.lbPositions?.accOfEst != null) const TextSpan(text: " • "), + if (widget.lbPositions?.accOfEst != null) TextSpan(text: widget.lbPositions!.accOfEst!.position >= 1000 ? "${t.top} ${f2.format(widget.lbPositions!.accOfEst!.percentage*100)}%" : "№${widget.lbPositions!.accOfEst!.position}") ] ), ), - if ((oldTl?.esttracc != null || widget.lbPositions != null) && currentTl.bestRank != "z") RichText(text: TextSpan( - text: "", - style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey, height: 0.5), - children: [ - if (oldTl?.esttracc != null) TextSpan(text: comparef.format(currentTl.esttracc! - oldTl!.esttracc!), style: TextStyle( - color: oldTl!.esttracc! > currentTl.esttracc! ? Colors.redAccent : Colors.greenAccent - ),), - if (oldTl?.esttracc != null && widget.lbPositions?.accOfEst != null) const TextSpan(text: " • "), - if (widget.lbPositions?.accOfEst != null) TextSpan(text: widget.lbPositions!.accOfEst!.position >= 1000 ? "Top ${f2.format(widget.lbPositions!.accOfEst!.percentage*100)}%" : "№${widget.lbPositions!.accOfEst!.position}") - ] - ), - ), - ],) + ],), + ) ], ), ) - // 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( - // "${bigScreen ? t.statCellNum.estOfTR : t.statCellNum.estOfTRShort}:", - // style: const TextStyle(fontSize: 24), - // ), - // Text( - // f2.format(currentTl.estTr!.esttr), - // style: const TextStyle(fontSize: 24), - // ), - // ], - // ), - // if (currentTl.rating >= 0) - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Text( - // "${bigScreen ? t.statCellNum.accOfEst : t.statCellNum.accOfEstShort}:", - // style: const TextStyle(fontSize: 24), - // ), - // Text( - // fDiff.format(currentTl.esttracc!), - // style: const TextStyle(fontSize: 24), - // ), - // ], - // ), - // ], - // ), - // ), ), if (currentTl.nerdStats != null) Graphs(currentTl.apm!, currentTl.pps!, currentTl.vs!, currentTl.nerdStats!, currentTl.playstyle!) ] diff --git a/lib/widgets/user_thingy.dart b/lib/widgets/user_thingy.dart index d971f7e..653b33e 100644 --- a/lib/widgets/user_thingy.dart +++ b/lib/widgets/user_thingy.dart @@ -91,6 +91,7 @@ class UserThingy extends StatelessWidget { ? Image.asset("res/avatars/tetrio_banned.png", fit: BoxFit.fitHeight, height: pfpHeight,) : player.avatarRevision != null ? Image.network("https://tetr.io/user-content/avatars/${player.userId}.jpg?rv=${player.avatarRevision}", + // TODO: osk banner can cause memory leak fit: BoxFit.fitHeight, height: 128, errorBuilder: (context, error, stackTrace) { developer.log("Error with building profile picture", name: "main_view", error: error, stackTrace: stackTrace); return Image.asset("res/avatars/tetrio_anon.png", fit: BoxFit.fitHeight, height: pfpHeight); diff --git a/res/i18n/strings.i18n.json b/res/i18n/strings.i18n.json index 79ec395..63ec0f8 100644 --- a/res/i18n/strings.i18n.json +++ b/res/i18n/strings.i18n.json @@ -46,6 +46,14 @@ "stoppedBeingTracked": "Removed from tracking list!", "tlLeaderboard": "Tetra League leaderboard", "noRecords": "No records", + "noOldRecords": { + "zero": "No records", + "one": "$n record", + "two": "$n records", + "few": "$n records", + "many": "$n records", + "other": "$n records" + }, "noRecord": "No record", "botRecord": "Bots are not allowed to set records", "anonRecord": "Guests are not allowed to set records", @@ -70,6 +78,9 @@ "comparingWith": "Data from ${newDate} comparing with ${oldDate}", "top": "Top", "topRank": "Top Rank", + "verdictGeneral": "$n $verdict than $rank rank average", + "verdictBetter": "better", + "verdictWorse": "worse", "gamesUntilRanked": "${left} games until being ranked", "nerdStats": "Nerd Stats", "playersYouTrack": "Players you track", @@ -93,8 +104,14 @@ "yourIDAlertTitle": "Your nickname in TETR.IO", "yourIDText": "When app loads, it will retrieve data for this account", "language": "Language", + "customization": "Customization", + "customizationDescription": "There is only one toggle, planned to add more settings", + "lbStats": "Show leaderboard based stats", + "lbStatsDescription": "That will impact on loading times, but will allow you to see position on LB by stats and comparison with average values", "aboutApp": "About app", "aboutAppText": "${appName} (${packageName}) Version ${version} Build ${buildNumber}\n\nDeveloped by dan63047\nFormulas provided by kerrmunism\nHistory provided by p1nkl0bst3r\nTETR.IO replay grabber API by szy", + "oskKagari": "Osk Kagari gimmick", + "oskKagariDescription": "If on, osk's rank on main view will be rendered as :kagari:", "stateViewTitle": "${nickname} account on ${date}", "statesViewTitle": "${number} states of ${nickname} account", "matchesViewTitle": "${nickname} TL matches", @@ -271,21 +288,30 @@ "errors":{ "connection": "Some issue with connection: ${code} ${message}", "noSuchUser": "No such user", + "noSuchUserSub": "Either you mistyped something, or the account no longer exists", + "discordNotAssigned": "No user assigned to given Discord ID", + "discordNotAssignedSub": "Make sure you provided valid ID", "history": "History for that player is missing", + "actionSuggestion": "Perhaps, you want to", "p1nkl0bst3rTLmatches": "No Tetra League matches was found", "clientException": "No internet connection", - "forbidden": "Your IP address is blocked.\nChange IP address or reach out to osk", - "tooManyRequests": "You have been rate limited. Try again later", + "forbidden": "Your IP address is blocked", + "forbiddenSub": "If you are using VPN or Proxy, turn it off. If this does not help, reach out to $nickname", + "tooManyRequests": "You have been rate limited.", + "tooManyRequestsSub": "Wait a few moments and try again", "internal": "Something happend on the tetr.io side", + "internalSub": "osk, probably, already aware about it", "internalWebVersion": "Something happend on the tetr.io side (or on oskware_bridge, idk honestly)", - "oskwareBridge": "Something happend with oskware_bridge. Let dan63047 know", - "p1nkl0bst3rForbidden": "Third party API blocked your IP address.\nChange IP address or reach out to p1nkl0bst3r", + "internalWebVersionSub": "If osk status page says that everything is ok, let dan63047 know about this issue", + "oskwareBridge": "Something happend with oskware_bridge", + "oskwareBridgeSub": "Let dan63047 know", + "p1nkl0bst3rForbidden": "Third party API blocked your IP address", "p1nkl0bst3rTooManyRequests": "Too many requests to third party API. Try again later", "p1nkl0bst3rinternal": "Something happend on the p1nkl0bst3r side", "p1nkl0bst3rinternalWebVersion": "Something happend on the p1nkl0bst3r side (or on oskware_bridge, idk honestly)", "replayAlreadySaved": "Replay already saved", "replayExpired": "Replay expired and not available anymore", - "replayRejected": "Third party API blocked your IP address.\nChange IP address or reach out to szy" + "replayRejected": "Third party API blocked your IP address" }, "countries(map)": { "": "Not selected", diff --git a/res/i18n/strings_ru.i18n.json b/res/i18n/strings_ru.i18n.json index 7afcbbd..6911667 100644 --- a/res/i18n/strings_ru.i18n.json +++ b/res/i18n/strings_ru.i18n.json @@ -46,6 +46,14 @@ "compare": "Сравнить", "tlLeaderboard": "Рейтинговая таблица", "noRecords": "Нет записей", + "noOldRecords": { + "zero": "Нет записей", + "one": "Всего один матч", + "two": "Всего $n матча", + "few": "Всего $n матча", + "many": "Всего $n матчей", + "other": "$n матчей" + }, "noRecord": "Нет рекорда", "botRecord": "Ботам нельзя ставить рекорды", "anonRecord": "Гостям нельзя ставить рекорды", @@ -70,6 +78,9 @@ "comparingWith": "Данные от ${newDate} в сравнении с данными от ${oldDate}", "top": "Топ", "topRank": "Топ Ранг", + "verdictGeneral": "$verdict среднего $rank ранга на $n", + "verdictBetter": "Лучше", + "verdictWorse": "Хуже", "gamesUntilRanked": "${left} матчей до получения рейтинга", "nerdStats": "Для задротов", "playersYouTrack": "Отслеживаемые игроки", @@ -93,8 +104,14 @@ "yourIDAlertTitle": "Ваш ник в TETR.IO", "yourIDText": "При запуске приложения оно будет получать статистику этого игрока.", "language": "Язык (Language)", + "customization": "Кастомизация", + "customizationDescription": "Здесь только один переключатель, в планах добавить больше", + "lbStats": "Показывать статистику, основанную на рейтинговой таблице", + "lbStatsDescription": "Это повлияет на время загрузки, но позволит видеть положение в рейтинге и сравнение со средними значениями по рангу по каждой стате", "aboutApp": "О приложении", "aboutAppText": "${appName} (${packageName}) Версия ${version} Сборка ${buildNumber}\n\nРазработал dan63047\nФормулы предоставил kerrmunism\nИсторию предоставляет p1nkl0bst3r\nВозможность скачивать повторы из TETR.IO предоставляет szy", + "oskKagari": "\"Оск Кагари\" прикол", + "oskKagariDescription": "Если включено, вместо настоящего ранга оска будет рендерится :kagari:", "stateViewTitle": "Аккаунт ${nickname} ${date}", "statesViewTitle": "${number} состояний аккаунта ${nickname}", "matchesViewTitle": "Матчи аккаунта ${nickname}", @@ -248,7 +265,7 @@ }, "tspinsTotal": { "zero": "$n T-спинов всего", - "one": "всего $n T-спин", + "one": "Всего $n T-спин", "two": "$n T-спина всего", "few": "$n T-спина всего", "many": "$n T-спинов всего", @@ -271,21 +288,30 @@ "errors":{ "connection": "Проблема с подключением: ${code} ${message}", "noSuchUser": "Нет такого пользователя", + "noSuchUserSub": "Либо вы ошиблись при вводе, либо аккаунта больше не существует", + "discordNotAssigned": "К данному Discord ID не привязан аккаунт", + "discordNotAssignedSub": "Убедитесь в том, что вы вставили правильный ID", "history": "История данного игрока отсутствует", + "actionSuggestion": "Возможно, вы хотите", "p1nkl0bst3rTLmatches": "Старых матчей Тетра Лиги не было найдено", "clientException": "Нет соединения с интернетом", - "forbidden": "Ваш IP адрес заблокирован.\nСмените IP адрес или свяжитесь с osk-ом", - "tooManyRequests": "Слишком много запросов. Попробуйте позже", + "forbidden": "Ваш IP адрес заблокирован", + "forbiddenSub": "Если у вас работает VPN или прокси, выключите его. Если это не помогло, свяжитесь с $nickname", + "tooManyRequests": "Слишком много запросов", + "tooManyRequestsSub": "Подождите немного и попробуйте снова", "internal": "Что-то случилось на стороне tetr.io", + "internalSub": "Скорее всего, osk уже в курсе об этом", "internalWebVersion": "Что-то случилось на стороне tetr.io (или на стороне oskware_bridge, я хз если честно)", - "oskwareBridge": "Что-то случилось с oskware_bridge. Дайте dan63047 знать", - "p1nkl0bst3rForbidden": "Стороннее API заблокировало ваш IP адрес.\nСмените IP адрес или свяжитесь с p1nkl0bst3r-ом", + "internalWebVersionSub": "Если статус страница osk-а говорит, что всё ок - свяжитесь с dan63047", + "oskwareBridge": "Что-то случилось с oskware_bridge", + "oskwareBridgeSub": "Дайте dan63047 знать", + "p1nkl0bst3rForbidden": "Стороннее API заблокировало ваш IP адрес", "p1nkl0bst3rTooManyRequests": "Слишком много запросов к стороннему API. Попробуйте позже", "p1nkl0bst3rinternal": "Что-то случилось на стороне p1nkl0bst3r-а", "p1nkl0bst3rinternalWebVersion": "Что-то случилось на стороне p1nkl0bst3r-а (или на стороне oskware_bridge, я хз если честно)", "replayAlreadySaved": "Повтор уже сохранён", "replayExpired": "Повтор истёк и больше недоступен", - "replayRejected": "Стороннее API заблокировало ваш IP адрес.\nСмените IP адрес или свяжитесь с szy" + "replayRejected": "Стороннее API заблокировало ваш IP адрес" }, "countries(map)": { "": "Не выбрана", diff --git a/test/api_test.dart b/test/api_test.dart index 3075be0..52f3e87 100644 --- a/test/api_test.dart +++ b/test/api_test.dart @@ -1,5 +1,4 @@ import 'dart:io'; -import 'dart:math'; import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart';