Failed attempt into Table + preparing for 1.5.0

This commit is contained in:
dan63047 2024-03-21 01:56:13 +03:00
parent c1f0e85b4a
commit 8ab4a4db35
18 changed files with 474 additions and 284 deletions

View File

@ -1,9 +1,9 @@
import 'dart:convert'; //import 'dart:convert';
import 'dart:io'; 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 /// That thing allows me to test my new staff i'm trying to implement
void main() async { void main() async {
@ -20,6 +20,6 @@ void main() async {
// List<List<Tetromino>> board = [for (var i = 0 ; i < 40; i++) [for (var i = 0 ; i < 10; i++) Tetromino.empty]]; // List<List<Tetromino>> board = [for (var i = 0 ; i < 40; i++) [for (var i = 0 ; i < 10; i++) Tetromino.empty]];
// print(replay.rawJson); // print(replay.rawJson);
print(""); //print("");
exit(0); exit(0);
} }

View File

@ -4,9 +4,9 @@
/// To regenerate, run: `dart run slang` /// To regenerate, run: `dart run slang`
/// ///
/// Locales: 2 /// 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 // coverage:ignore-file
// ignore_for_file: type=lint // ignore_for_file: type=lint
@ -181,6 +181,14 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
String get stoppedBeingTracked => 'Removed from tracking list!'; String get stoppedBeingTracked => 'Removed from tracking list!';
String get tlLeaderboard => 'Tetra League leaderboard'; String get tlLeaderboard => 'Tetra League leaderboard';
String get noRecords => 'No records'; 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 noRecord => 'No record';
String get botRecord => 'Bots are not allowed to set records'; String get botRecord => 'Bots are not allowed to set records';
String get anonRecord => 'Guests are not allowed to set records'; String get anonRecord => 'Guests are not allowed to set records';
@ -205,6 +213,9 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
String comparingWith({required Object newDate, required Object oldDate}) => 'Data from ${newDate} comparing with ${oldDate}'; String comparingWith({required Object newDate, required Object oldDate}) => 'Data from ${newDate} comparing with ${oldDate}';
String get top => 'Top'; String get top => 'Top';
String get topRank => 'Top Rank'; 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 gamesUntilRanked({required Object left}) => '${left} games until being ranked';
String get nerdStats => 'Nerd Stats'; String get nerdStats => 'Nerd Stats';
String get playersYouTrack => 'Players you track'; String get playersYouTrack => 'Players you track';
@ -228,8 +239,14 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
String get yourIDAlertTitle => 'Your nickname in TETR.IO'; String get yourIDAlertTitle => 'Your nickname in TETR.IO';
String get yourIDText => 'When app loads, it will retrieve data for this account'; String get yourIDText => 'When app loads, it will retrieve data for this account';
String get language => 'Language'; 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 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 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 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 statesViewTitle({required Object number, required Object nickname}) => '${number} states of ${nickname} account';
String matchesViewTitle({required Object nickname}) => '${nickname} TL matches'; String matchesViewTitle({required Object nickname}) => '${nickname} TL matches';
@ -722,21 +739,30 @@ class _StringsErrorsEn {
// Translations // Translations
String connection({required Object code, required Object message}) => 'Some issue with connection: ${code} ${message}'; String connection({required Object code, required Object message}) => 'Some issue with connection: ${code} ${message}';
String get noSuchUser => 'No such user'; 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 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 p1nkl0bst3rTLmatches => 'No Tetra League matches was found';
String get clientException => 'No internet connection'; String get clientException => 'No internet connection';
String get forbidden => 'Your IP address is blocked.\nChange IP address or reach out to osk'; String get forbidden => 'Your IP address is blocked';
String get tooManyRequests => 'You have been rate limited. Try again later'; 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 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 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 internalWebVersionSub => 'If osk status page says that everything is ok, let dan63047 know about this issue';
String get p1nkl0bst3rForbidden => 'Third party API blocked your IP address.\nChange IP address or reach out to p1nkl0bst3r'; 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 p1nkl0bst3rTooManyRequests => 'Too many requests to third party API. Try again later';
String get p1nkl0bst3rinternal => 'Something happend on the p1nkl0bst3r side'; 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 p1nkl0bst3rinternalWebVersion => 'Something happend on the p1nkl0bst3r side (or on oskware_bridge, idk honestly)';
String get replayAlreadySaved => 'Replay already saved'; String get replayAlreadySaved => 'Replay already saved';
String get replayExpired => 'Replay expired and not available anymore'; 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: <root> // Path: <root>
@ -796,6 +822,14 @@ class _StringsRu implements Translations {
@override String get compare => 'Сравнить'; @override String get compare => 'Сравнить';
@override String get tlLeaderboard => 'Рейтинговая таблица'; @override String get tlLeaderboard => 'Рейтинговая таблица';
@override String get noRecords => 'Нет записей'; @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 noRecord => 'Нет рекорда';
@override String get botRecord => 'Ботам нельзя ставить рекорды'; @override String get botRecord => 'Ботам нельзя ставить рекорды';
@override String get anonRecord => 'Гостям нельзя ставить рекорды'; @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 comparingWith({required Object newDate, required Object oldDate}) => 'Данные от ${newDate} в сравнении с данными от ${oldDate}';
@override String get top => 'Топ'; @override String get top => 'Топ';
@override String get topRank => 'Топ Ранг'; @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 gamesUntilRanked({required Object left}) => '${left} матчей до получения рейтинга';
@override String get nerdStats => 'Для задротов'; @override String get nerdStats => 'Для задротов';
@override String get playersYouTrack => 'Отслеживаемые игроки'; @override String get playersYouTrack => 'Отслеживаемые игроки';
@ -843,8 +880,14 @@ class _StringsRu implements Translations {
@override String get yourIDAlertTitle => 'Ваш ник в TETR.IO'; @override String get yourIDAlertTitle => 'Ваш ник в TETR.IO';
@override String get yourIDText => 'При запуске приложения оно будет получать статистику этого игрока.'; @override String get yourIDText => 'При запуске приложения оно будет получать статистику этого игрока.';
@override String get language => 'Язык (Language)'; @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 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 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 stateViewTitle({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}';
@override String statesViewTitle({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}'; @override String statesViewTitle({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}';
@override String matchesViewTitle({required Object nickname}) => 'Матчи аккаунта ${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, @override String tspinsTotal({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n,
zero: '${n} T-спинов всего', zero: '${n} T-спинов всего',
one: 'всего ${n} T-спин', one: 'Всего ${n} T-спин',
two: '${n} T-спина всего', two: '${n} T-спина всего',
few: '${n} T-спина всего', few: '${n} T-спина всего',
many: '${n} T-спинов всего', many: '${n} T-спинов всего',
@ -1337,21 +1380,30 @@ class _StringsErrorsRu implements _StringsErrorsEn {
// Translations // Translations
@override String connection({required Object code, required Object message}) => 'Проблема с подключением: ${code} ${message}'; @override String connection({required Object code, required Object message}) => 'Проблема с подключением: ${code} ${message}';
@override String get noSuchUser => 'Нет такого пользователя'; @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 history => 'История данного игрока отсутствует';
@override String get actionSuggestion => 'Возможно, вы хотите';
@override String get p1nkl0bst3rTLmatches => 'Старых матчей Тетра Лиги не было найдено'; @override String get p1nkl0bst3rTLmatches => 'Старых матчей Тетра Лиги не было найдено';
@override String get clientException => 'Нет соединения с интернетом'; @override String get clientException => 'Нет соединения с интернетом';
@override String get forbidden => 'Ваш IP адрес заблокирован.\nСмените IP адрес или свяжитесь с osk-ом'; @override String get forbidden => 'Ваш IP адрес заблокирован';
@override String get tooManyRequests => 'Слишком много запросов. Попробуйте позже'; @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 internal => 'Что-то случилось на стороне tetr.io';
@override String get internalSub => 'Скорее всего, osk уже в курсе об этом';
@override String get internalWebVersion => 'Что-то случилось на стороне tetr.io (или на стороне oskware_bridge, я хз если честно)'; @override String get internalWebVersion => 'Что-то случилось на стороне tetr.io (или на стороне oskware_bridge, я хз если честно)';
@override String get oskwareBridge => 'Что-то случилось с oskware_bridge. Дайте dan63047 знать'; @override String get internalWebVersionSub => 'Если статус страница osk-а говорит, что всё ок - свяжитесь с dan63047';
@override String get p1nkl0bst3rForbidden => 'Стороннее API заблокировало ваш IP адрес.\nСмените IP адрес или свяжитесь с p1nkl0bst3r-ом'; @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 p1nkl0bst3rTooManyRequests => 'Слишком много запросов к стороннему API. Попробуйте позже';
@override String get p1nkl0bst3rinternal => 'Что-то случилось на стороне p1nkl0bst3r-а'; @override String get p1nkl0bst3rinternal => 'Что-то случилось на стороне p1nkl0bst3r-а';
@override String get p1nkl0bst3rinternalWebVersion => 'Что-то случилось на стороне p1nkl0bst3r-а (или на стороне oskware_bridge, я хз если честно)'; @override String get p1nkl0bst3rinternalWebVersion => 'Что-то случилось на стороне p1nkl0bst3r-а (или на стороне oskware_bridge, я хз если честно)';
@override String get replayAlreadySaved => 'Повтор уже сохранён'; @override String get replayAlreadySaved => 'Повтор уже сохранён';
@override String get replayExpired => 'Повтор истёк и больше недоступен'; @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. /// Flat map(s) containing all translations.
@ -1403,6 +1455,14 @@ extension on Translations {
case 'stoppedBeingTracked': return 'Removed from tracking list!'; case 'stoppedBeingTracked': return 'Removed from tracking list!';
case 'tlLeaderboard': return 'Tetra League leaderboard'; case 'tlLeaderboard': return 'Tetra League leaderboard';
case 'noRecords': return 'No records'; 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 'noRecord': return 'No record';
case 'botRecord': return 'Bots are not allowed to set records'; case 'botRecord': return 'Bots are not allowed to set records';
case 'anonRecord': return 'Guests 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 'comparingWith': return ({required Object newDate, required Object oldDate}) => 'Data from ${newDate} comparing with ${oldDate}';
case 'top': return 'Top'; case 'top': return 'Top';
case 'topRank': return 'Top Rank'; 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 'gamesUntilRanked': return ({required Object left}) => '${left} games until being ranked';
case 'nerdStats': return 'Nerd Stats'; case 'nerdStats': return 'Nerd Stats';
case 'playersYouTrack': return 'Players you track'; case 'playersYouTrack': return 'Players you track';
@ -1450,8 +1513,14 @@ extension on Translations {
case 'yourIDAlertTitle': return 'Your nickname in TETR.IO'; case 'yourIDAlertTitle': return 'Your nickname in TETR.IO';
case 'yourIDText': return 'When app loads, it will retrieve data for this account'; case 'yourIDText': return 'When app loads, it will retrieve data for this account';
case 'language': return 'Language'; 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 '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 '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 '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 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} states of ${nickname} account';
case 'matchesViewTitle': return ({required Object nickname}) => '${nickname} TL matches'; case 'matchesViewTitle': return ({required Object nickname}) => '${nickname} TL matches';
@ -1619,21 +1688,30 @@ extension on Translations {
case 'popupActions.ok': return 'OK'; case 'popupActions.ok': return 'OK';
case 'errors.connection': return ({required Object code, required Object message}) => 'Some issue with connection: ${code} ${message}'; 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.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.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.p1nkl0bst3rTLmatches': return 'No Tetra League matches was found';
case 'errors.clientException': return 'No internet connection'; 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.forbidden': return 'Your IP address is blocked';
case 'errors.tooManyRequests': return 'You have been rate limited. Try again later'; 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.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.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.internalWebVersionSub': return 'If osk status page says that everything is ok, let dan63047 know about this issue';
case 'errors.p1nkl0bst3rForbidden': return 'Third party API blocked your IP address.\nChange IP address or reach out to p1nkl0bst3r'; 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.p1nkl0bst3rTooManyRequests': return 'Too many requests to third party API. Try again later';
case 'errors.p1nkl0bst3rinternal': return 'Something happend on the p1nkl0bst3r side'; 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.p1nkl0bst3rinternalWebVersion': return 'Something happend on the p1nkl0bst3r side (or on oskware_bridge, idk honestly)';
case 'errors.replayAlreadySaved': return 'Replay already saved'; case 'errors.replayAlreadySaved': return 'Replay already saved';
case 'errors.replayExpired': return 'Replay expired and not available anymore'; 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.': return 'Not selected';
case 'countries.AF': return 'Afghanistan'; case 'countries.AF': return 'Afghanistan';
case 'countries.AX': return 'Åland Islands'; case 'countries.AX': return 'Åland Islands';
@ -1944,6 +2022,14 @@ extension on _StringsRu {
case 'compare': return 'Сравнить'; case 'compare': return 'Сравнить';
case 'tlLeaderboard': return 'Рейтинговая таблица'; case 'tlLeaderboard': return 'Рейтинговая таблица';
case 'noRecords': 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 'noRecord': return 'Нет рекорда';
case 'botRecord': return 'Ботам нельзя ставить рекорды'; case 'botRecord': return 'Ботам нельзя ставить рекорды';
case 'anonRecord': return 'Гостям нельзя ставить рекорды'; case 'anonRecord': return 'Гостям нельзя ставить рекорды';
@ -1968,6 +2054,9 @@ extension on _StringsRu {
case 'comparingWith': return ({required Object newDate, required Object oldDate}) => 'Данные от ${newDate} в сравнении с данными от ${oldDate}'; case 'comparingWith': return ({required Object newDate, required Object oldDate}) => 'Данные от ${newDate} в сравнении с данными от ${oldDate}';
case 'top': return 'Топ'; case 'top': return 'Топ';
case 'topRank': 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 'gamesUntilRanked': return ({required Object left}) => '${left} матчей до получения рейтинга';
case 'nerdStats': return 'Для задротов'; case 'nerdStats': return 'Для задротов';
case 'playersYouTrack': return 'Отслеживаемые игроки'; case 'playersYouTrack': return 'Отслеживаемые игроки';
@ -1991,8 +2080,14 @@ extension on _StringsRu {
case 'yourIDAlertTitle': return 'Ваш ник в TETR.IO'; case 'yourIDAlertTitle': return 'Ваш ник в TETR.IO';
case 'yourIDText': return 'При запуске приложения оно будет получать статистику этого игрока.'; case 'yourIDText': return 'При запуске приложения оно будет получать статистику этого игрока.';
case 'language': return 'Язык (Language)'; case 'language': return 'Язык (Language)';
case 'customization': return 'Кастомизация';
case 'customizationDescription': return 'Здесь только один переключатель, в планах добавить больше';
case 'lbStats': return 'Показывать статистику, основанную на рейтинговой таблице';
case 'lbStatsDescription': return 'Это повлияет на время загрузки, но позволит видеть положение в рейтинге и сравнение со средними значениями по рангу по каждой стате';
case 'aboutApp': 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 '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 'stateViewTitle': return ({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}';
case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}'; case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}';
case 'matchesViewTitle': return ({required Object nickname}) => 'Матчи аккаунта ${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, case 'numOfGameActions.tspinsTotal': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n,
zero: '${n} T-спинов всего', zero: '${n} T-спинов всего',
one: 'всего ${n} T-спин', one: 'Всего ${n} T-спин',
two: '${n} T-спина всего', two: '${n} T-спина всего',
few: '${n} T-спина всего', few: '${n} T-спина всего',
many: '${n} T-спинов всего', many: '${n} T-спинов всего',
@ -2160,21 +2255,30 @@ extension on _StringsRu {
case 'popupActions.ok': return 'OK'; case 'popupActions.ok': return 'OK';
case 'errors.connection': return ({required Object code, required Object message}) => 'Проблема с подключением: ${code} ${message}'; case 'errors.connection': return ({required Object code, required Object message}) => 'Проблема с подключением: ${code} ${message}';
case 'errors.noSuchUser': return 'Нет такого пользователя'; 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.history': return 'История данного игрока отсутствует';
case 'errors.actionSuggestion': return 'Возможно, вы хотите';
case 'errors.p1nkl0bst3rTLmatches': return 'Старых матчей Тетра Лиги не было найдено'; case 'errors.p1nkl0bst3rTLmatches': return 'Старых матчей Тетра Лиги не было найдено';
case 'errors.clientException': return 'Нет соединения с интернетом'; case 'errors.clientException': return 'Нет соединения с интернетом';
case 'errors.forbidden': return 'Ваш IP адрес заблокирован.\nСмените IP адрес или свяжитесь с osk-ом'; case 'errors.forbidden': return 'Ваш IP адрес заблокирован';
case 'errors.tooManyRequests': return 'Слишком много запросов. Попробуйте позже'; 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.internal': return 'Что-то случилось на стороне tetr.io';
case 'errors.internalSub': return 'Скорее всего, osk уже в курсе об этом';
case 'errors.internalWebVersion': return 'Что-то случилось на стороне tetr.io (или на стороне oskware_bridge, я хз если честно)'; case 'errors.internalWebVersion': return 'Что-то случилось на стороне tetr.io (или на стороне oskware_bridge, я хз если честно)';
case 'errors.oskwareBridge': return 'Что-то случилось с oskware_bridge. Дайте dan63047 знать'; case 'errors.internalWebVersionSub': return 'Если статус страница osk-а говорит, что всё ок - свяжитесь с dan63047';
case 'errors.p1nkl0bst3rForbidden': return 'Стороннее API заблокировало ваш IP адрес.\nСмените IP адрес или свяжитесь с p1nkl0bst3r-ом'; case 'errors.oskwareBridge': return 'Что-то случилось с oskware_bridge';
case 'errors.oskwareBridgeSub': return 'Дайте dan63047 знать';
case 'errors.p1nkl0bst3rForbidden': return 'Стороннее API заблокировало ваш IP адрес';
case 'errors.p1nkl0bst3rTooManyRequests': return 'Слишком много запросов к стороннему API. Попробуйте позже'; case 'errors.p1nkl0bst3rTooManyRequests': return 'Слишком много запросов к стороннему API. Попробуйте позже';
case 'errors.p1nkl0bst3rinternal': return 'Что-то случилось на стороне p1nkl0bst3r-а'; case 'errors.p1nkl0bst3rinternal': return 'Что-то случилось на стороне p1nkl0bst3r-а';
case 'errors.p1nkl0bst3rinternalWebVersion': return 'Что-то случилось на стороне p1nkl0bst3r-а (или на стороне oskware_bridge, я хз если честно)'; case 'errors.p1nkl0bst3rinternalWebVersion': return 'Что-то случилось на стороне p1nkl0bst3r-а (или на стороне oskware_bridge, я хз если честно)';
case 'errors.replayAlreadySaved': return 'Повтор уже сохранён'; case 'errors.replayAlreadySaved': return 'Повтор уже сохранён';
case 'errors.replayExpired': 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.': return 'Не выбрана';
case 'countries.AF': return 'Афганистан'; case 'countries.AF': return 'Афганистан';
case 'countries.AX': return 'Аландские острова'; case 'countries.AX': return 'Аландские острова';

View File

@ -14,6 +14,8 @@ class TetrioPlayerAlreadyExist implements Exception {}
class TetrioPlayerNotExist implements Exception {} class TetrioPlayerNotExist implements Exception {}
class TetrioDiscordNotExist implements Exception {}
class TetrioHistoryNotExist implements Exception {} class TetrioHistoryNotExist implements Exception {}
class TetrioTooManyRequests implements Exception {} class TetrioTooManyRequests implements Exception {}

View File

@ -974,7 +974,7 @@ class TetrioService extends DB {
user = json['data']['user']['_id']; user = json['data']['user']['_id'];
} else { // fail - throw an exception } else { // fail - throw an exception
developer.log("fetchPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body); developer.log("fetchPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body);
throw TetrioPlayerNotExist(); throw TetrioDiscordNotExist();
} }
break; break;
// more exceptions to god of exceptions // more exceptions to god of exceptions

View File

@ -7,3 +7,4 @@ final NumberFormat f4 = NumberFormat.decimalPatternDigits(locale: LocaleSettings
final NumberFormat f3 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3); final NumberFormat f3 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3);
final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2); final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2);
final NumberFormat f2l = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2)..minimumFractionDigits = 0; final NumberFormat f2l = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2)..minimumFractionDigits = 0;
final NumberFormat f0 = NumberFormat.decimalPattern(LocaleSettings.currentLocale.languageCode);

View File

@ -69,7 +69,7 @@ class CalcState extends State<CalcView> {
body: SafeArea( body: SafeArea(
child: Center( child: Center(
child: Container( child: Container(
constraints: BoxConstraints(maxWidth: 768), constraints: const BoxConstraints(maxWidth: 768),
child: NestedScrollView( child: NestedScrollView(
controller: _scrollController, controller: _scrollController,
headerSliverBuilder: (context, value) { headerSliverBuilder: (context, value) {

View File

@ -258,7 +258,7 @@ class CompareState extends State<CompareView> {
body: SafeArea( body: SafeArea(
child: Center( child: Center(
child: Container( child: Container(
constraints: BoxConstraints(maxWidth: 768), constraints: const BoxConstraints(maxWidth: 768),
child: NestedScrollView( child: NestedScrollView(
controller: _scrollController, controller: _scrollController,
headerSliverBuilder: (context, value) { headerSliverBuilder: (context, value) {
@ -327,7 +327,7 @@ class CompareState extends State<CompareView> {
}, },
body: Center( body: Center(
child: Container( child: Container(
constraints: BoxConstraints(maxWidth: 768), constraints: const BoxConstraints(maxWidth: 768),
child: ListView( child: ListView(
children: !listEquals(theGreenSide, [null, null, null]) && !listEquals(theRedSide, [null, null, null])? [ children: !listEquals(theGreenSide, [null, null, null]) && !listEquals(theRedSide, [null, null, null])? [
if (theGreenSide[0] != null && if (theGreenSide[0] != null &&

View File

@ -20,6 +20,7 @@ class CustomizationView extends StatefulWidget {
class CustomizationState extends State<CustomizationView> { class CustomizationState extends State<CustomizationView> {
late SharedPreferences prefs; late SharedPreferences prefs;
late bool oskKagariGimmick;
void changeColor(Color color) { void changeColor(Color color) {
setState(() => pickerColor = color); setState(() => pickerColor = color);
@ -31,7 +32,7 @@ class CustomizationState extends State<CustomizationView> {
windowManager.getTitle().then((value) => oldWindowTitle = value); windowManager.getTitle().then((value) => oldWindowTitle = value);
windowManager.setTitle("Tetra Stats: ${t.settings}"); windowManager.setTitle("Tetra Stats: ${t.settings}");
} }
_getPreferences(); _getPreferences().then((value) => setState((){}));
super.initState(); super.initState();
} }
@ -43,6 +44,11 @@ class CustomizationState extends State<CustomizationView> {
Future<void> _getPreferences() async { Future<void> _getPreferences() async {
prefs = await SharedPreferences.getInstance(); prefs = await SharedPreferences.getInstance();
if (prefs.getBool("oskKagariGimmick") != null) {
oskKagariGimmick = prefs.getBool("oskKagariGimmick")!;
} else {
oskKagariGimmick = true;
}
} }
ThemeData getTheme(BuildContext context, Color color){ ThemeData getTheme(BuildContext context, Color color){
@ -66,59 +72,48 @@ class CustomizationState extends State<CustomizationView> {
body: SafeArea( body: SafeArea(
child: ListView( child: ListView(
children: [ children: [
ListTile( // ListTile(
title: const Text("Accent Color"), // title: const Text("Accent color"),
trailing: ColorIndicator(HSVColor.fromColor(Theme.of(context).colorScheme.primary)), // trailing: ColorIndicator(HSVColor.fromColor(Theme.of(context).colorScheme.primary)),
onTap: () { // onTap: () {
showDialog( // showDialog(
context: context, // context: context,
builder: (BuildContext context) => AlertDialog( // builder: (BuildContext context) => AlertDialog(
title: const Text('Pick a color!'), // title: const Text('Pick an accent color'),
content: SingleChildScrollView( // content: SingleChildScrollView(
child: ColorPicker( // child: ColorPicker(
pickerColor: pickerColor, // pickerColor: pickerColor,
onColorChanged: changeColor, // onColorChanged: changeColor,
), // ),
// Use Material color picker: // ),
// // actions: <Widget>[
// child: MaterialPicker( // ElevatedButton(
// pickerColor: pickerColor, // child: const Text('Set'),
// onColorChanged: changeColor, // onPressed: () {
// showLabel: true, // only on portrait mode // setState(() {
// ), // setAccentColor(pickerColor);
// // });
// Use Block color picker: // Navigator.of(context).pop();
// // },
// child: BlockPicker( // ),
// pickerColor: currentColor, // ]));
// onColorChanged: changeColor, // }),
// ), // const ListTile(
// // title: Text("Font"),
// child: MultipleChoiceBlockPicker( // subtitle: Text("Not implemented"),
// pickerColors: currentColors, // ),
// onColorsChanged: changeColors, // const ListTile(
// ), // title: Text("Stats Table in TL mathes list"),
), // subtitle: Text("Not implemented"),
actions: <Widget>[ // ),
ElevatedButton( ListTile(title: Text(t.oskKagari),
child: const Text('Got it'), subtitle: Text(t.oskKagariDescription),
onPressed: () { trailing: Switch(value: oskKagariGimmick, onChanged: (bool value){
setState(() { prefs.setBool("oskKagariGimmick", value);
setAccentColor(pickerColor); setState(() {
}); oskKagariGimmick = value;
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"),
),
], ],
)), )),
); );

View File

@ -34,6 +34,7 @@ import 'package:go_router/go_router.dart';
final TetrioService teto = TetrioService(); // thing, that manadge our local DB final TetrioService teto = TetrioService(); // thing, that manadge our local DB
int _chartsIndex = 0; 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"]; 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; late ScrollController _scrollController;
final NumberFormat _timeInSec = NumberFormat("#,###.###s.", LocaleSettings.currentLocale.languageCode); final NumberFormat _timeInSec = NumberFormat("#,###.###s.", LocaleSettings.currentLocale.languageCode);
@ -84,8 +85,10 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
String _titleNickname = "dan63047"; String _titleNickname = "dan63047";
/// Each dropdown menu item contains list of dots for the graph /// Each dropdown menu item contains list of dots for the graph
var chartsData = <DropdownMenuItem<List<FlSpot>>>[]; var chartsData = <DropdownMenuItem<List<FlSpot>>>[];
//var tableData = <TableRow>[];
final bodyGlobalKey = GlobalKey(); final bodyGlobalKey = GlobalKey();
bool _showSearchBar = false; bool _showSearchBar = false;
bool _TLHistoryWasFetched = false;
late TabController _tabController; late TabController _tabController;
late TabController _wideScreenTabController; late TabController _wideScreenTabController;
late bool fixedScroll; late bool fixedScroll;
@ -143,6 +146,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
/// If at least one request to Tetra Channel API fails, whole function will throw an exception. /// If at least one request to Tetra Channel API fails, whole function will throw an exception.
Future<List> fetch(String nickOrID, {bool fetchHistory = false, bool fetchTLmatches = false}) async { Future<List> fetch(String nickOrID, {bool fetchHistory = false, bool fetchTLmatches = false}) async {
TetrioPlayer me; TetrioPlayer me;
_TLHistoryWasFetched = false;
// If user trying to search with discord id // If user trying to search with discord id
if (nickOrID.startsWith("ds:")){ if (nickOrID.startsWith("ds:")){
@ -211,8 +215,11 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.errors.p1nkl0bst3rinternal))); if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.errors.p1nkl0bst3rinternal)));
}on P1nkl0bst3rTooManyRequests{ }on P1nkl0bst3rTooManyRequests{
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.errors.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) { for (var match in storedRecords) {
// add stored match to list only if it missing from retrived ones // add stored match to list only if it missing from retrived ones
if (!tlMatches.contains(match)) tlMatches.add(match); if (!tlMatches.contains(match)) tlMatches.add(match);
@ -246,6 +253,11 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
if (uniqueTL.isEmpty) uniqueTL.add(element.tlSeason1); if (uniqueTL.isEmpty) uniqueTL.add(element.tlSeason1);
} }
// Also i need previous Tetra League State for comparison if avaliable // 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){ if (uniqueTL.length >= 2){
compareWith = uniqueTL.toList().elementAtOrNull(uniqueTL.length - 2); compareWith = uniqueTL.toList().elementAtOrNull(uniqueTL.length - 2);
chartsData = <DropdownMenuItem<List<FlSpot>>>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid chartsData = <DropdownMenuItem<List<FlSpot>>>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid
@ -381,9 +393,9 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
return notification.depth == 0; return notification.depth == 0;
}, },
child: NestedScrollView( child: NestedScrollView(
scrollBehavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
controller: _scrollController, controller: _scrollController,
scrollBehavior: const MaterialScrollBehavior(),
headerSliverBuilder: (context, value) { headerSliverBuilder: (context, value) {
return [ return [
SliverToBoxAdapter( SliverToBoxAdapter(
@ -422,7 +434,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
children: [ children: [
Container( Container(
width: MediaQuery.of(context).size.width-450, width: MediaQuery.of(context).size.width-450,
constraints: BoxConstraints(maxWidth: 1024), constraints: const BoxConstraints(maxWidth: 1024),
child: TLThingy( child: TLThingy(
tl: snapshot.data![0].tlSeason1, tl: snapshot.data![0].tlSeason1,
userID: snapshot.data![0].userId, userID: snapshot.data![0].userId,
@ -436,23 +448,11 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
), ),
SizedBox( SizedBox(
width: 450, 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,), _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],) _OtherThingy(zen: snapshot.data![1]['zen'], bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![6],)
] : [ ] : [
TLThingy( TLThingy(
@ -465,8 +465,8 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
averages: rankAverages, averages: rankAverages,
lbPositions: meAmongEveryone lbPositions: meAmongEveryone
), ),
_TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], 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, 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),
_RecordThingy(record: snapshot.data![1]['sprint'], rank: snapshot.data![0].tlSeason1.percentileRank), _RecordThingy(record: snapshot.data![1]['sprint'], rank: snapshot.data![0].tlSeason1.percentileRank),
_RecordThingy(record: snapshot.data![1]['blitz'], 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],) _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<MainView> with TickerProviderStateMixin {
); );
} else if (snapshot.hasError) { } else if (snapshot.hasError) {
String errText = ""; String errText = "";
String? subText;
switch (snapshot.error.runtimeType){ switch (snapshot.error.runtimeType){
case TetrioPlayerNotExist: case TetrioPlayerNotExist:
errText = t.errors.noSuchUser; errText = t.errors.noSuchUser;
subText = t.errors.noSuchUserSub;
break; break;
case TetrioDiscordNotExist:
errText = t.errors.discordNotAssigned;
subText = t.errors.discordNotAssignedSub;
case ConnectionIssue: case ConnectionIssue:
var err = snapshot.error as ConnectionIssue; var err = snapshot.error as ConnectionIssue;
errText = t.errors.connection(code: err.code, message: err.message); errText = t.errors.connection(code: err.code, message: err.message);
break; break;
case TetrioHistoryNotExist:
errText = t.errors.history;
break;
case TetrioForbidden: case TetrioForbidden:
errText = t.errors.forbidden; errText = t.errors.forbidden;
subText = t.errors.forbiddenSub(nickname: 'osk');
break; break;
case TetrioTooManyRequests: case TetrioTooManyRequests:
errText = t.errors.tooManyRequests; errText = t.errors.tooManyRequests;
subText = t.errors.tooManyRequestsSub;
break; break;
case TetrioOskwareBridgeProblem: case TetrioOskwareBridgeProblem:
errText = t.errors.oskwareBridge; errText = t.errors.oskwareBridge;
subText = t.errors.oskwareBridgeSub;
break; break;
case TetrioInternalProblem: case TetrioInternalProblem:
errText = kIsWeb ? t.errors.internalWebVersion : t.errors.internal; errText = kIsWeb ? t.errors.internalWebVersion : t.errors.internal;
subText = kIsWeb ? t.errors.internalWebVersionSub : t.errors.internalSub;
break; break;
case ClientException: case ClientException:
errText = t.errors.clientException; errText = t.errors.clientException;
@ -505,7 +511,18 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
default: default:
errText = snapshot.error.toString(); 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; break;
} }
@ -646,27 +663,42 @@ class _TLRecords extends StatelessWidget {
final Function changePlayer; final Function changePlayer;
final List<TetraLeagueAlphaRecord> data; final List<TetraLeagueAlphaRecord> data;
final bool wasActiveInTL; final bool wasActiveInTL;
final bool oldMathcesHere;
/// Widget, that displays Tetra League records. /// Widget, that displays Tetra League records.
/// Accepts list of TL records ([data]) and [userID] of player from the view /// 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (data.isEmpty) return Center(child: Column( if (data.isEmpty) {
return Center(child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text(t.noRecords, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)), 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)) if (wasActiveInTL) TextButton(onPressed: (){changePlayer(userID, fetchTLmatches: true);}, child: Text(t.fetchAndSaveOldTLmatches))
], ],
)); ));
}
bool bigScreen = MediaQuery.of(context).size.width >= 768; bool bigScreen = MediaQuery.of(context).size.width >= 768;
int length = data.length;
return ListView.builder( return ListView.builder(
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
controller: ScrollController(), controller: ScrollController(),
itemCount: data.length, itemCount: oldMathcesHere ? length : length + 1,
itemBuilder: (BuildContext context, int index) { 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; var accentColor = data[index].endContext.firstWhere((element) => element.userId == userID).success ? Colors.green : Colors.red;
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -698,7 +730,6 @@ class _TLRecords extends StatelessWidget {
class _History extends StatelessWidget{ class _History extends StatelessWidget{
final List<DropdownMenuItem<List<FlSpot>>> chartsData; final List<DropdownMenuItem<List<FlSpot>>> chartsData;
final List<TetrioPlayer> states;
final String userID; final String userID;
final Function update; final Function update;
final Function changePlayer; 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. /// 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 /// 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool bigScreen = MediaQuery.of(context).size.width > 768; if (chartsData.isEmpty) {
return chartsData.isNotEmpty ? return Center(child: Column(
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(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text(t.noHistorySaved, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)), Text(t.noHistorySaved, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)),
if (wasActiveInTL) Text("Perhaps, you want"), if (wasActiveInTL) Text(t.errors.actionSuggestion),
if (wasActiveInTL)TextButton(onPressed: (){changePlayer(userID, fetchHistory: true);}, child: Text(t.fetchAndsaveTLHistory)) 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{ // class _HistoryTableThingy extends StatelessWidget{
final List<TetrioPlayer> states; // final List<TableRow> tableData;
const _HistoryTableThingy(this.states); // const _HistoryTableThingy(this.tableData);
// :tf: // // :tf:
@override // @override
Widget build(BuildContext context) { // Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints){ // return LayoutBuilder(builder: (context, constraints){
return SingleChildScrollView(child: Table( // return Table(
children: [ // defaultColumnWidth: FixedColumnWidth(75),
TableRow(children: [Text("Date & Time"), Text("Tr")]), // columnWidths: {
], // 0: FixedColumnWidth(170),
)); // 1: FixedColumnWidth(100),
}); // 2: FixedColumnWidth(90),
} // 18: FixedColumnWidth(100),
} // 19: FixedColumnWidth(90),
// },
// children: tableData,
// );
// });
// }
// }
class _TwoRecordsThingy extends StatelessWidget { class _TwoRecordsThingy extends StatelessWidget {
final RecordSingle? sprint; final RecordSingle? sprint;
@ -1082,13 +1145,13 @@ class _TwoRecordsThingy extends StatelessWidget {
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ 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) 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( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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( RichText(text: TextSpan(
text: sprint != null ? get40lTime(sprint!.endContext!.finalTime.inMicroseconds) : "---", 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), 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: "", text: "",
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
children: [ 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 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( 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: sprintBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent color: sprintBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
)), )),
if (sprint!.rank != null) TextSpan(text: "${sprint!.rank}", style: TextStyle(color: getColorOfRank(sprint!.rank!))), if (sprint!.rank != null) TextSpan(text: "${sprint!.rank}", style: TextStyle(color: getColorOfRank(sprint!.rank!))),
if (sprint!.rank != null) const TextSpan(text: ""), if (sprint!.rank != null) const TextSpan(text: ""),
@ -1157,10 +1220,10 @@ class _TwoRecordsThingy extends StatelessWidget {
text: "", text: "",
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
children: [ 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 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 color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
)), )),
TextSpan(text: _dateFormat.format(blitz!.timestamp!)), 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)), 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( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ 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) 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) child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageBlitz.key}.png", height: 96)
), ),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (record!.stream.contains("40l")) Text(t.sprint, 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: 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( RichText(text: TextSpan(
text: record!.stream.contains("40l") ? get40lTime(record!.endContext!.finalTime.inMicroseconds) : NumberFormat.decimalPattern().format(record!.endContext!.score), 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), 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: "", text: "",
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
children: [ 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 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( 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: sprintBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent 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 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 color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
)), )),
if (record!.rank != null) TextSpan(text: "${record!.rank}", style: TextStyle(color: getColorOfRank(record!.rank!))), 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(), physics: const AlwaysScrollableScrollPhysics(),
itemCount: newsletter!.length+1, itemCount: newsletter!.length+1,
itemBuilder: (BuildContext context, int index) { 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]);
} }
)) ))
] ]

View File

@ -1,4 +1,5 @@
import 'dart:io'; import 'dart:io';
import 'package:go_router/go_router.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/main.dart' show packageInfo; import 'package:tetra_stats/main.dart' show packageInfo;
import 'package:file_selector/file_selector.dart'; import 'package:file_selector/file_selector.dart';
@ -260,14 +261,14 @@ class SettingsState extends State<SettingsView> {
}, },
), ),
), ),
ListTile(title: const Text("Customization"), ListTile(title: Text(t.customization),
subtitle: const Text("I don't want to implement this"), subtitle: Text(t.customizationDescription),
trailing: const Icon(Icons.arrow_right), trailing: const Icon(Icons.arrow_right),
onTap: () { onTap: () {
Navigator.pushNamed(context, "/customization"); context.go("/customization");
},), },),
ListTile(title: Text("Show leaderboard based stats"), ListTile(title: Text(t.lbStats),
subtitle: Text("That will impact on loading times, but will allow you to see position on LB by stats and comparison with average values"), subtitle: Text(t.lbStatsDescription),
trailing: Switch(value: showPositions, onChanged: (bool value){ trailing: Switch(value: showPositions, onChanged: (bool value){
prefs.setBool("showPositions", value); prefs.setBool("showPositions", value);
setState(() { setState(() {
@ -281,6 +282,7 @@ class SettingsState extends State<SettingsView> {
}, },
title: Text(t.aboutApp), title: Text(t.aboutApp),
subtitle: Text(t.aboutAppText(appName: packageInfo.appName, packageName: packageInfo.packageName, version: packageInfo.version, buildNumber: packageInfo.buildNumber)), subtitle: Text(t.aboutAppText(appName: packageInfo.appName, packageName: packageInfo.packageName, version: packageInfo.version, buildNumber: packageInfo.buildNumber)),
trailing: const Icon(Icons.arrow_right)
), ),
], ],
)), )),

View File

@ -98,7 +98,7 @@ class GaugetNum extends StatelessWidget {
oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent
),), ),),
if ((oldTl != null && oldTl!.gamesPlayed > 0) && pos != null) const TextSpan(text: ""), 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}")
] ]
), ),
), ),

View File

@ -20,11 +20,11 @@ class LineclearsThingy extends StatelessWidget{
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text(t.numOfGameActions.lineClears(n: lines), style: TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended")), Text(t.numOfGameActions.lineClears(n: lines), style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("Quads"), Text(clears.quads.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Quads"), Text(clears.quads.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("Triples"), Text(clears.triples.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Triples"), Text(clears.triples.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("Doubles"), Text(clears.doubles.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Doubles"), Text(clears.doubles.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("Singles"), Text(clears.singles.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("\n${t.numOfGameActions.pc}"), Text("\n${clears.allClears.toString()}")]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text(t.numOfGameActions.hold), Text(holds.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text(t.numOfGameActions.hold), Text(holds.toString())]),
], ],
@ -35,14 +35,14 @@ class LineclearsThingy extends StatelessWidget{
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text(t.numOfGameActions.tspinsTotal(n: tSpins), style: TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended")), Text(t.numOfGameActions.tspinsTotal(n: tSpins), style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("T-spins triples"), Text(clears.tSpinTriples.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const 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: [const 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: [const 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: [const 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: [const 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: [const Text("Mini T-spins singles"), Text(clears.tSpinMiniSingles.toString())]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Text("Mini T-spins zeros"), Text(clears.tSpinMiniZeros.toString())]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins zeros"), Text(clears.tSpinMiniZeros.toString())]),
], ],
), ),
), ),

View File

@ -70,7 +70,7 @@ class StatCellNum extends StatelessWidget {
oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent
),), ),),
if (oldPlayerStat != null && pos != null) const TextSpan(text: ""), 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}")
] ]
), ),
), ),

View File

@ -3,8 +3,8 @@ import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:syncfusion_flutter_gauges/gauges.dart'; import 'package:syncfusion_flutter_gauges/gauges.dart';
import 'package:tetra_stats/gen/strings.g.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/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/gauget_num.dart';
import 'package:tetra_stats/widgets/graphs.dart'; import 'package:tetra_stats/widgets/graphs.dart';
import 'package:tetra_stats/widgets/stat_sell_num.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart';
@ -38,11 +38,13 @@ class TLThingy extends StatefulWidget {
} }
class _TLThingyState extends State<TLThingy> { class _TLThingyState extends State<TLThingy> {
late bool oskKagariGimmick;
@override @override
void initState() { void initState() {
_currentRangeValues = const RangeValues(0, 1); _currentRangeValues = const RangeValues(0, 1);
sortedStates = widget.states.reversed.toList(); sortedStates = widget.states.reversed.toList();
oskKagariGimmick = prefs.getBool("oskKagariGimmick")??true;
try{ try{
oldTl = sortedStates[1].tlSeason1; oldTl = sortedStates[1].tlSeason1;
}on RangeError{ }on RangeError{
@ -97,7 +99,7 @@ class _TLThingyState extends State<TLThingy> {
crossAxisAlignment: WrapCrossAlignment.center, crossAxisAlignment: WrapCrossAlignment.center,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
children: [ 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/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), : Image.asset("res/tetrio_tl_alpha_ranks/${currentTl.rank}.png", height: 128),
Column( Column(
@ -292,27 +294,28 @@ class _TLThingyState extends State<TLThingy> {
), ),
if (currentTl.estTr != null) if (currentTl.estTr != null)
Padding( Padding(
padding: const EdgeInsets.fromLTRB(0, 48, 0, 48), padding: const EdgeInsets.fromLTRB(0, 20, 0, 20),
child: Container( child: Container(
//alignment: Alignment.center, //alignment: Alignment.center,
width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85, width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85,
height: 70,
constraints: BoxConstraints(maxWidth: 768), constraints: BoxConstraints(maxWidth: 768),
child: Wrap( child: Stack(
alignment: WrapAlignment.spaceBetween,
spacing: 20,
children: [ children: [
Column( Positioned(
crossAxisAlignment: CrossAxisAlignment.start, left: 0,
children: [ child: Column(
Text(t.statCellNum.estOfTR, style: TextStyle(height: 0.1),), crossAxisAlignment: CrossAxisAlignment.start,
RichText( children: [
text: TextSpan( Text(t.statCellNum.estOfTR, style: TextStyle(height: 0.1),),
text: intf.format(currentTl.estTr!.esttr.truncate()), RichText(
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white), text: TextSpan(
children: [TextSpan(text: fractionfEstTR.format(currentTl.estTr!.esttr - currentTl.estTr!.esttr.truncate()).substring(1), style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))] 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))]
),
), ),
), RichText(text: TextSpan(
if (oldTl?.estTr?.esttr != null || widget.lbPositions != null) RichText(text: TextSpan(
text: "", text: "",
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey, height: 0.5), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey, height: 0.5),
children: [ children: [
@ -320,78 +323,46 @@ class _TLThingyState extends State<TLThingy> {
color: oldTl!.estTr!.esttr > currentTl.estTr!.esttr ? Colors.redAccent : Colors.greenAccent color: oldTl!.estTr!.esttr > currentTl.estTr!.esttr ? Colors.redAccent : Colors.greenAccent
),), ),),
if (oldTl?.estTr?.esttr != null && widget.lbPositions?.estTr != null) const TextSpan(text: ""), 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: ""), if (widget.lbPositions?.estTr != null) const TextSpan(text: ""),
TextSpan(text: "Glicko: ${f2.format(currentTl.estTr!.estglicko)}") TextSpan(text: "Glicko: ${f2.format(currentTl.estTr!.estglicko)}")
] ]
), ),
), ),
],), ],),
Column( ),
crossAxisAlignment: CrossAxisAlignment.end, Positioned(
children: [ right: 0,
Text(t.statCellNum.accOfEst, style: const TextStyle(height: 0.1),), child: Column(
RichText( crossAxisAlignment: CrossAxisAlignment.end,
text: TextSpan( children: [
text: (currentTl.esttracc != null && currentTl.bestRank != "z") ? intFDiff.format(currentTl.esttracc!.truncate()) : "---", Text(t.statCellNum.accOfEst, style: const TextStyle(height: 0.1),),
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white), 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: [ 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!) if (currentTl.nerdStats != null) Graphs(currentTl.apm!, currentTl.pps!, currentTl.vs!, currentTl.nerdStats!, currentTl.playstyle!)
] ]

View File

@ -91,6 +91,7 @@ class UserThingy extends StatelessWidget {
? Image.asset("res/avatars/tetrio_banned.png", fit: BoxFit.fitHeight, height: pfpHeight,) ? Image.asset("res/avatars/tetrio_banned.png", fit: BoxFit.fitHeight, height: pfpHeight,)
: player.avatarRevision != null : player.avatarRevision != null
? Image.network("https://tetr.io/user-content/avatars/${player.userId}.jpg?rv=${player.avatarRevision}", ? 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) { fit: BoxFit.fitHeight, height: 128, errorBuilder: (context, error, stackTrace) {
developer.log("Error with building profile picture", name: "main_view", error: error, stackTrace: 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); return Image.asset("res/avatars/tetrio_anon.png", fit: BoxFit.fitHeight, height: pfpHeight);

View File

@ -46,6 +46,14 @@
"stoppedBeingTracked": "Removed from tracking list!", "stoppedBeingTracked": "Removed from tracking list!",
"tlLeaderboard": "Tetra League leaderboard", "tlLeaderboard": "Tetra League leaderboard",
"noRecords": "No records", "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", "noRecord": "No record",
"botRecord": "Bots are not allowed to set records", "botRecord": "Bots are not allowed to set records",
"anonRecord": "Guests 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}", "comparingWith": "Data from ${newDate} comparing with ${oldDate}",
"top": "Top", "top": "Top",
"topRank": "Top Rank", "topRank": "Top Rank",
"verdictGeneral": "$n $verdict than $rank rank average",
"verdictBetter": "better",
"verdictWorse": "worse",
"gamesUntilRanked": "${left} games until being ranked", "gamesUntilRanked": "${left} games until being ranked",
"nerdStats": "Nerd Stats", "nerdStats": "Nerd Stats",
"playersYouTrack": "Players you track", "playersYouTrack": "Players you track",
@ -93,8 +104,14 @@
"yourIDAlertTitle": "Your nickname in TETR.IO", "yourIDAlertTitle": "Your nickname in TETR.IO",
"yourIDText": "When app loads, it will retrieve data for this account", "yourIDText": "When app loads, it will retrieve data for this account",
"language": "Language", "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", "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", "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}", "stateViewTitle": "${nickname} account on ${date}",
"statesViewTitle": "${number} states of ${nickname} account", "statesViewTitle": "${number} states of ${nickname} account",
"matchesViewTitle": "${nickname} TL matches", "matchesViewTitle": "${nickname} TL matches",
@ -271,21 +288,30 @@
"errors":{ "errors":{
"connection": "Some issue with connection: ${code} ${message}", "connection": "Some issue with connection: ${code} ${message}",
"noSuchUser": "No such user", "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", "history": "History for that player is missing",
"actionSuggestion": "Perhaps, you want to",
"p1nkl0bst3rTLmatches": "No Tetra League matches was found", "p1nkl0bst3rTLmatches": "No Tetra League matches was found",
"clientException": "No internet connection", "clientException": "No internet connection",
"forbidden": "Your IP address is blocked.\nChange IP address or reach out to osk", "forbidden": "Your IP address is blocked",
"tooManyRequests": "You have been rate limited. Try again later", "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", "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)", "internalWebVersion": "Something happend on the tetr.io side (or on oskware_bridge, idk honestly)",
"oskwareBridge": "Something happend with oskware_bridge. Let dan63047 know", "internalWebVersionSub": "If osk status page says that everything is ok, let dan63047 know about this issue",
"p1nkl0bst3rForbidden": "Third party API blocked your IP address.\nChange IP address or reach out to p1nkl0bst3r", "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", "p1nkl0bst3rTooManyRequests": "Too many requests to third party API. Try again later",
"p1nkl0bst3rinternal": "Something happend on the p1nkl0bst3r side", "p1nkl0bst3rinternal": "Something happend on the p1nkl0bst3r side",
"p1nkl0bst3rinternalWebVersion": "Something happend on the p1nkl0bst3r side (or on oskware_bridge, idk honestly)", "p1nkl0bst3rinternalWebVersion": "Something happend on the p1nkl0bst3r side (or on oskware_bridge, idk honestly)",
"replayAlreadySaved": "Replay already saved", "replayAlreadySaved": "Replay already saved",
"replayExpired": "Replay expired and not available anymore", "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)": { "countries(map)": {
"": "Not selected", "": "Not selected",

View File

@ -46,6 +46,14 @@
"compare": "Сравнить", "compare": "Сравнить",
"tlLeaderboard": "Рейтинговая таблица", "tlLeaderboard": "Рейтинговая таблица",
"noRecords": "Нет записей", "noRecords": "Нет записей",
"noOldRecords": {
"zero": "Нет записей",
"one": "Всего один матч",
"two": "Всего $n матча",
"few": "Всего $n матча",
"many": "Всего $n матчей",
"other": "$n матчей"
},
"noRecord": "Нет рекорда", "noRecord": "Нет рекорда",
"botRecord": "Ботам нельзя ставить рекорды", "botRecord": "Ботам нельзя ставить рекорды",
"anonRecord": "Гостям нельзя ставить рекорды", "anonRecord": "Гостям нельзя ставить рекорды",
@ -70,6 +78,9 @@
"comparingWith": "Данные от ${newDate} в сравнении с данными от ${oldDate}", "comparingWith": "Данные от ${newDate} в сравнении с данными от ${oldDate}",
"top": "Топ", "top": "Топ",
"topRank": "Топ Ранг", "topRank": "Топ Ранг",
"verdictGeneral": "$verdict среднего $rank ранга на $n",
"verdictBetter": "Лучше",
"verdictWorse": "Хуже",
"gamesUntilRanked": "${left} матчей до получения рейтинга", "gamesUntilRanked": "${left} матчей до получения рейтинга",
"nerdStats": "Для задротов", "nerdStats": "Для задротов",
"playersYouTrack": "Отслеживаемые игроки", "playersYouTrack": "Отслеживаемые игроки",
@ -93,8 +104,14 @@
"yourIDAlertTitle": "Ваш ник в TETR.IO", "yourIDAlertTitle": "Ваш ник в TETR.IO",
"yourIDText": "При запуске приложения оно будет получать статистику этого игрока.", "yourIDText": "При запуске приложения оно будет получать статистику этого игрока.",
"language": "Язык (Language)", "language": "Язык (Language)",
"customization": "Кастомизация",
"customizationDescription": "Здесь только один переключатель, в планах добавить больше",
"lbStats": "Показывать статистику, основанную на рейтинговой таблице",
"lbStatsDescription": "Это повлияет на время загрузки, но позволит видеть положение в рейтинге и сравнение со средними значениями по рангу по каждой стате",
"aboutApp": "О приложении", "aboutApp": "О приложении",
"aboutAppText": "${appName} (${packageName}) Версия ${version} Сборка ${buildNumber}\n\nРазработал dan63047\nФормулы предоставил kerrmunism\nИсторию предоставляет p1nkl0bst3r\nВозможность скачивать повторы из TETR.IO предоставляет szy", "aboutAppText": "${appName} (${packageName}) Версия ${version} Сборка ${buildNumber}\n\nРазработал dan63047\nФормулы предоставил kerrmunism\nИсторию предоставляет p1nkl0bst3r\nВозможность скачивать повторы из TETR.IO предоставляет szy",
"oskKagari": "\"Оск Кагари\" прикол",
"oskKagariDescription": "Если включено, вместо настоящего ранга оска будет рендерится :kagari:",
"stateViewTitle": "Аккаунт ${nickname} ${date}", "stateViewTitle": "Аккаунт ${nickname} ${date}",
"statesViewTitle": "${number} состояний аккаунта ${nickname}", "statesViewTitle": "${number} состояний аккаунта ${nickname}",
"matchesViewTitle": "Матчи аккаунта ${nickname}", "matchesViewTitle": "Матчи аккаунта ${nickname}",
@ -248,7 +265,7 @@
}, },
"tspinsTotal": { "tspinsTotal": {
"zero": "$n T-спинов всего", "zero": "$n T-спинов всего",
"one": "всего $n T-спин", "one": "Всего $n T-спин",
"two": "$n T-спина всего", "two": "$n T-спина всего",
"few": "$n T-спина всего", "few": "$n T-спина всего",
"many": "$n T-спинов всего", "many": "$n T-спинов всего",
@ -271,21 +288,30 @@
"errors":{ "errors":{
"connection": "Проблема с подключением: ${code} ${message}", "connection": "Проблема с подключением: ${code} ${message}",
"noSuchUser": "Нет такого пользователя", "noSuchUser": "Нет такого пользователя",
"noSuchUserSub": "Либо вы ошиблись при вводе, либо аккаунта больше не существует",
"discordNotAssigned": "К данному Discord ID не привязан аккаунт",
"discordNotAssignedSub": "Убедитесь в том, что вы вставили правильный ID",
"history": "История данного игрока отсутствует", "history": "История данного игрока отсутствует",
"actionSuggestion": "Возможно, вы хотите",
"p1nkl0bst3rTLmatches": "Старых матчей Тетра Лиги не было найдено", "p1nkl0bst3rTLmatches": "Старых матчей Тетра Лиги не было найдено",
"clientException": "Нет соединения с интернетом", "clientException": "Нет соединения с интернетом",
"forbidden": "Ваш IP адрес заблокирован.\nСмените IP адрес или свяжитесь с osk-ом", "forbidden": "Ваш IP адрес заблокирован",
"tooManyRequests": "Слишком много запросов. Попробуйте позже", "forbiddenSub": "Если у вас работает VPN или прокси, выключите его. Если это не помогло, свяжитесь с $nickname",
"tooManyRequests": "Слишком много запросов",
"tooManyRequestsSub": "Подождите немного и попробуйте снова",
"internal": "Что-то случилось на стороне tetr.io", "internal": "Что-то случилось на стороне tetr.io",
"internalSub": "Скорее всего, osk уже в курсе об этом",
"internalWebVersion": "Что-то случилось на стороне tetr.io (или на стороне oskware_bridge, я хз если честно)", "internalWebVersion": "Что-то случилось на стороне tetr.io (или на стороне oskware_bridge, я хз если честно)",
"oskwareBridge": "Что-то случилось с oskware_bridge. Дайте dan63047 знать", "internalWebVersionSub": "Если статус страница osk-а говорит, что всё ок - свяжитесь с dan63047",
"p1nkl0bst3rForbidden": "Стороннее API заблокировало ваш IP адрес.\nСмените IP адрес или свяжитесь с p1nkl0bst3r-ом", "oskwareBridge": "Что-то случилось с oskware_bridge",
"oskwareBridgeSub": "Дайте dan63047 знать",
"p1nkl0bst3rForbidden": "Стороннее API заблокировало ваш IP адрес",
"p1nkl0bst3rTooManyRequests": "Слишком много запросов к стороннему API. Попробуйте позже", "p1nkl0bst3rTooManyRequests": "Слишком много запросов к стороннему API. Попробуйте позже",
"p1nkl0bst3rinternal": "Что-то случилось на стороне p1nkl0bst3r-а", "p1nkl0bst3rinternal": "Что-то случилось на стороне p1nkl0bst3r-а",
"p1nkl0bst3rinternalWebVersion": "Что-то случилось на стороне p1nkl0bst3r-а (или на стороне oskware_bridge, я хз если честно)", "p1nkl0bst3rinternalWebVersion": "Что-то случилось на стороне p1nkl0bst3r-а (или на стороне oskware_bridge, я хз если честно)",
"replayAlreadySaved": "Повтор уже сохранён", "replayAlreadySaved": "Повтор уже сохранён",
"replayExpired": "Повтор истёк и больше недоступен", "replayExpired": "Повтор истёк и больше недоступен",
"replayRejected": "Стороннее API заблокировало ваш IP адрес.\nСмените IP адрес или свяжитесь с szy" "replayRejected": "Стороннее API заблокировало ваш IP адрес"
}, },
"countries(map)": { "countries(map)": {
"": "Не выбрана", "": "Не выбрана",

View File

@ -1,5 +1,4 @@
import 'dart:io'; import 'dart:io';
import 'dart:math';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';