App now can tell what happening with connection
Also now we can manage stored matches
This commit is contained in:
parent
2c4c72aa1a
commit
7ed93d3fb1
|
@ -4,9 +4,9 @@
|
||||||
/// To regenerate, run: `dart run slang`
|
/// To regenerate, run: `dart run slang`
|
||||||
///
|
///
|
||||||
/// Locales: 2
|
/// Locales: 2
|
||||||
/// Strings: 914 (457 per locale)
|
/// Strings: 940 (470 per locale)
|
||||||
///
|
///
|
||||||
/// Built on 2023-09-06 at 18:46 UTC
|
/// Built on 2023-09-23 at 18:57 UTC
|
||||||
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
|
@ -223,8 +223,11 @@ class _StringsEn implements BaseTranslations<AppLocale, _StringsEn> {
|
||||||
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';
|
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';
|
||||||
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 statesViewEntry({required Object level, required Object gameTime, required Object friends, required Object rd}) => 'Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD';
|
String statesViewEntry({required Object level, required Object gameTime, required Object friends, required Object rd}) => 'Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD';
|
||||||
String stateRemoved({required Object date}) => '${date} state was removed from database!';
|
String stateRemoved({required Object date}) => '${date} state was removed from database!';
|
||||||
|
String matchRemoved({required Object date}) => '${date} match was removed from database!';
|
||||||
|
String get viewAllMatches => 'View all matches';
|
||||||
String get trackedPlayersViewTitle => 'Stored data';
|
String get trackedPlayersViewTitle => 'Stored data';
|
||||||
String get trackedPlayersZeroEntrys => 'Empty list. Press "Track" button in previous view to add current player here';
|
String get trackedPlayersZeroEntrys => 'Empty list. Press "Track" button in previous view to add current player here';
|
||||||
String get trackedPlayersOneEntry => 'There is only one player';
|
String get trackedPlayersOneEntry => 'There is only one player';
|
||||||
|
@ -654,7 +657,17 @@ 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 socketException({required Object host, required Object message}) => 'Can\'t connect with ${host}: ${message}';
|
String get history => 'History for that player is missing';
|
||||||
|
String get clientException => 'No internet connection';
|
||||||
|
String get forbidden => 'Your IP address is blocked.\nChange IP address or reach out to osk';
|
||||||
|
String get tooManyRequests => 'You have been rate limited. Try again later';
|
||||||
|
String get internal => 'Something happend on the tetr.io side';
|
||||||
|
String get internalWebVersion => 'Something happend on the tetr.io side (or on oskware_bridge, idk honestly)';
|
||||||
|
String get oskwareBridge => 'Something happend with oskware_bridge. Let dan63047 know';
|
||||||
|
String get p1nkl0bst3rForbidden => 'Third party API blocked your IP address.\nChange IP address or reach out to p1nkl0bst3r';
|
||||||
|
String get p1nkl0bst3rTooManyRequests => 'Too many requests to third party API. Try again later';
|
||||||
|
String get p1nkl0bst3rinternal => 'Something happend on the p1nkl0bst3r side';
|
||||||
|
String get p1nkl0bst3rinternalWebVersion => 'Something happend on the p1nkl0bst3r side (or on oskware_bridge, idk honestly)';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path: <root>
|
// Path: <root>
|
||||||
|
@ -755,8 +768,11 @@ class _StringsRu implements _StringsEn {
|
||||||
@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';
|
@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';
|
||||||
@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 statesViewEntry({required Object level, required Object gameTime, required Object friends, required Object rd}) => '${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD';
|
@override String statesViewEntry({required Object level, required Object gameTime, required Object friends, required Object rd}) => '${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD';
|
||||||
@override String stateRemoved({required Object date}) => 'Состояние от ${date} было удалено из локальной базы данных!';
|
@override String stateRemoved({required Object date}) => 'Состояние от ${date} было удалено из локальной базы данных!';
|
||||||
|
@override String matchRemoved({required Object date}) => 'Матч от ${date} был удален из локальной базы данных!';
|
||||||
|
@override String get viewAllMatches => 'Все матчи';
|
||||||
@override String get trackedPlayersViewTitle => 'Сохранённые данные';
|
@override String get trackedPlayersViewTitle => 'Сохранённые данные';
|
||||||
@override String get trackedPlayersZeroEntrys => 'Пустой список. Вернитесь на предыдущий экран и нажмите кнопку "Отслеживать", чтобы текущий игрок появился здесь';
|
@override String get trackedPlayersZeroEntrys => 'Пустой список. Вернитесь на предыдущий экран и нажмите кнопку "Отслеживать", чтобы текущий игрок появился здесь';
|
||||||
@override String get trackedPlayersOneEntry => 'В списке только один игрок';
|
@override String get trackedPlayersOneEntry => 'В списке только один игрок';
|
||||||
|
@ -1186,7 +1202,17 @@ 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 socketException({required Object host, required Object message}) => 'Невозможно подключиться к ${host}: ${message}';
|
@override String get history => 'История данного игрока отсутствует';
|
||||||
|
@override String get clientException => 'Нет соединения с интернетом';
|
||||||
|
@override String get forbidden => 'Ваш IP адрес заблокирован.\nСмените IP адрес или свяжитесь с osk-ом';
|
||||||
|
@override String get tooManyRequests => 'Слишком много запросов. Попробуйте позже';
|
||||||
|
@override String get internal => 'Что-то случилось на стороне tetr.io';
|
||||||
|
@override String get internalWebVersion => 'Что-то случилось на стороне tetr.io (или на стороне oskware_bridge, я хз если честно)';
|
||||||
|
@override String get oskwareBridge => 'Что-то случилось с oskware_bridge. Дайте dan63047 знать';
|
||||||
|
@override String get p1nkl0bst3rForbidden => 'Стороннее API заблокировало ваш IP адрес.\nСмените IP адрес или свяжитесь с p1nkl0bst3r-ом';
|
||||||
|
@override String get p1nkl0bst3rTooManyRequests => 'Слишком много запросов к стороннему API. Попробуйте позже';
|
||||||
|
@override String get p1nkl0bst3rinternal => 'Что-то случилось на стороне p1nkl0bst3r-а';
|
||||||
|
@override String get p1nkl0bst3rinternalWebVersion => 'Что-то случилось на стороне p1nkl0bst3r-а (или на стороне oskware_bridge, я хз если честно)';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flat map(s) containing all translations.
|
/// Flat map(s) containing all translations.
|
||||||
|
@ -1266,8 +1292,11 @@ extension on _StringsEn {
|
||||||
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';
|
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';
|
||||||
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 'statesViewEntry': return ({required Object level, required Object gameTime, required Object friends, required Object rd}) => 'Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD';
|
case 'statesViewEntry': return ({required Object level, required Object gameTime, required Object friends, required Object rd}) => 'Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD';
|
||||||
case 'stateRemoved': return ({required Object date}) => '${date} state was removed from database!';
|
case 'stateRemoved': return ({required Object date}) => '${date} state was removed from database!';
|
||||||
|
case 'matchRemoved': return ({required Object date}) => '${date} match was removed from database!';
|
||||||
|
case 'viewAllMatches': return 'View all matches';
|
||||||
case 'trackedPlayersViewTitle': return 'Stored data';
|
case 'trackedPlayersViewTitle': return 'Stored data';
|
||||||
case 'trackedPlayersZeroEntrys': return 'Empty list. Press "Track" button in previous view to add current player here';
|
case 'trackedPlayersZeroEntrys': return 'Empty list. Press "Track" button in previous view to add current player here';
|
||||||
case 'trackedPlayersOneEntry': return 'There is only one player';
|
case 'trackedPlayersOneEntry': return 'There is only one player';
|
||||||
|
@ -1394,7 +1423,17 @@ extension on _StringsEn {
|
||||||
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.socketException': return ({required Object host, required Object message}) => 'Can\'t connect with ${host}: ${message}';
|
case 'errors.history': return 'History for that player is missing';
|
||||||
|
case 'errors.clientException': return 'No internet connection';
|
||||||
|
case 'errors.forbidden': return 'Your IP address is blocked.\nChange IP address or reach out to osk';
|
||||||
|
case 'errors.tooManyRequests': return 'You have been rate limited. Try again later';
|
||||||
|
case 'errors.internal': return 'Something happend on the tetr.io side';
|
||||||
|
case 'errors.internalWebVersion': return 'Something happend on the tetr.io side (or on oskware_bridge, idk honestly)';
|
||||||
|
case 'errors.oskwareBridge': return 'Something happend with oskware_bridge. Let dan63047 know';
|
||||||
|
case 'errors.p1nkl0bst3rForbidden': return 'Third party API blocked your IP address.\nChange IP address or reach out to p1nkl0bst3r';
|
||||||
|
case 'errors.p1nkl0bst3rTooManyRequests': return 'Too many requests to third party API. Try again later';
|
||||||
|
case 'errors.p1nkl0bst3rinternal': return 'Something happend on the p1nkl0bst3r side';
|
||||||
|
case 'errors.p1nkl0bst3rinternalWebVersion': return 'Something happend on the p1nkl0bst3r side (or on oskware_bridge, idk honestly)';
|
||||||
case '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';
|
||||||
|
@ -1733,8 +1772,11 @@ extension on _StringsRu {
|
||||||
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';
|
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';
|
||||||
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 'statesViewEntry': return ({required Object level, required Object gameTime, required Object friends, required Object rd}) => '${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD';
|
case 'statesViewEntry': return ({required Object level, required Object gameTime, required Object friends, required Object rd}) => '${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD';
|
||||||
case 'stateRemoved': return ({required Object date}) => 'Состояние от ${date} было удалено из локальной базы данных!';
|
case 'stateRemoved': return ({required Object date}) => 'Состояние от ${date} было удалено из локальной базы данных!';
|
||||||
|
case 'matchRemoved': return ({required Object date}) => 'Матч от ${date} был удален из локальной базы данных!';
|
||||||
|
case 'viewAllMatches': return 'Все матчи';
|
||||||
case 'trackedPlayersViewTitle': return 'Сохранённые данные';
|
case 'trackedPlayersViewTitle': return 'Сохранённые данные';
|
||||||
case 'trackedPlayersZeroEntrys': return 'Пустой список. Вернитесь на предыдущий экран и нажмите кнопку "Отслеживать", чтобы текущий игрок появился здесь';
|
case 'trackedPlayersZeroEntrys': return 'Пустой список. Вернитесь на предыдущий экран и нажмите кнопку "Отслеживать", чтобы текущий игрок появился здесь';
|
||||||
case 'trackedPlayersOneEntry': return 'В списке только один игрок';
|
case 'trackedPlayersOneEntry': return 'В списке только один игрок';
|
||||||
|
@ -1861,7 +1903,17 @@ 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.socketException': return ({required Object host, required Object message}) => 'Невозможно подключиться к ${host}: ${message}';
|
case 'errors.history': return 'История данного игрока отсутствует';
|
||||||
|
case 'errors.clientException': return 'Нет соединения с интернетом';
|
||||||
|
case 'errors.forbidden': return 'Ваш IP адрес заблокирован.\nСмените IP адрес или свяжитесь с osk-ом';
|
||||||
|
case 'errors.tooManyRequests': return 'Слишком много запросов. Попробуйте позже';
|
||||||
|
case 'errors.internal': return 'Что-то случилось на стороне tetr.io';
|
||||||
|
case 'errors.internalWebVersion': return 'Что-то случилось на стороне tetr.io (или на стороне oskware_bridge, я хз если честно)';
|
||||||
|
case 'errors.oskwareBridge': return 'Что-то случилось с oskware_bridge. Дайте dan63047 знать';
|
||||||
|
case 'errors.p1nkl0bst3rForbidden': return 'Стороннее API заблокировало ваш IP адрес.\nСмените IP адрес или свяжитесь с p1nkl0bst3r-ом';
|
||||||
|
case 'errors.p1nkl0bst3rTooManyRequests': return 'Слишком много запросов к стороннему API. Попробуйте позже';
|
||||||
|
case 'errors.p1nkl0bst3rinternal': return 'Что-то случилось на стороне p1nkl0bst3r-а';
|
||||||
|
case 'errors.p1nkl0bst3rinternalWebVersion': return 'Что-то случилось на стороне p1nkl0bst3r-а (или на стороне oskware_bridge, я хз если честно)';
|
||||||
case 'countries.': return 'Не выбрана';
|
case 'countries.': return 'Не выбрана';
|
||||||
case 'countries.AF': return 'Афганистан';
|
case 'countries.AF': return 'Афганистан';
|
||||||
case 'countries.AX': return 'Аландские острова';
|
case 'countries.AX': return 'Аландские острова';
|
||||||
|
|
|
@ -6,12 +6,30 @@ class UnableToGetDocuments implements Exception {}
|
||||||
|
|
||||||
class CouldNotDeletePlayer implements Exception {}
|
class CouldNotDeletePlayer implements Exception {}
|
||||||
|
|
||||||
|
class CouldNotDeleteMatch implements Exception {}
|
||||||
|
|
||||||
class CouldNotUpdatePlayer implements Exception {}
|
class CouldNotUpdatePlayer implements Exception {}
|
||||||
|
|
||||||
class TetrioPlayerAlreadyExist implements Exception {}
|
class TetrioPlayerAlreadyExist implements Exception {}
|
||||||
|
|
||||||
class TetrioPlayerNotExist implements Exception {}
|
class TetrioPlayerNotExist implements Exception {}
|
||||||
|
|
||||||
|
class TetrioHistoryNotExist implements Exception {}
|
||||||
|
|
||||||
|
class TetrioTooManyRequests implements Exception {}
|
||||||
|
|
||||||
|
class TetrioForbidden implements Exception {}
|
||||||
|
|
||||||
|
class P1nkl0bst3rTooManyRequests implements Exception {}
|
||||||
|
|
||||||
|
class P1nkl0bst3rForbidden implements Exception {}
|
||||||
|
|
||||||
|
class P1nkl0bst3rInternalProblem implements Exception {}
|
||||||
|
|
||||||
|
class TetrioOskwareBridgeProblem implements Exception {}
|
||||||
|
|
||||||
|
class TetrioInternalProblem implements Exception {}
|
||||||
|
|
||||||
class ConnectionIssue implements Exception {
|
class ConnectionIssue implements Exception {
|
||||||
const ConnectionIssue(this.code, this.message);
|
const ConnectionIssue(this.code, this.message);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
class UserAgentClient extends http.BaseClient {
|
||||||
|
final String userAgent;
|
||||||
|
final http.Client _inner;
|
||||||
|
|
||||||
|
UserAgentClient(this.userAgent, this._inner);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<http.StreamedResponse> send(http.BaseRequest request) {
|
||||||
|
request.headers['user-agent'] = userAgent;
|
||||||
|
return _inner.send(request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:developer' as developer;
|
import 'dart:developer' as developer;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:tetra_stats/services/custom_http_client.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:tetra_stats/services/crud_exceptions.dart';
|
import 'package:tetra_stats/services/crud_exceptions.dart';
|
||||||
import 'package:tetra_stats/services/sqlite_db_controller.dart';
|
import 'package:tetra_stats/services/sqlite_db_controller.dart';
|
||||||
|
@ -53,6 +54,7 @@ class TetrioService extends DB {
|
||||||
final Map<String, Map<String, dynamic>> _recordsCache = {};
|
final Map<String, Map<String, dynamic>> _recordsCache = {};
|
||||||
final Map<String, TetrioPlayersLeaderboard> _leaderboardsCache = {};
|
final Map<String, TetrioPlayersLeaderboard> _leaderboardsCache = {};
|
||||||
final Map<String, TetraLeagueAlphaStream> _tlStreamsCache = {}; // i'm trying to respect oskware api It should look something like {"cached_until": TetrioPlayer}
|
final Map<String, TetraLeagueAlphaStream> _tlStreamsCache = {}; // i'm trying to respect oskware api It should look something like {"cached_until": TetrioPlayer}
|
||||||
|
final client = UserAgentClient("Tetra Stats v1.2.3 (dm @dan63047 if someone abuse that software)", http.Client());
|
||||||
static final TetrioService _shared = TetrioService._sharedInstance();
|
static final TetrioService _shared = TetrioService._sharedInstance();
|
||||||
factory TetrioService() => _shared;
|
factory TetrioService() => _shared;
|
||||||
late final StreamController<Map<String, List<TetrioPlayer>>> _tetrioStreamController;
|
late final StreamController<Map<String, List<TetrioPlayer>>> _tetrioStreamController;
|
||||||
|
@ -109,54 +111,74 @@ class TetrioService extends DB {
|
||||||
} else {
|
} else {
|
||||||
url = Uri.https('api.p1nkl0bst3r.xyz', 'tlhist/$id');
|
url = Uri.https('api.p1nkl0bst3r.xyz', 'tlhist/$id');
|
||||||
}
|
}
|
||||||
final response = await http.get(url);
|
try{
|
||||||
if (response.statusCode == 200) {
|
final response = await client.get(url);
|
||||||
List<List<dynamic>> csv = const CsvToListConverter().convert(response.body)..removeAt(0);
|
|
||||||
List<TetrioPlayer> history = [];
|
switch (response.statusCode) {
|
||||||
String nick = await getNicknameByID(id);
|
case 200:
|
||||||
for (List<dynamic> entry in csv){
|
List<List<dynamic>> csv = const CsvToListConverter().convert(response.body)..removeAt(0);
|
||||||
TetrioPlayer state = TetrioPlayer(
|
List<TetrioPlayer> history = [];
|
||||||
userId: id,
|
String nick = await getNicknameByID(id);
|
||||||
username: nick,
|
for (List<dynamic> entry in csv){
|
||||||
role: "p1nkl0bst3r",
|
TetrioPlayer state = TetrioPlayer(
|
||||||
state: DateTime.parse(entry[9]),
|
userId: id,
|
||||||
badges: [],
|
username: nick,
|
||||||
friendCount: -1,
|
role: "p1nkl0bst3r",
|
||||||
gamesPlayed: -1,
|
state: DateTime.parse(entry[9]),
|
||||||
gamesWon: -1,
|
badges: [],
|
||||||
gameTime: const Duration(seconds: -1),
|
friendCount: -1,
|
||||||
xp: -1,
|
gamesPlayed: -1,
|
||||||
supporterTier: 0,
|
gamesWon: -1,
|
||||||
verified: false,
|
gameTime: const Duration(seconds: -1),
|
||||||
connections: null,
|
xp: -1,
|
||||||
tlSeason1: TetraLeagueAlpha(timestamp: DateTime.parse(entry[9]), apm: entry[6] != '' ? entry[6] : null, pps: entry[7] != '' ? entry[7] : null, vs: entry[8] != '' ? entry[8] : null, glicko: entry[4], rd: noTrRd, gamesPlayed: entry[1], gamesWon: entry[2], bestRank: "z", decaying: false, rating: entry[3], rank: entry[5], percentileRank: entry[5], percentile: rankCutoffs[entry[5]]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1),
|
supporterTier: 0,
|
||||||
sprint: [],
|
verified: false,
|
||||||
blitz: []
|
connections: null,
|
||||||
);
|
tlSeason1: TetraLeagueAlpha(timestamp: DateTime.parse(entry[9]), apm: entry[6] != '' ? entry[6] : null, pps: entry[7] != '' ? entry[7] : null, vs: entry[8] != '' ? entry[8] : null, glicko: entry[4], rd: noTrRd, gamesPlayed: entry[1], gamesWon: entry[2], bestRank: "z", decaying: false, rating: entry[3], rank: entry[5], percentileRank: entry[5], percentile: rankCutoffs[entry[5]]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1),
|
||||||
history.add(state);
|
sprint: [],
|
||||||
|
blitz: []
|
||||||
|
);
|
||||||
|
history.add(state);
|
||||||
|
}
|
||||||
|
await ensureDbIsOpen();
|
||||||
|
final db = getDatabaseOrThrow();
|
||||||
|
late List<TetrioPlayer> states;
|
||||||
|
try{
|
||||||
|
states = _players[id]!;
|
||||||
|
}catch(e){
|
||||||
|
var player = await fetchPlayer(id);
|
||||||
|
await createPlayer(player);
|
||||||
|
states = _players[id]!;
|
||||||
|
}
|
||||||
|
states.insertAll(0, history.reversed);
|
||||||
|
final Map<String, dynamic> statesJson = {};
|
||||||
|
for (var e in states) {
|
||||||
|
statesJson.addEntries({(e.state.millisecondsSinceEpoch ~/ 1000).toString(): e.toJson()}.entries);
|
||||||
|
}
|
||||||
|
await db.update(tetrioUsersTable, {idCol: id, nickCol: nick, statesCol: jsonEncode(statesJson)}, where: '$idCol = ?', whereArgs: [id]);
|
||||||
|
_tetrioStreamController.add(_players);
|
||||||
|
return history;
|
||||||
|
case 404:
|
||||||
|
developer.log("fetchTLHistory: Probably, history doesn't exist", name: "services/tetrio_crud", error: response.statusCode);
|
||||||
|
throw TetrioHistoryNotExist();
|
||||||
|
case 403:
|
||||||
|
throw P1nkl0bst3rForbidden();
|
||||||
|
case 429:
|
||||||
|
throw P1nkl0bst3rTooManyRequests();
|
||||||
|
case 418:
|
||||||
|
throw TetrioOskwareBridgeProblem();
|
||||||
|
case 500:
|
||||||
|
case 502:
|
||||||
|
case 503:
|
||||||
|
case 504:
|
||||||
|
throw P1nkl0bst3rInternalProblem();
|
||||||
|
default:
|
||||||
|
developer.log("fetchTLHistory: Failed to fetch history", name: "services/tetrio_crud", error: response.statusCode);
|
||||||
|
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||||
}
|
}
|
||||||
await ensureDbIsOpen();
|
} on http.ClientException catch (e, s) {
|
||||||
final db = getDatabaseOrThrow();
|
developer.log("$e, $s");
|
||||||
late List<TetrioPlayer> states;
|
throw http.ClientException(e.message, e.uri);
|
||||||
try{
|
|
||||||
states = _players[id]!;
|
|
||||||
}catch(e){
|
|
||||||
var player = await fetchPlayer(id);
|
|
||||||
await createPlayer(player);
|
|
||||||
states = _players[id]!;
|
|
||||||
}
|
|
||||||
states.insertAll(0, history.reversed);
|
|
||||||
final Map<String, dynamic> statesJson = {};
|
|
||||||
for (var e in states) {
|
|
||||||
statesJson.addEntries({(e.state.millisecondsSinceEpoch ~/ 1000).toString(): e.toJson()}.entries);
|
|
||||||
}
|
|
||||||
await db.update(tetrioUsersTable, {idCol: id, nickCol: nick, statesCol: jsonEncode(statesJson)}, where: '$idCol = ?', whereArgs: [id]);
|
|
||||||
_tetrioStreamController.add(_players);
|
|
||||||
return history;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
developer.log("fetchTLHistory: Probably, history doesn't exist", name: "services/tetrio_crud", error: response.statusCode);
|
|
||||||
throw Exception('Failed to fetch player');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,21 +201,39 @@ class TetrioService extends DB {
|
||||||
} else {
|
} else {
|
||||||
url = Uri.https('ch.tetr.io', 'api/users/lists/league/all');
|
url = Uri.https('ch.tetr.io', 'api/users/lists/league/all');
|
||||||
}
|
}
|
||||||
final response = await http.get(url);
|
try{
|
||||||
if (response.statusCode == 200) {
|
final response = await client.get(url);
|
||||||
var rawJson = jsonDecode(response.body);
|
|
||||||
if (rawJson['success']) {
|
switch (response.statusCode) {
|
||||||
TetrioPlayersLeaderboard leaderboard = TetrioPlayersLeaderboard.fromJson(rawJson['data']['users'], "league", DateTime.fromMillisecondsSinceEpoch(rawJson['cache']['cached_at']));
|
case 200:
|
||||||
developer.log("fetchTLLeaderboard: Leaderboard retrieved and cached", name: "services/tetrio_crud");
|
var rawJson = jsonDecode(response.body);
|
||||||
_leaderboardsCache[rawJson['cache']['cached_until'].toString()] = leaderboard;
|
if (rawJson['success']) {
|
||||||
return leaderboard;
|
TetrioPlayersLeaderboard leaderboard = TetrioPlayersLeaderboard.fromJson(rawJson['data']['users'], "league", DateTime.fromMillisecondsSinceEpoch(rawJson['cache']['cached_at']));
|
||||||
} else {
|
developer.log("fetchTLLeaderboard: Leaderboard retrieved and cached", name: "services/tetrio_crud");
|
||||||
developer.log("fetchTLLeaderboard: Bruh", name: "services/tetrio_crud", error: rawJson);
|
_leaderboardsCache[rawJson['cache']['cached_until'].toString()] = leaderboard;
|
||||||
throw Exception("User doesn't exist");
|
return leaderboard;
|
||||||
|
} else {
|
||||||
|
developer.log("fetchTLLeaderboard: Bruh", name: "services/tetrio_crud", error: rawJson);
|
||||||
|
throw Exception("Failed to get leaderboard (problems on the tetr.io side)");
|
||||||
|
}
|
||||||
|
case 403:
|
||||||
|
throw TetrioForbidden();
|
||||||
|
case 429:
|
||||||
|
throw TetrioTooManyRequests();
|
||||||
|
case 418:
|
||||||
|
throw TetrioOskwareBridgeProblem();
|
||||||
|
case 500:
|
||||||
|
case 502:
|
||||||
|
case 503:
|
||||||
|
case 504:
|
||||||
|
throw TetrioInternalProblem();
|
||||||
|
default:
|
||||||
|
developer.log("fetchTLLeaderboard: Failed to fetch leaderboard", name: "services/tetrio_crud", error: response.statusCode);
|
||||||
|
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||||
}
|
}
|
||||||
} else {
|
} on http.ClientException catch (e, s) {
|
||||||
developer.log("fetchTLLeaderboard: Failed to fetch leaderboard", name: "services/tetrio_crud", error: response.statusCode);
|
developer.log("$e, $s");
|
||||||
throw Exception('Failed to fetch player');
|
throw http.ClientException(e.message, e.uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,22 +257,38 @@ class TetrioService extends DB {
|
||||||
} else {
|
} else {
|
||||||
url = Uri.https('ch.tetr.io', 'api/streams/league_userrecent_${userID.toLowerCase().trim()}');
|
url = Uri.https('ch.tetr.io', 'api/streams/league_userrecent_${userID.toLowerCase().trim()}');
|
||||||
}
|
}
|
||||||
final response = await http.get(url);
|
try {
|
||||||
|
final response = await client.get(url);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
switch (response.statusCode) {
|
||||||
if (jsonDecode(response.body)['success']) {
|
case 200:
|
||||||
TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(
|
if (jsonDecode(response.body)['success']) {
|
||||||
jsonDecode(response.body)['data']['records'], userID);
|
TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(jsonDecode(response.body)['data']['records'], userID);
|
||||||
developer.log("getTLStream: $userID stream retrieved and cached", name: "services/tetrio_crud");
|
developer.log("getTLStream: $userID stream retrieved and cached", name: "services/tetrio_crud");
|
||||||
_tlStreamsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = stream;
|
_tlStreamsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = stream;
|
||||||
return stream;
|
return stream;
|
||||||
} else {
|
} else {
|
||||||
developer.log("getTLStream User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
developer.log("getTLStream User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||||
throw Exception("User doesn't exist");
|
throw TetrioPlayerNotExist();
|
||||||
|
}
|
||||||
|
case 403:
|
||||||
|
throw TetrioForbidden();
|
||||||
|
case 429:
|
||||||
|
throw TetrioTooManyRequests();
|
||||||
|
case 418:
|
||||||
|
throw TetrioOskwareBridgeProblem();
|
||||||
|
case 500:
|
||||||
|
case 502:
|
||||||
|
case 503:
|
||||||
|
case 504:
|
||||||
|
throw TetrioInternalProblem();
|
||||||
|
default:
|
||||||
|
developer.log("getTLStream Failed to fetch stream", name: "services/tetrio_crud", error: response.statusCode);
|
||||||
|
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||||
}
|
}
|
||||||
} else {
|
} on http.ClientException catch (e, s) {
|
||||||
developer.log("getTLStream Failed to fetch stream", name: "services/tetrio_crud", error: response.statusCode);
|
developer.log("$e, $s");
|
||||||
throw Exception('Failed to fetch player');
|
throw http.ClientException(e.message, e.uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,6 +313,15 @@ class TetrioService extends DB {
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> deleteTLMatch(String matchID) async {
|
||||||
|
await ensureDbIsOpen();
|
||||||
|
final db = getDatabaseOrThrow();
|
||||||
|
final results = await db.delete(tetraLeagueMatchesTable, where: '$idCol = ?', whereArgs: [matchID]);
|
||||||
|
if (results != 1) {
|
||||||
|
throw CouldNotDeleteMatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> fetchRecords(String userID) async {
|
Future<Map<String, dynamic>> fetchRecords(String userID) async {
|
||||||
try{
|
try{
|
||||||
var cached = _recordsCache.entries.firstWhere((element) => element.value['user'] == userID);
|
var cached = _recordsCache.entries.firstWhere((element) => element.value['user'] == userID);
|
||||||
|
@ -277,29 +342,46 @@ class TetrioService extends DB {
|
||||||
} else {
|
} else {
|
||||||
url = Uri.https('ch.tetr.io', 'api/users/${userID.toLowerCase().trim()}/records');
|
url = Uri.https('ch.tetr.io', 'api/users/${userID.toLowerCase().trim()}/records');
|
||||||
}
|
}
|
||||||
final response = await http.get(url);
|
try{
|
||||||
|
final response = await client.get(url);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
switch (response.statusCode) {
|
||||||
if (jsonDecode(response.body)['success']) {
|
case 200:
|
||||||
Map jsonRecords = jsonDecode(response.body);
|
if (jsonDecode(response.body)['success']) {
|
||||||
var sprint = jsonRecords['data']['records']['40l']['record'] != null
|
Map jsonRecords = jsonDecode(response.body);
|
||||||
? [RecordSingle.fromJson(jsonRecords['data']['records']['40l']['record'], jsonRecords['data']['records']['40l']['rank'])]
|
var sprint = jsonRecords['data']['records']['40l']['record'] != null
|
||||||
: [];
|
? [RecordSingle.fromJson(jsonRecords['data']['records']['40l']['record'], jsonRecords['data']['records']['40l']['rank'])]
|
||||||
var blitz = jsonRecords['data']['records']['blitz']['record'] != null
|
: [];
|
||||||
? [RecordSingle.fromJson(jsonRecords['data']['records']['blitz']['record'], jsonRecords['data']['records']['blitz']['rank'])]
|
var blitz = jsonRecords['data']['records']['blitz']['record'] != null
|
||||||
: [];
|
? [RecordSingle.fromJson(jsonRecords['data']['records']['blitz']['record'], jsonRecords['data']['records']['blitz']['rank'])]
|
||||||
var zen = TetrioZen.fromJson(jsonRecords['data']['zen']);
|
: [];
|
||||||
Map<String, dynamic> map = {"user": userID.toLowerCase().trim(), "sprint": sprint, "blitz": blitz, "zen": zen};
|
var zen = TetrioZen.fromJson(jsonRecords['data']['zen']);
|
||||||
developer.log("fetchRecords: $userID records retrieved and cached", name: "services/tetrio_crud");
|
Map<String, dynamic> map = {"user": userID.toLowerCase().trim(), "sprint": sprint, "blitz": blitz, "zen": zen};
|
||||||
_recordsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = map;
|
developer.log("fetchRecords: $userID records retrieved and cached", name: "services/tetrio_crud");
|
||||||
return map;
|
_recordsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = map;
|
||||||
} else {
|
return map;
|
||||||
developer.log("fetchRecords User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
} else {
|
||||||
throw Exception("User doesn't exist");
|
developer.log("fetchRecords User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||||
|
throw TetrioPlayerNotExist();
|
||||||
|
}
|
||||||
|
case 403:
|
||||||
|
throw TetrioForbidden();
|
||||||
|
case 429:
|
||||||
|
throw TetrioTooManyRequests();
|
||||||
|
case 418:
|
||||||
|
throw TetrioOskwareBridgeProblem();
|
||||||
|
case 500:
|
||||||
|
case 502:
|
||||||
|
case 503:
|
||||||
|
case 504:
|
||||||
|
throw TetrioInternalProblem();
|
||||||
|
default:
|
||||||
|
developer.log("fetchRecords Failed to fetch records", name: "services/tetrio_crud", error: response.statusCode);
|
||||||
|
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||||
}
|
}
|
||||||
} else {
|
} on http.ClientException catch (e, s) {
|
||||||
developer.log("fetchRecords Failed to fetch records", name: "services/tetrio_crud", error: response.statusCode);
|
developer.log("$e, $s");
|
||||||
throw Exception('Failed to fetch player');
|
throw http.ClientException(e.message, e.uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,11 +414,7 @@ class TetrioService extends DB {
|
||||||
await ensureDbIsOpen();
|
await ensureDbIsOpen();
|
||||||
final db = getDatabaseOrThrow();
|
final db = getDatabaseOrThrow();
|
||||||
final results = await db.query(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
|
final results = await db.query(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
|
||||||
if (results.isEmpty) {
|
return results.isNotEmpty;
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Iterable<String>> getAllPlayerToTrack() async {
|
Future<Iterable<String>> getAllPlayerToTrack() async {
|
||||||
|
@ -437,43 +515,79 @@ class TetrioService extends DB {
|
||||||
} else {
|
} else {
|
||||||
dUrl = Uri.https('ch.tetr.io', 'api/users/search/${user.toLowerCase().trim()}');
|
dUrl = Uri.https('ch.tetr.io', 'api/users/search/${user.toLowerCase().trim()}');
|
||||||
}
|
}
|
||||||
final response = await http.get(dUrl);
|
try{
|
||||||
if (response.statusCode == 200) {
|
final response = await client.get(dUrl);
|
||||||
var json = jsonDecode(response.body);
|
|
||||||
if (json['success'] && json['data'] != null) {
|
switch (response.statusCode) {
|
||||||
user = json['data']['user']['_id'];
|
case 200:
|
||||||
} else {
|
var json = jsonDecode(response.body);
|
||||||
developer.log("fetchPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
if (json['success'] && json['data'] != null) {
|
||||||
throw TetrioPlayerNotExist();
|
user = json['data']['user']['_id'];
|
||||||
|
} else {
|
||||||
|
developer.log("fetchPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||||
|
throw TetrioPlayerNotExist();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
throw TetrioForbidden();
|
||||||
|
case 429:
|
||||||
|
throw TetrioTooManyRequests();
|
||||||
|
case 418:
|
||||||
|
throw TetrioOskwareBridgeProblem();
|
||||||
|
case 500:
|
||||||
|
case 502:
|
||||||
|
case 503:
|
||||||
|
case 504:
|
||||||
|
throw TetrioInternalProblem();
|
||||||
|
default:
|
||||||
|
developer.log("fetchPlayer Failed to fetch player", name: "services/tetrio_crud", error: response.statusCode);
|
||||||
|
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||||
}
|
}
|
||||||
} else {
|
} on http.ClientException catch (e, s) {
|
||||||
developer.log("fetchPlayer Failed to fetch player", name: "services/tetrio_crud", error: response.statusCode);
|
developer.log("$e, $s");
|
||||||
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
throw http.ClientException(e.message, e.uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Uri url;
|
Uri url;
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "tetrioUser", "user": user.toLowerCase().trim()});
|
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "tetrioUser", "user": user.toLowerCase().trim()});
|
||||||
} else {
|
} else {
|
||||||
url = Uri.https('ch.tetr.io', 'api/users/${user.toLowerCase().trim()}');
|
url = Uri.https('ch.tetr.io', 'api/users/${user.toLowerCase().trim()}');
|
||||||
}
|
}
|
||||||
final response = await http.get(url);
|
try{
|
||||||
|
final response = await client.get(url);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
switch (response.statusCode) {
|
||||||
var json = jsonDecode(response.body);
|
case 200:
|
||||||
if (json['success']) {
|
var json = jsonDecode(response.body);
|
||||||
TetrioPlayer player = TetrioPlayer.fromJson(json['data']['user'], DateTime.fromMillisecondsSinceEpoch(json['cache']['cached_at'], isUtc: true), json['data']['user']['_id'], json['data']['user']['username']);
|
if (json['success']) {
|
||||||
developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud");
|
TetrioPlayer player = TetrioPlayer.fromJson(json['data']['user'], DateTime.fromMillisecondsSinceEpoch(json['cache']['cached_at'], isUtc: true), json['data']['user']['_id'], json['data']['user']['username']);
|
||||||
_playersCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = player;
|
developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud");
|
||||||
return player;
|
_playersCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = player;
|
||||||
} else {
|
return player;
|
||||||
developer.log("fetchPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
} else {
|
||||||
throw TetrioPlayerNotExist();
|
developer.log("fetchPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||||
|
throw TetrioPlayerNotExist();
|
||||||
|
}
|
||||||
|
case 403:
|
||||||
|
throw TetrioForbidden();
|
||||||
|
case 429:
|
||||||
|
throw TetrioTooManyRequests();
|
||||||
|
case 418:
|
||||||
|
throw TetrioOskwareBridgeProblem();
|
||||||
|
case 500:
|
||||||
|
case 502:
|
||||||
|
case 503:
|
||||||
|
case 504:
|
||||||
|
throw TetrioInternalProblem();
|
||||||
|
default:
|
||||||
|
developer.log("fetchPlayer Failed to fetch player", name: "services/tetrio_crud", error: response.statusCode);
|
||||||
|
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||||
}
|
}
|
||||||
} else {
|
}on http.ClientException catch (e, s) {
|
||||||
developer.log("fetchPlayer Failed to fetch player", name: "services/tetrio_crud", error: response.statusCode);
|
developer.log("$e, $s");
|
||||||
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
throw http.ClientException(e.message, e.uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -875,7 +875,10 @@ class CompareState extends State<CompareView> {
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
] : [Text(t.compareViewNoValues(avgR: "\$avgR"))], // This is so fucked up holy shit
|
] : [Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(t.compareViewNoValues(avgR: "\$avgR"), textAlign: TextAlign.center),
|
||||||
|
)], // This is so fucked up holy shit
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:io';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
@ -31,6 +32,7 @@ const allowedHeightForPlayerBioInPixels = 30.0;
|
||||||
const givenTextHeightByScreenPercentage = 0.3;
|
const givenTextHeightByScreenPercentage = 0.3;
|
||||||
final NumberFormat timeInSec = NumberFormat("#,###.###s.");
|
final NumberFormat timeInSec = NumberFormat("#,###.###s.");
|
||||||
final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2);
|
final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2);
|
||||||
|
final NumberFormat secs = NumberFormat("00.###");
|
||||||
final NumberFormat f4 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 4);
|
final NumberFormat f4 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 4);
|
||||||
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
|
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
|
||||||
|
|
||||||
|
@ -348,19 +350,37 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
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 SocketException: // TODO: Find a way to catch
|
case P1nkl0bst3rForbidden:
|
||||||
var err = snapshot.error as SocketException;
|
errText = t.errors.p1nkl0bst3rForbidden;
|
||||||
errText = t.errors.socketException(host: err.address!.host, message: err.osError!.message);
|
break;
|
||||||
|
case P1nkl0bst3rTooManyRequests:
|
||||||
|
errText = t.errors.p1nkl0bst3rTooManyRequests;
|
||||||
|
break;
|
||||||
|
case P1nkl0bst3rInternalProblem:
|
||||||
|
errText = kIsWeb ? t.errors.p1nkl0bst3rinternalWebVersion : t.errors.p1nkl0bst3rinternal;
|
||||||
|
break;
|
||||||
|
case TetrioHistoryNotExist:
|
||||||
|
errText = t.errors.history;
|
||||||
|
break;
|
||||||
|
case TetrioForbidden:
|
||||||
|
errText = t.errors.forbidden;
|
||||||
|
break;
|
||||||
|
case TetrioTooManyRequests:
|
||||||
|
errText = t.errors.tooManyRequests;
|
||||||
|
break;
|
||||||
|
case TetrioOskwareBridgeProblem:
|
||||||
|
errText = t.errors.oskwareBridge;
|
||||||
|
break;
|
||||||
|
case TetrioInternalProblem:
|
||||||
|
errText = kIsWeb ? t.errors.internalWebVersion : t.errors.internal;
|
||||||
|
break;
|
||||||
|
case ClientException:
|
||||||
|
errText = t.errors.clientException;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
errText = snapshot.error.toString();
|
errText = snapshot.error.toString();
|
||||||
}
|
}
|
||||||
return Center(
|
return Center(child: Text(errText, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 42, fontWeight: FontWeight.bold), textAlign: TextAlign.center));
|
||||||
child: Text(errText,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontFamily: "Eurostile Round Extended",
|
|
||||||
fontSize: 42),
|
|
||||||
textAlign: TextAlign.center));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -570,7 +590,7 @@ class _History extends StatelessWidget{
|
||||||
else Center(child: Text(t.notEnoughData, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))
|
else Center(child: Text(t.notEnoughData, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
] : [Center(child: Text(t.noHistorySaved, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))]);
|
] : [Center(child: Text(t.noHistorySaved, textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,7 +670,7 @@ class _RecordThingy extends StatelessWidget {
|
||||||
fontSize: bigScreen ? 42 : 28)),
|
fontSize: bigScreen ? 42 : 28)),
|
||||||
if (record!.stream.contains("40l"))
|
if (record!.stream.contains("40l"))
|
||||||
if (record!.endContext!.finalTime.inMicroseconds > 60000000) Text(
|
if (record!.endContext!.finalTime.inMicroseconds > 60000000) Text(
|
||||||
"${(record!.endContext!.finalTime.inMicroseconds/1000000/60).floor()}:${(f2.format(record!.endContext!.finalTime.inMicroseconds /1000000 % 60))}",
|
"${(record!.endContext!.finalTime.inMicroseconds/1000000/60).floor()}:${(secs.format(record!.endContext!.finalTime.inMicroseconds /1000000 % 60))}",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: "Eurostile Round Extended",
|
fontFamily: "Eurostile Round Extended",
|
||||||
fontSize: bigScreen ? 42 : 28))
|
fontSize: bigScreen ? 42 : 28))
|
||||||
|
@ -755,194 +775,108 @@ class _RecordThingy extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Text("${t.numOfGameActions.pc}:",
|
Text("${t.numOfGameActions.pc}:", style: const TextStyle(fontSize: 24)),
|
||||||
style: const TextStyle(fontSize: 24)),
|
Text(record!.endContext!.clears.allClears.toString(), style: const TextStyle(fontSize: 24)),
|
||||||
Text(
|
],
|
||||||
record!.endContext!.clears.allClears
|
),
|
||||||
.toString(),
|
Row(
|
||||||
style: const TextStyle(fontSize: 24),
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
),
|
children: [
|
||||||
|
Text("${t.numOfGameActions.hold}:", style: const TextStyle(fontSize: 24)),
|
||||||
|
Text(record!.endContext!.holds.toString(), style: const TextStyle(fontSize: 24)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text("${t.numOfGameActions.tspinsTotal}:", style: const TextStyle(fontSize: 24)),
|
||||||
|
Text(record!.endContext!.tSpins.toString(), style: const TextStyle(fontSize: 24)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text(" - T-spin zero:", style: TextStyle(fontSize: 18)),
|
||||||
|
Text(record!.endContext!.clears.tSpinZeros.toString(), style: const TextStyle(fontSize: 18)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text(" - T-spin singles:", style: TextStyle(fontSize: 18)),
|
||||||
|
Text(record!.endContext!.clears.tSpinSingles.toString(), style: const TextStyle(fontSize: 18)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text(" - T-spin doubles:", style: TextStyle(fontSize: 18)),
|
||||||
|
Text(record!.endContext!.clears.tSpinDoubles.toString(), style: const TextStyle(fontSize: 18)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text(" - T-spin triples:", style: TextStyle(fontSize: 18)),
|
||||||
|
Text(record!.endContext!.clears.tSpinTriples.toString(), style: const TextStyle(fontSize: 18)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text(" - T-spin mini zero:", style: TextStyle(fontSize: 18)),
|
||||||
|
Text(record!.endContext!.clears.tSpinMiniZeros.toString(), style: const TextStyle(fontSize: 18)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text(" - T-spin mini singles:", style: TextStyle(fontSize: 18)),
|
||||||
|
Text(record!.endContext!.clears.tSpinMiniSingles.toString(), style: const TextStyle(fontSize: 18)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text(" - T-spin mini doubles:", style: TextStyle(fontSize: 18)),
|
||||||
|
Text(record!.endContext!.clears.tSpinMiniDoubles.toString(), style: const TextStyle(fontSize: 18)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text("${t.numOfGameActions.lineClears}:", style: const TextStyle(fontSize: 24)),
|
||||||
|
Text(record!.endContext!.lines.toString(), style: const TextStyle(fontSize: 24)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
MainAxisAlignment.spaceBetween,
|
MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text("${t.numOfGameActions.hold}:",
|
const Text(" - Singles:", style: TextStyle(fontSize: 18)),
|
||||||
style: const TextStyle(fontSize: 24)),
|
Text(record!.endContext!.clears.singles.toString(), style: const TextStyle(fontSize: 18)),
|
||||||
Text(
|
|
||||||
record!.endContext!.holds.toString(),
|
|
||||||
style: const TextStyle(fontSize: 24),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Text("${t.numOfGameActions.tspinsTotal}:",
|
const Text(" - Doubles:", style: TextStyle(fontSize: 18)),
|
||||||
style: const TextStyle(fontSize: 24)),
|
Text(record!.endContext!.clears.doubles.toString(), style: const TextStyle(fontSize: 18)),
|
||||||
Text(
|
|
||||||
record!.endContext!.tSpins.toString(),
|
|
||||||
style: const TextStyle(fontSize: 24),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
const Text(" - T-spin zero:",
|
const Text(" - Triples:", style: TextStyle(fontSize: 18)),
|
||||||
style: TextStyle(fontSize: 18)),
|
Text(record!.endContext!.clears.triples.toString(), style: const TextStyle(fontSize: 18)),
|
||||||
Text(
|
|
||||||
record!.endContext!.clears.tSpinZeros
|
|
||||||
.toString(),
|
|
||||||
style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
const Text(" - T-spin singles:",
|
const Text(" - Quads:", style: TextStyle(fontSize: 18)),
|
||||||
style: TextStyle(fontSize: 18)),
|
Text(record!.endContext!.clears.quads.toString(), style: const TextStyle(fontSize: 18)),
|
||||||
Text(
|
|
||||||
record!.endContext!.clears.tSpinSingles
|
|
||||||
.toString(),
|
|
||||||
style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
const Text(" - T-spin doubles:",
|
|
||||||
style: TextStyle(fontSize: 18)),
|
|
||||||
Text(
|
|
||||||
record!.endContext!.clears.tSpinDoubles
|
|
||||||
.toString(),
|
|
||||||
style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
const Text(" - T-spin triples:",
|
|
||||||
style: TextStyle(fontSize: 18)),
|
|
||||||
Text(
|
|
||||||
record!.endContext!.clears.tSpinTriples
|
|
||||||
.toString(),
|
|
||||||
style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
const Text(" - T-spin mini zero:",
|
|
||||||
style: TextStyle(fontSize: 18)),
|
|
||||||
Text(
|
|
||||||
record!.endContext!.clears.tSpinMiniZeros
|
|
||||||
.toString(),
|
|
||||||
style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
const Text(" - T-spin mini singles:",
|
|
||||||
style: TextStyle(fontSize: 18)),
|
|
||||||
Text(
|
|
||||||
record!.endContext!.clears.tSpinMiniSingles
|
|
||||||
.toString(),
|
|
||||||
style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
const Text(" - T-spin mini doubles:",
|
|
||||||
style: TextStyle(fontSize: 18)),
|
|
||||||
Text(
|
|
||||||
record!.endContext!.clears.tSpinMiniDoubles
|
|
||||||
.toString(),
|
|
||||||
style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text("${t.numOfGameActions.lineClears}:",
|
|
||||||
style: const TextStyle(fontSize: 24)),
|
|
||||||
Text(
|
|
||||||
record!.endContext!.lines.toString(),
|
|
||||||
style: const TextStyle(fontSize: 24),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
const Text(" - Singles:",
|
|
||||||
style: TextStyle(fontSize: 18)),
|
|
||||||
Text(
|
|
||||||
record!.endContext!.clears.singles
|
|
||||||
.toString(),
|
|
||||||
style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
const Text(" - Doubles:",
|
|
||||||
style: TextStyle(fontSize: 18)),
|
|
||||||
Text(
|
|
||||||
record!.endContext!.clears.doubles
|
|
||||||
.toString(),
|
|
||||||
style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
const Text(" - Triples:",
|
|
||||||
style: TextStyle(fontSize: 18)),
|
|
||||||
Text(
|
|
||||||
record!.endContext!.clears.triples
|
|
||||||
.toString(),
|
|
||||||
style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
const Text(" - Quads:",
|
|
||||||
style: TextStyle(fontSize: 18)),
|
|
||||||
Text(
|
|
||||||
record!.endContext!.clears.quads.toString(),
|
|
||||||
style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -951,7 +885,7 @@ class _RecordThingy extends StatelessWidget {
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
Text(t.noRecord, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28))
|
Text(t.noRecord, textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:tetra_stats/services/tetrio_crud.dart';
|
||||||
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
|
import 'package:tetra_stats/views/tl_match_view.dart';
|
||||||
|
|
||||||
|
final TetrioService teto = TetrioService();
|
||||||
|
final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2);
|
||||||
|
|
||||||
|
class MatchesView extends StatefulWidget {
|
||||||
|
final String userID;
|
||||||
|
final String username;
|
||||||
|
const MatchesView({Key? key, required this.userID, required this.username}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => MatchesState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MatchesState extends State<MatchesView> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final t = Translations.of(context);
|
||||||
|
bool bigScreen = MediaQuery.of(context).size.width > 768;
|
||||||
|
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(t.matchesViewTitle(nickname: widget.username)),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
body: SafeArea(
|
||||||
|
child: FutureBuilder(
|
||||||
|
future: teto.getTLMatchesbyPlayerID(widget.userID),
|
||||||
|
builder: (context, snapshot){
|
||||||
|
switch (snapshot.connectionState) {
|
||||||
|
case ConnectionState.none:
|
||||||
|
case ConnectionState.waiting:
|
||||||
|
case ConnectionState.active:
|
||||||
|
return const Center(child: CircularProgressIndicator(color: Colors.white));
|
||||||
|
case ConnectionState.done:
|
||||||
|
return ListView(
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
children: (snapshot.data!.isNotEmpty)
|
||||||
|
? [for (var value in snapshot.data!) ListTile(
|
||||||
|
leading: Text("${value.endContext.firstWhere((element) => element.userId == widget.userID).points} : ${value.endContext.firstWhere((element) => element.userId != widget.userID).points}",
|
||||||
|
style: bigScreen ? const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28) :
|
||||||
|
const TextStyle(fontSize: 28)),
|
||||||
|
title: Text("vs. ${value.endContext.firstWhere((element) => element.userId != widget.userID).username}"),
|
||||||
|
subtitle: Text(dateFormat.format(value.timestamp)),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(Icons.delete_forever),
|
||||||
|
onPressed: () {
|
||||||
|
DateTime nn = value.timestamp;
|
||||||
|
teto.deleteTLMatch(value.ownId).then((value) => setState(() {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.matchRemoved(date: dateFormat.format(nn)))));
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
onTap: (){Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => TlMatchResultView(record: value, initPlayerId: widget.userID),
|
||||||
|
),
|
||||||
|
);},
|
||||||
|
)]
|
||||||
|
: [Center(child: Text(t.noRecords, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -346,7 +346,7 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
||||||
_ListEntry(value: widget.rank[1]["lowestVSAPM"], label: "VS / APM", id: widget.rank[1]["lowestVSAPMid"], username: widget.rank[1]["lowestVSAPMnick"], approximate: false, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["lowestVSAPM"], label: "VS / APM", id: widget.rank[1]["lowestVSAPMid"], username: widget.rank[1]["lowestVSAPMnick"], approximate: false, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["lowestDSS"], label: t.statCellNum.dss.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestDSSid"], username: widget.rank[1]["lowestDSSnick"], approximate: false, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["lowestDSS"], label: t.statCellNum.dss.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestDSSid"], username: widget.rank[1]["lowestDSSnick"], approximate: false, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["lowestDSP"], label: t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestDSPid"], username: widget.rank[1]["lowestDSPnick"], approximate: false, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["lowestDSP"], label: t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestDSPid"], username: widget.rank[1]["lowestDSPnick"], approximate: false, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["lowestAPPDSP"], label: t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestAPPDSPid"], username: widget.rank[1]["lowestAPPDSPnick"], approximate: false, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["lowestAPPDSP"], label: t.statCellNum.appdsp.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestAPPDSPid"], username: widget.rank[1]["lowestAPPDSPnick"], approximate: false, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["lowestCheese"], label: t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestCheeseID"], username: widget.rank[1]["lowestCheeseNick"], approximate: false, fractionDigits: 2),
|
_ListEntry(value: widget.rank[1]["lowestCheese"], label: t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestCheeseID"], username: widget.rank[1]["lowestCheeseNick"], approximate: false, fractionDigits: 2),
|
||||||
_ListEntry(value: widget.rank[1]["lowestGBE"], label: t.statCellNum.gbe.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestGBEid"], username: widget.rank[1]["lowestGBEnick"], approximate: false, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["lowestGBE"], label: t.statCellNum.gbe.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestGBEid"], username: widget.rank[1]["lowestGBEnick"], approximate: false, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["lowestNyaAPP"], label: t.statCellNum.nyaapp.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestNyaAPPid"], username: widget.rank[1]["lowestNyaAPPnick"], approximate: false, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["lowestNyaAPP"], label: t.statCellNum.nyaapp.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestNyaAPPid"], username: widget.rank[1]["lowestNyaAPPnick"], approximate: false, fractionDigits: 3),
|
||||||
|
@ -380,7 +380,7 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
||||||
_ListEntry(value: widget.rank[1]["avgAPP"], label: "VS / APM", id: "", username: "", approximate: true, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["avgAPP"], label: "VS / APM", id: "", username: "", approximate: true, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["avgDSS"], label: t.statCellNum.dss.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["avgDSS"], label: t.statCellNum.dss.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["avgDSP"], label: t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["avgDSP"], label: t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["avgAPPDSP"], label: t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["avgAPPDSP"], label: t.statCellNum.appdsp.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["avgCheese"], label: t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 2),
|
_ListEntry(value: widget.rank[1]["avgCheese"], label: t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 2),
|
||||||
_ListEntry(value: widget.rank[1]["avgGBE"], label: t.statCellNum.gbe.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["avgGBE"], label: t.statCellNum.gbe.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["avgNyaAPP"], label: t.statCellNum.nyaapp.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["avgNyaAPP"], label: t.statCellNum.nyaapp.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 3),
|
||||||
|
@ -413,7 +413,7 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
||||||
_ListEntry(value: widget.rank[1]["highestVSAPM"], label: "VS / APM", id: widget.rank[1]["highestVSAPMid"], username: widget.rank[1]["highestVSAPMnick"], approximate: false, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["highestVSAPM"], label: "VS / APM", id: widget.rank[1]["highestVSAPMid"], username: widget.rank[1]["highestVSAPMnick"], approximate: false, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["highestDSS"], label: t.statCellNum.dss.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestDSSid"], username: widget.rank[1]["highestDSSnick"], approximate: false, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["highestDSS"], label: t.statCellNum.dss.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestDSSid"], username: widget.rank[1]["highestDSSnick"], approximate: false, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["highestDSP"], label: t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestDSPid"], username: widget.rank[1]["highestDSPnick"], approximate: false, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["highestDSP"], label: t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestDSPid"], username: widget.rank[1]["highestDSPnick"], approximate: false, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["highestAPPDSP"], label: t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestAPPDSPid"], username: widget.rank[1]["highestAPPDSPnick"], approximate: false, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["highestAPPDSP"], label: t.statCellNum.appdsp.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestAPPDSPid"], username: widget.rank[1]["highestAPPDSPnick"], approximate: false, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["highestCheese"], label: t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestCheeseID"], username: widget.rank[1]["highestCheeseNick"], approximate: false, fractionDigits: 2),
|
_ListEntry(value: widget.rank[1]["highestCheese"], label: t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestCheeseID"], username: widget.rank[1]["highestCheeseNick"], approximate: false, fractionDigits: 2),
|
||||||
_ListEntry(value: widget.rank[1]["highestGBE"], label: t.statCellNum.gbe.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestGBEid"], username: widget.rank[1]["highestGBEnick"], approximate: false, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["highestGBE"], label: t.statCellNum.gbe.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestGBEid"], username: widget.rank[1]["highestGBEnick"], approximate: false, fractionDigits: 3),
|
||||||
_ListEntry(value: widget.rank[1]["highestNyaAPP"], label: t.statCellNum.nyaapp.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestNyaAPPid"], username: widget.rank[1]["highestNyaAPPnick"], approximate: false, fractionDigits: 3),
|
_ListEntry(value: widget.rank[1]["highestNyaAPP"], label: t.statCellNum.nyaapp.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestNyaAPPid"], username: widget.rank[1]["highestNyaAPPnick"], approximate: false, fractionDigits: 3),
|
||||||
|
|
|
@ -44,12 +44,14 @@ class RanksAverages extends State<RankAveragesView> {
|
||||||
subtitle: Text("${f2.format(averages[keys[index]]?[0].apm)} APM, ${f2.format(averages[keys[index]]?[0].pps)} PPS, ${f2.format(averages[keys[index]]?[0].vs)} VS, ${f2.format(averages[keys[index]]?[0].nerdStats.app)} APP, ${f2.format(averages[keys[index]]?[0].nerdStats.vsapm)} VS/APM"),
|
subtitle: Text("${f2.format(averages[keys[index]]?[0].apm)} APM, ${f2.format(averages[keys[index]]?[0].pps)} PPS, ${f2.format(averages[keys[index]]?[0].vs)} VS, ${f2.format(averages[keys[index]]?[0].nerdStats.app)} APP, ${f2.format(averages[keys[index]]?[0].nerdStats.vsapm)} VS/APM"),
|
||||||
trailing: Text("${f2.format(averages[keys[index]]?[1]["toEnterTR"])} TR", style: bigScreen ? const TextStyle(fontSize: 28) : null),
|
trailing: Text("${f2.format(averages[keys[index]]?[1]["toEnterTR"])} TR", style: bigScreen ? const TextStyle(fontSize: 28) : null),
|
||||||
onTap: (){
|
onTap: (){
|
||||||
Navigator.push(
|
if (averages[keys[index]]?[1]["players"] > 0) {
|
||||||
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => RankView(rank: averages[keys[index]]!),
|
builder: (context) => RankView(rank: averages[keys[index]]!),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||||
import 'package:tetra_stats/gen/strings.g.dart';
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
import 'package:tetra_stats/views/compare_view.dart';
|
import 'package:tetra_stats/views/mathes_view.dart';
|
||||||
import 'package:tetra_stats/views/state_view.dart';
|
import 'package:tetra_stats/views/state_view.dart';
|
||||||
|
|
||||||
class StatesView extends StatefulWidget {
|
class StatesView extends StatefulWidget {
|
||||||
|
@ -21,6 +21,17 @@ class StatesState extends State<StatesView> {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(t.statesViewTitle(number: widget.states.length, nickname: widget.states.last.username.toUpperCase())),
|
title: Text(t.statesViewTitle(number: widget.states.length, nickname: widget.states.last.username.toUpperCase())),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: (){
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => MatchesView(userID: widget.states.first.userId, username: widget.states.first.username),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}, icon: const Icon(Icons.list), tooltip: t.viewAllMatches)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
|
|
28
pubspec.lock
28
pubspec.lock
|
@ -21,10 +21,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "49b1fad315e57ab0bbc15bcbb874e83116a1d78f77ebd500a4af6c9407d6b28e"
|
sha256: "1227dc3efc4ea571eebb2dfb814506ed2cfb1d4b1b89fb918abdddde617ead3c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.8"
|
version: "3.4.0"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -229,10 +229,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file_selector_macos
|
name: file_selector_macos
|
||||||
sha256: "182c3f8350cee659f7b115e956047ee3dc672a96665883a545e81581b9a82c72"
|
sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.3+2"
|
version: "0.9.3+3"
|
||||||
file_selector_platform_interface:
|
file_selector_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -361,10 +361,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf
|
sha256: "6e703d5e2f8c63fb31a77753915c1ec8baebde8088844e0d29f71b8f0b108888"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.17"
|
version: "4.1.0"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -782,10 +782,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: sqlite3_flutter_libs
|
name: sqlite3_flutter_libs
|
||||||
sha256: fb115050b0c2589afe2085a62d77f5deda4db65db20a5c65a6e0c92fda89b45e
|
sha256: "11a41f380fbcbda5bbba03ddcdbe0545e46094ab043783c46c70e8335831df03"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.16"
|
version: "0.5.17"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -814,18 +814,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: syncfusion_flutter_core
|
name: syncfusion_flutter_core
|
||||||
sha256: "2baf60cd245a21a7069f036bbca1ca222633d38f57748e133da97a305712627c"
|
sha256: aea119c8117953fa5decf4a313b431e556b0959cd35ff88f8fbdc0eda9bedb06
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "22.2.11"
|
version: "23.1.36"
|
||||||
syncfusion_flutter_gauges:
|
syncfusion_flutter_gauges:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: syncfusion_flutter_gauges
|
name: syncfusion_flutter_gauges
|
||||||
sha256: c086f17e84452e809b12f9832763ec4cea347b9f6e1e662a0e8addabca6cc2e5
|
sha256: ae46df959f60f0fed6a8c86c8c971883ed790450f8d32f546dc8a02cb4500cbd
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "22.2.11"
|
version: "23.1.36"
|
||||||
synchronized:
|
synchronized:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1014,10 +1014,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: "9e82a402b7f3d518fb9c02d0e9ae45952df31b9bf34d77baf19da2de03fc2aaa"
|
sha256: c97defd418eef4ec88c0d1652cdce84b9f7b63dd7198e266d06ac1710d527067
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.7"
|
version: "5.0.8"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -2,7 +2,7 @@ name: tetra_stats
|
||||||
description: Track your and other player stats in TETR.IO
|
description: Track your and other player stats in TETR.IO
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 1.2.2+10
|
version: 1.2.3+11
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.19.6 <3.0.0'
|
sdk: '>=2.19.6 <3.0.0'
|
||||||
|
@ -31,7 +31,7 @@ dependencies:
|
||||||
package_info_plus: ^4.0.2
|
package_info_plus: ^4.0.2
|
||||||
shared_preferences: ^2.1.1
|
shared_preferences: ^2.1.1
|
||||||
intl: ^0.18.0
|
intl: ^0.18.0
|
||||||
syncfusion_flutter_gauges: ^22.1.34
|
syncfusion_flutter_gauges: ^23.1.36
|
||||||
file_selector: ^1.0.1
|
file_selector: ^1.0.1
|
||||||
file_picker: ^5.3.2
|
file_picker: ^5.3.2
|
||||||
slang: ^3.20.0
|
slang: ^3.20.0
|
||||||
|
|
|
@ -72,8 +72,11 @@
|
||||||
"aboutAppText": "${appName} (${packageName}) Version ${version} Build ${buildNumber}\n\nDeveloped by dan63047\nFormulas provided by kerrmunism\nHistory provided by p1nkl0bst3r",
|
"aboutAppText": "${appName} (${packageName}) Version ${version} Build ${buildNumber}\n\nDeveloped by dan63047\nFormulas provided by kerrmunism\nHistory provided by p1nkl0bst3r",
|
||||||
"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",
|
||||||
"statesViewEntry": "Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD",
|
"statesViewEntry": "Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD",
|
||||||
"stateRemoved": "${date} state was removed from database!",
|
"stateRemoved": "${date} state was removed from database!",
|
||||||
|
"matchRemoved": "${date} match was removed from database!",
|
||||||
|
"viewAllMatches": "View all matches",
|
||||||
"trackedPlayersViewTitle": "Stored data",
|
"trackedPlayersViewTitle": "Stored data",
|
||||||
"trackedPlayersZeroEntrys": "Empty list. Press \"Track\" button in previous view to add current player here",
|
"trackedPlayersZeroEntrys": "Empty list. Press \"Track\" button in previous view to add current player here",
|
||||||
"trackedPlayersOneEntry": "There is only one player",
|
"trackedPlayersOneEntry": "There is only one player",
|
||||||
|
@ -209,7 +212,17 @@
|
||||||
"errors":{
|
"errors":{
|
||||||
"connection": "Some issue with connection: ${code} ${message}",
|
"connection": "Some issue with connection: ${code} ${message}",
|
||||||
"noSuchUser": "No such user",
|
"noSuchUser": "No such user",
|
||||||
"socketException": "Can't connect with ${host}: ${message}"
|
"history": "History for that player is missing",
|
||||||
|
"clientException": "No internet connection",
|
||||||
|
"forbidden": "Your IP address is blocked.\nChange IP address or reach out to osk",
|
||||||
|
"tooManyRequests": "You have been rate limited. Try again later",
|
||||||
|
"internal": "Something happend on the tetr.io side",
|
||||||
|
"internalWebVersion": "Something happend on the tetr.io side (or on oskware_bridge, idk honestly)",
|
||||||
|
"oskwareBridge": "Something happend with oskware_bridge. Let dan63047 know",
|
||||||
|
"p1nkl0bst3rForbidden": "Third party API blocked your IP address.\nChange IP address or reach out to p1nkl0bst3r",
|
||||||
|
"p1nkl0bst3rTooManyRequests": "Too many requests to third party API. Try again later",
|
||||||
|
"p1nkl0bst3rinternal": "Something happend on the p1nkl0bst3r side",
|
||||||
|
"p1nkl0bst3rinternalWebVersion": "Something happend on the p1nkl0bst3r side (or on oskware_bridge, idk honestly)"
|
||||||
},
|
},
|
||||||
"countries(map)": {
|
"countries(map)": {
|
||||||
"": "Not selected",
|
"": "Not selected",
|
||||||
|
|
|
@ -72,8 +72,11 @@
|
||||||
"aboutAppText": "${appName} (${packageName}) Версия ${version} Сборка ${buildNumber}\n\nРазработал dan63047\nФормулы предоставил kerrmunism\nИсторию предоставляет p1nkl0bst3r",
|
"aboutAppText": "${appName} (${packageName}) Версия ${version} Сборка ${buildNumber}\n\nРазработал dan63047\nФормулы предоставил kerrmunism\nИсторию предоставляет p1nkl0bst3r",
|
||||||
"stateViewTitle": "Аккаунт ${nickname} ${date}",
|
"stateViewTitle": "Аккаунт ${nickname} ${date}",
|
||||||
"statesViewTitle": "${number} состояний аккаунта ${nickname}",
|
"statesViewTitle": "${number} состояний аккаунта ${nickname}",
|
||||||
|
"matchesViewTitle": "Матчи аккаунта ${nickname}",
|
||||||
"statesViewEntry": "${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD",
|
"statesViewEntry": "${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD",
|
||||||
"stateRemoved": "Состояние от ${date} было удалено из локальной базы данных!",
|
"stateRemoved": "Состояние от ${date} было удалено из локальной базы данных!",
|
||||||
|
"matchRemoved": "Матч от ${date} был удален из локальной базы данных!",
|
||||||
|
"viewAllMatches": "Все матчи",
|
||||||
"trackedPlayersViewTitle": "Сохранённые данные",
|
"trackedPlayersViewTitle": "Сохранённые данные",
|
||||||
"trackedPlayersZeroEntrys": "Пустой список. Вернитесь на предыдущий экран и нажмите кнопку \"Отслеживать\", чтобы текущий игрок появился здесь",
|
"trackedPlayersZeroEntrys": "Пустой список. Вернитесь на предыдущий экран и нажмите кнопку \"Отслеживать\", чтобы текущий игрок появился здесь",
|
||||||
"trackedPlayersOneEntry": "В списке только один игрок",
|
"trackedPlayersOneEntry": "В списке только один игрок",
|
||||||
|
@ -209,7 +212,17 @@
|
||||||
"errors":{
|
"errors":{
|
||||||
"connection": "Проблема с подключением: ${code} ${message}",
|
"connection": "Проблема с подключением: ${code} ${message}",
|
||||||
"noSuchUser": "Нет такого пользователя",
|
"noSuchUser": "Нет такого пользователя",
|
||||||
"socketException": "Невозможно подключиться к ${host}: ${message}"
|
"history": "История данного игрока отсутствует",
|
||||||
|
"clientException": "Нет соединения с интернетом",
|
||||||
|
"forbidden": "Ваш IP адрес заблокирован.\nСмените IP адрес или свяжитесь с osk-ом",
|
||||||
|
"tooManyRequests": "Слишком много запросов. Попробуйте позже",
|
||||||
|
"internal": "Что-то случилось на стороне tetr.io",
|
||||||
|
"internalWebVersion": "Что-то случилось на стороне tetr.io (или на стороне oskware_bridge, я хз если честно)",
|
||||||
|
"oskwareBridge": "Что-то случилось с oskware_bridge. Дайте dan63047 знать",
|
||||||
|
"p1nkl0bst3rForbidden": "Стороннее API заблокировало ваш IP адрес.\nСмените IP адрес или свяжитесь с p1nkl0bst3r-ом",
|
||||||
|
"p1nkl0bst3rTooManyRequests": "Слишком много запросов к стороннему API. Попробуйте позже",
|
||||||
|
"p1nkl0bst3rinternal": "Что-то случилось на стороне p1nkl0bst3r-а",
|
||||||
|
"p1nkl0bst3rinternalWebVersion": "Что-то случилось на стороне p1nkl0bst3r-а (или на стороне oskware_bridge, я хз если честно)"
|
||||||
},
|
},
|
||||||
"countries(map)": {
|
"countries(map)": {
|
||||||
"": "Не выбрана",
|
"": "Не выбрана",
|
||||||
|
|
Loading…
Reference in New Issue