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`
|
||||
///
|
||||
/// 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
|
||||
// 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 stateViewTitle({required Object nickname, required Object date}) => '${nickname} account on ${date}';
|
||||
String statesViewTitle({required Object number, required Object nickname}) => '${number} states of ${nickname} account';
|
||||
String matchesViewTitle({required Object nickname}) => '${nickname} TL matches';
|
||||
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 matchRemoved({required Object date}) => '${date} match was removed from database!';
|
||||
String get viewAllMatches => 'View all matches';
|
||||
String get trackedPlayersViewTitle => 'Stored data';
|
||||
String get trackedPlayersZeroEntrys => 'Empty list. Press "Track" button in previous view to add current player here';
|
||||
String get trackedPlayersOneEntry => 'There is only one player';
|
||||
|
@ -654,7 +657,17 @@ class _StringsErrorsEn {
|
|||
// Translations
|
||||
String connection({required Object code, required Object message}) => 'Some issue with connection: ${code} ${message}';
|
||||
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>
|
||||
|
@ -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 stateViewTitle({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}';
|
||||
@override String statesViewTitle({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}';
|
||||
@override String matchesViewTitle({required Object nickname}) => 'Матчи аккаунта ${nickname}';
|
||||
@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 matchRemoved({required Object date}) => 'Матч от ${date} был удален из локальной базы данных!';
|
||||
@override String get viewAllMatches => 'Все матчи';
|
||||
@override String get trackedPlayersViewTitle => 'Сохранённые данные';
|
||||
@override String get trackedPlayersZeroEntrys => 'Пустой список. Вернитесь на предыдущий экран и нажмите кнопку "Отслеживать", чтобы текущий игрок появился здесь';
|
||||
@override String get trackedPlayersOneEntry => 'В списке только один игрок';
|
||||
|
@ -1186,7 +1202,17 @@ class _StringsErrorsRu implements _StringsErrorsEn {
|
|||
// Translations
|
||||
@override String connection({required Object code, required Object message}) => 'Проблема с подключением: ${code} ${message}';
|
||||
@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.
|
||||
|
@ -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 'stateViewTitle': return ({required Object nickname, required Object date}) => '${nickname} account on ${date}';
|
||||
case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} states of ${nickname} account';
|
||||
case 'matchesViewTitle': return ({required Object nickname}) => '${nickname} TL matches';
|
||||
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 'matchRemoved': return ({required Object date}) => '${date} match was removed from database!';
|
||||
case 'viewAllMatches': return 'View all matches';
|
||||
case 'trackedPlayersViewTitle': return 'Stored data';
|
||||
case 'trackedPlayersZeroEntrys': return 'Empty list. Press "Track" button in previous view to add current player here';
|
||||
case 'trackedPlayersOneEntry': return 'There is only one player';
|
||||
|
@ -1394,7 +1423,17 @@ extension on _StringsEn {
|
|||
case 'popupActions.ok': return 'OK';
|
||||
case 'errors.connection': return ({required Object code, required Object message}) => 'Some issue with connection: ${code} ${message}';
|
||||
case 'errors.noSuchUser': return 'No such user';
|
||||
case 'errors.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.AF': return 'Afghanistan';
|
||||
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 'stateViewTitle': return ({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}';
|
||||
case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}';
|
||||
case 'matchesViewTitle': return ({required Object nickname}) => 'Матчи аккаунта ${nickname}';
|
||||
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 'matchRemoved': return ({required Object date}) => 'Матч от ${date} был удален из локальной базы данных!';
|
||||
case 'viewAllMatches': return 'Все матчи';
|
||||
case 'trackedPlayersViewTitle': return 'Сохранённые данные';
|
||||
case 'trackedPlayersZeroEntrys': return 'Пустой список. Вернитесь на предыдущий экран и нажмите кнопку "Отслеживать", чтобы текущий игрок появился здесь';
|
||||
case 'trackedPlayersOneEntry': return 'В списке только один игрок';
|
||||
|
@ -1861,7 +1903,17 @@ extension on _StringsRu {
|
|||
case 'popupActions.ok': return 'OK';
|
||||
case 'errors.connection': return ({required Object code, required Object message}) => 'Проблема с подключением: ${code} ${message}';
|
||||
case 'errors.noSuchUser': return 'Нет такого пользователя';
|
||||
case 'errors.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.AF': return 'Афганистан';
|
||||
case 'countries.AX': return 'Аландские острова';
|
||||
|
|
|
@ -6,12 +6,30 @@ class UnableToGetDocuments implements Exception {}
|
|||
|
||||
class CouldNotDeletePlayer implements Exception {}
|
||||
|
||||
class CouldNotDeleteMatch implements Exception {}
|
||||
|
||||
class CouldNotUpdatePlayer implements Exception {}
|
||||
|
||||
class TetrioPlayerAlreadyExist 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 {
|
||||
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:developer' as developer;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:tetra_stats/services/custom_http_client.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:tetra_stats/services/crud_exceptions.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, TetrioPlayersLeaderboard> _leaderboardsCache = {};
|
||||
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();
|
||||
factory TetrioService() => _shared;
|
||||
late final StreamController<Map<String, List<TetrioPlayer>>> _tetrioStreamController;
|
||||
|
@ -109,54 +111,74 @@ class TetrioService extends DB {
|
|||
} else {
|
||||
url = Uri.https('api.p1nkl0bst3r.xyz', 'tlhist/$id');
|
||||
}
|
||||
final response = await http.get(url);
|
||||
if (response.statusCode == 200) {
|
||||
List<List<dynamic>> csv = const CsvToListConverter().convert(response.body)..removeAt(0);
|
||||
List<TetrioPlayer> history = [];
|
||||
String nick = await getNicknameByID(id);
|
||||
for (List<dynamic> entry in csv){
|
||||
TetrioPlayer state = TetrioPlayer(
|
||||
userId: id,
|
||||
username: nick,
|
||||
role: "p1nkl0bst3r",
|
||||
state: DateTime.parse(entry[9]),
|
||||
badges: [],
|
||||
friendCount: -1,
|
||||
gamesPlayed: -1,
|
||||
gamesWon: -1,
|
||||
gameTime: const Duration(seconds: -1),
|
||||
xp: -1,
|
||||
supporterTier: 0,
|
||||
verified: false,
|
||||
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),
|
||||
sprint: [],
|
||||
blitz: []
|
||||
);
|
||||
history.add(state);
|
||||
try{
|
||||
final response = await client.get(url);
|
||||
|
||||
switch (response.statusCode) {
|
||||
case 200:
|
||||
List<List<dynamic>> csv = const CsvToListConverter().convert(response.body)..removeAt(0);
|
||||
List<TetrioPlayer> history = [];
|
||||
String nick = await getNicknameByID(id);
|
||||
for (List<dynamic> entry in csv){
|
||||
TetrioPlayer state = TetrioPlayer(
|
||||
userId: id,
|
||||
username: nick,
|
||||
role: "p1nkl0bst3r",
|
||||
state: DateTime.parse(entry[9]),
|
||||
badges: [],
|
||||
friendCount: -1,
|
||||
gamesPlayed: -1,
|
||||
gamesWon: -1,
|
||||
gameTime: const Duration(seconds: -1),
|
||||
xp: -1,
|
||||
supporterTier: 0,
|
||||
verified: false,
|
||||
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),
|
||||
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();
|
||||
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;
|
||||
}
|
||||
else {
|
||||
developer.log("fetchTLHistory: Probably, history doesn't exist", name: "services/tetrio_crud", error: response.statusCode);
|
||||
throw Exception('Failed to fetch player');
|
||||
} on http.ClientException catch (e, s) {
|
||||
developer.log("$e, $s");
|
||||
throw http.ClientException(e.message, e.uri);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,21 +201,39 @@ class TetrioService extends DB {
|
|||
} else {
|
||||
url = Uri.https('ch.tetr.io', 'api/users/lists/league/all');
|
||||
}
|
||||
final response = await http.get(url);
|
||||
if (response.statusCode == 200) {
|
||||
var rawJson = jsonDecode(response.body);
|
||||
if (rawJson['success']) {
|
||||
TetrioPlayersLeaderboard leaderboard = TetrioPlayersLeaderboard.fromJson(rawJson['data']['users'], "league", DateTime.fromMillisecondsSinceEpoch(rawJson['cache']['cached_at']));
|
||||
developer.log("fetchTLLeaderboard: Leaderboard retrieved and cached", name: "services/tetrio_crud");
|
||||
_leaderboardsCache[rawJson['cache']['cached_until'].toString()] = leaderboard;
|
||||
return leaderboard;
|
||||
} else {
|
||||
developer.log("fetchTLLeaderboard: Bruh", name: "services/tetrio_crud", error: rawJson);
|
||||
throw Exception("User doesn't exist");
|
||||
try{
|
||||
final response = await client.get(url);
|
||||
|
||||
switch (response.statusCode) {
|
||||
case 200:
|
||||
var rawJson = jsonDecode(response.body);
|
||||
if (rawJson['success']) {
|
||||
TetrioPlayersLeaderboard leaderboard = TetrioPlayersLeaderboard.fromJson(rawJson['data']['users'], "league", DateTime.fromMillisecondsSinceEpoch(rawJson['cache']['cached_at']));
|
||||
developer.log("fetchTLLeaderboard: Leaderboard retrieved and cached", name: "services/tetrio_crud");
|
||||
_leaderboardsCache[rawJson['cache']['cached_until'].toString()] = leaderboard;
|
||||
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 {
|
||||
developer.log("fetchTLLeaderboard: Failed to fetch leaderboard", name: "services/tetrio_crud", error: response.statusCode);
|
||||
throw Exception('Failed to fetch player');
|
||||
} on http.ClientException catch (e, s) {
|
||||
developer.log("$e, $s");
|
||||
throw http.ClientException(e.message, e.uri);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,22 +257,38 @@ class TetrioService extends DB {
|
|||
} else {
|
||||
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) {
|
||||
if (jsonDecode(response.body)['success']) {
|
||||
TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(
|
||||
jsonDecode(response.body)['data']['records'], userID);
|
||||
developer.log("getTLStream: $userID stream retrieved and cached", name: "services/tetrio_crud");
|
||||
_tlStreamsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = stream;
|
||||
return stream;
|
||||
} else {
|
||||
developer.log("getTLStream User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||
throw Exception("User doesn't exist");
|
||||
switch (response.statusCode) {
|
||||
case 200:
|
||||
if (jsonDecode(response.body)['success']) {
|
||||
TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(jsonDecode(response.body)['data']['records'], userID);
|
||||
developer.log("getTLStream: $userID stream retrieved and cached", name: "services/tetrio_crud");
|
||||
_tlStreamsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = stream;
|
||||
return stream;
|
||||
} else {
|
||||
developer.log("getTLStream 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("getTLStream Failed to fetch stream", name: "services/tetrio_crud", error: response.statusCode);
|
||||
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||
}
|
||||
} else {
|
||||
developer.log("getTLStream Failed to fetch stream", name: "services/tetrio_crud", error: response.statusCode);
|
||||
throw Exception('Failed to fetch player');
|
||||
} on http.ClientException catch (e, s) {
|
||||
developer.log("$e, $s");
|
||||
throw http.ClientException(e.message, e.uri);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -257,6 +313,15 @@ class TetrioService extends DB {
|
|||
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 {
|
||||
try{
|
||||
var cached = _recordsCache.entries.firstWhere((element) => element.value['user'] == userID);
|
||||
|
@ -277,29 +342,46 @@ class TetrioService extends DB {
|
|||
} else {
|
||||
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) {
|
||||
if (jsonDecode(response.body)['success']) {
|
||||
Map jsonRecords = jsonDecode(response.body);
|
||||
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 zen = TetrioZen.fromJson(jsonRecords['data']['zen']);
|
||||
Map<String, dynamic> map = {"user": userID.toLowerCase().trim(), "sprint": sprint, "blitz": blitz, "zen": zen};
|
||||
developer.log("fetchRecords: $userID records retrieved and cached", name: "services/tetrio_crud");
|
||||
_recordsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = map;
|
||||
return map;
|
||||
} else {
|
||||
developer.log("fetchRecords User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||
throw Exception("User doesn't exist");
|
||||
switch (response.statusCode) {
|
||||
case 200:
|
||||
if (jsonDecode(response.body)['success']) {
|
||||
Map jsonRecords = jsonDecode(response.body);
|
||||
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 zen = TetrioZen.fromJson(jsonRecords['data']['zen']);
|
||||
Map<String, dynamic> map = {"user": userID.toLowerCase().trim(), "sprint": sprint, "blitz": blitz, "zen": zen};
|
||||
developer.log("fetchRecords: $userID records retrieved and cached", name: "services/tetrio_crud");
|
||||
_recordsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = map;
|
||||
return map;
|
||||
} else {
|
||||
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 {
|
||||
developer.log("fetchRecords Failed to fetch records", name: "services/tetrio_crud", error: response.statusCode);
|
||||
throw Exception('Failed to fetch player');
|
||||
} on http.ClientException catch (e, s) {
|
||||
developer.log("$e, $s");
|
||||
throw http.ClientException(e.message, e.uri);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,11 +414,7 @@ class TetrioService extends DB {
|
|||
await ensureDbIsOpen();
|
||||
final db = getDatabaseOrThrow();
|
||||
final results = await db.query(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
|
||||
if (results.isEmpty) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return results.isNotEmpty;
|
||||
}
|
||||
|
||||
Future<Iterable<String>> getAllPlayerToTrack() async {
|
||||
|
@ -437,43 +515,79 @@ class TetrioService extends DB {
|
|||
} else {
|
||||
dUrl = Uri.https('ch.tetr.io', 'api/users/search/${user.toLowerCase().trim()}');
|
||||
}
|
||||
final response = await http.get(dUrl);
|
||||
if (response.statusCode == 200) {
|
||||
var json = jsonDecode(response.body);
|
||||
if (json['success'] && json['data'] != null) {
|
||||
user = json['data']['user']['_id'];
|
||||
} else {
|
||||
developer.log("fetchPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||
throw TetrioPlayerNotExist();
|
||||
try{
|
||||
final response = await client.get(dUrl);
|
||||
|
||||
switch (response.statusCode) {
|
||||
case 200:
|
||||
var json = jsonDecode(response.body);
|
||||
if (json['success'] && json['data'] != null) {
|
||||
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 {
|
||||
developer.log("fetchPlayer Failed to fetch player", name: "services/tetrio_crud", error: response.statusCode);
|
||||
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||
} on http.ClientException catch (e, s) {
|
||||
developer.log("$e, $s");
|
||||
throw http.ClientException(e.message, e.uri);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Uri url;
|
||||
if (kIsWeb) {
|
||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "tetrioUser", "user": user.toLowerCase().trim()});
|
||||
} else {
|
||||
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) {
|
||||
var json = jsonDecode(response.body);
|
||||
if (json['success']) {
|
||||
TetrioPlayer player = TetrioPlayer.fromJson(json['data']['user'], DateTime.fromMillisecondsSinceEpoch(json['cache']['cached_at'], isUtc: true), json['data']['user']['_id'], json['data']['user']['username']);
|
||||
developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud");
|
||||
_playersCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = player;
|
||||
return player;
|
||||
} else {
|
||||
developer.log("fetchPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||
throw TetrioPlayerNotExist();
|
||||
switch (response.statusCode) {
|
||||
case 200:
|
||||
var json = jsonDecode(response.body);
|
||||
if (json['success']) {
|
||||
TetrioPlayer player = TetrioPlayer.fromJson(json['data']['user'], DateTime.fromMillisecondsSinceEpoch(json['cache']['cached_at'], isUtc: true), json['data']['user']['_id'], json['data']['user']['username']);
|
||||
developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud");
|
||||
_playersCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = player;
|
||||
return player;
|
||||
} else {
|
||||
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 {
|
||||
developer.log("fetchPlayer Failed to fetch player", name: "services/tetrio_crud", error: response.statusCode);
|
||||
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||
}on http.ClientException catch (e, s) {
|
||||
developer.log("$e, $s");
|
||||
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/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'dart:math';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
|
@ -31,6 +32,7 @@ const allowedHeightForPlayerBioInPixels = 30.0;
|
|||
const givenTextHeightByScreenPercentage = 0.3;
|
||||
final NumberFormat timeInSec = NumberFormat("#,###.###s.");
|
||||
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 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;
|
||||
errText = t.errors.connection(code: err.code, message: err.message);
|
||||
break;
|
||||
case SocketException: // TODO: Find a way to catch
|
||||
var err = snapshot.error as SocketException;
|
||||
errText = t.errors.socketException(host: err.address!.host, message: err.osError!.message);
|
||||
case P1nkl0bst3rForbidden:
|
||||
errText = t.errors.p1nkl0bst3rForbidden;
|
||||
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;
|
||||
default:
|
||||
errText = snapshot.error.toString();
|
||||
}
|
||||
return Center(
|
||||
child: Text(errText,
|
||||
style: const TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: 42),
|
||||
textAlign: TextAlign.center));
|
||||
return Center(child: Text(errText, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 42, fontWeight: FontWeight.bold), textAlign: TextAlign.center));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -570,7 +590,7 @@ class _History extends StatelessWidget{
|
|||
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)),
|
||||
if (record!.stream.contains("40l"))
|
||||
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(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: bigScreen ? 42 : 28))
|
||||
|
@ -755,194 +775,108 @@ class _RecordThingy extends StatelessWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("${t.numOfGameActions.pc}:",
|
||||
style: const TextStyle(fontSize: 24)),
|
||||
Text(
|
||||
record!.endContext!.clears.allClears
|
||||
.toString(),
|
||||
style: const TextStyle(fontSize: 24),
|
||||
),
|
||||
Text("${t.numOfGameActions.pc}:", style: const TextStyle(fontSize: 24)),
|
||||
Text(record!.endContext!.clears.allClears.toString(), style: const TextStyle(fontSize: 24)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("${t.numOfGameActions.hold}:", style: const TextStyle(fontSize: 24)),
|
||||
Text(record!.endContext!.holds.toString(), style: const TextStyle(fontSize: 24)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("${t.numOfGameActions.tspinsTotal}:", style: const TextStyle(fontSize: 24)),
|
||||
Text(record!.endContext!.tSpins.toString(), style: const TextStyle(fontSize: 24)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin zero:", style: TextStyle(fontSize: 18)),
|
||||
Text(record!.endContext!.clears.tSpinZeros.toString(), style: const TextStyle(fontSize: 18)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin singles:", style: TextStyle(fontSize: 18)),
|
||||
Text(record!.endContext!.clears.tSpinSingles.toString(), style: const TextStyle(fontSize: 18)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin doubles:", style: TextStyle(fontSize: 18)),
|
||||
Text(record!.endContext!.clears.tSpinDoubles.toString(), style: const TextStyle(fontSize: 18)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin triples:", style: TextStyle(fontSize: 18)),
|
||||
Text(record!.endContext!.clears.tSpinTriples.toString(), style: const TextStyle(fontSize: 18)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin mini zero:", style: TextStyle(fontSize: 18)),
|
||||
Text(record!.endContext!.clears.tSpinMiniZeros.toString(), style: const TextStyle(fontSize: 18)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin mini singles:", style: TextStyle(fontSize: 18)),
|
||||
Text(record!.endContext!.clears.tSpinMiniSingles.toString(), style: const TextStyle(fontSize: 18)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin mini doubles:", style: TextStyle(fontSize: 18)),
|
||||
Text(record!.endContext!.clears.tSpinMiniDoubles.toString(), style: const TextStyle(fontSize: 18)),
|
||||
],
|
||||
),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("${t.numOfGameActions.lineClears}:", style: const TextStyle(fontSize: 24)),
|
||||
Text(record!.endContext!.lines.toString(), style: const TextStyle(fontSize: 24)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("${t.numOfGameActions.hold}:",
|
||||
style: const TextStyle(fontSize: 24)),
|
||||
Text(
|
||||
record!.endContext!.holds.toString(),
|
||||
style: const TextStyle(fontSize: 24),
|
||||
),
|
||||
const Text(" - Singles:", style: TextStyle(fontSize: 18)),
|
||||
Text(record!.endContext!.clears.singles.toString(), style: const TextStyle(fontSize: 18)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("${t.numOfGameActions.tspinsTotal}:",
|
||||
style: const TextStyle(fontSize: 24)),
|
||||
Text(
|
||||
record!.endContext!.tSpins.toString(),
|
||||
style: const TextStyle(fontSize: 24),
|
||||
),
|
||||
const Text(" - Doubles:", style: TextStyle(fontSize: 18)),
|
||||
Text(record!.endContext!.clears.doubles.toString(), style: const TextStyle(fontSize: 18)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin zero:",
|
||||
style: TextStyle(fontSize: 18)),
|
||||
Text(
|
||||
record!.endContext!.clears.tSpinZeros
|
||||
.toString(),
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
const Text(" - Triples:", style: TextStyle(fontSize: 18)),
|
||||
Text(record!.endContext!.clears.triples.toString(), style: const TextStyle(fontSize: 18)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin singles:",
|
||||
style: TextStyle(fontSize: 18)),
|
||||
Text(
|
||||
record!.endContext!.clears.tSpinSingles
|
||||
.toString(),
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin doubles:",
|
||||
style: TextStyle(fontSize: 18)),
|
||||
Text(
|
||||
record!.endContext!.clears.tSpinDoubles
|
||||
.toString(),
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin triples:",
|
||||
style: TextStyle(fontSize: 18)),
|
||||
Text(
|
||||
record!.endContext!.clears.tSpinTriples
|
||||
.toString(),
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin mini zero:",
|
||||
style: TextStyle(fontSize: 18)),
|
||||
Text(
|
||||
record!.endContext!.clears.tSpinMiniZeros
|
||||
.toString(),
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin mini singles:",
|
||||
style: TextStyle(fontSize: 18)),
|
||||
Text(
|
||||
record!.endContext!.clears.tSpinMiniSingles
|
||||
.toString(),
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - T-spin mini doubles:",
|
||||
style: TextStyle(fontSize: 18)),
|
||||
Text(
|
||||
record!.endContext!.clears.tSpinMiniDoubles
|
||||
.toString(),
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("${t.numOfGameActions.lineClears}:",
|
||||
style: const TextStyle(fontSize: 24)),
|
||||
Text(
|
||||
record!.endContext!.lines.toString(),
|
||||
style: const TextStyle(fontSize: 24),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - Singles:",
|
||||
style: TextStyle(fontSize: 18)),
|
||||
Text(
|
||||
record!.endContext!.clears.singles
|
||||
.toString(),
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - Doubles:",
|
||||
style: TextStyle(fontSize: 18)),
|
||||
Text(
|
||||
record!.endContext!.clears.doubles
|
||||
.toString(),
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - Triples:",
|
||||
style: TextStyle(fontSize: 18)),
|
||||
Text(
|
||||
record!.endContext!.clears.triples
|
||||
.toString(),
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(" - Quads:",
|
||||
style: TextStyle(fontSize: 18)),
|
||||
Text(
|
||||
record!.endContext!.clears.quads.toString(),
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
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]["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]["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]["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),
|
||||
|
@ -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]["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]["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]["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),
|
||||
|
@ -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]["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]["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]["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),
|
||||
|
|
|
@ -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"),
|
||||
trailing: Text("${f2.format(averages[keys[index]]?[1]["toEnterTR"])} TR", style: bigScreen ? const TextStyle(fontSize: 28) : null),
|
||||
onTap: (){
|
||||
Navigator.push(
|
||||
if (averages[keys[index]]?[1]["players"] > 0) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => RankView(rank: averages[keys[index]]!),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
})
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:intl/intl.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
import 'package:tetra_stats/views/compare_view.dart';
|
||||
import 'package:tetra_stats/views/mathes_view.dart';
|
||||
import 'package:tetra_stats/views/state_view.dart';
|
||||
|
||||
class StatesView extends StatefulWidget {
|
||||
|
@ -21,6 +21,17 @@ class StatesState extends State<StatesView> {
|
|||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
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,
|
||||
body: SafeArea(
|
||||
|
|
28
pubspec.lock
28
pubspec.lock
|
@ -21,10 +21,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: "49b1fad315e57ab0bbc15bcbb874e83116a1d78f77ebd500a4af6c9407d6b28e"
|
||||
sha256: "1227dc3efc4ea571eebb2dfb814506ed2cfb1d4b1b89fb918abdddde617ead3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.8"
|
||||
version: "3.4.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -229,10 +229,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_macos
|
||||
sha256: "182c3f8350cee659f7b115e956047ee3dc672a96665883a545e81581b9a82c72"
|
||||
sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.3+2"
|
||||
version: "0.9.3+3"
|
||||
file_selector_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -361,10 +361,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf
|
||||
sha256: "6e703d5e2f8c63fb31a77753915c1ec8baebde8088844e0d29f71b8f0b108888"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.17"
|
||||
version: "4.1.0"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -782,10 +782,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: sqlite3_flutter_libs
|
||||
sha256: fb115050b0c2589afe2085a62d77f5deda4db65db20a5c65a6e0c92fda89b45e
|
||||
sha256: "11a41f380fbcbda5bbba03ddcdbe0545e46094ab043783c46c70e8335831df03"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.16"
|
||||
version: "0.5.17"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -814,18 +814,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: syncfusion_flutter_core
|
||||
sha256: "2baf60cd245a21a7069f036bbca1ca222633d38f57748e133da97a305712627c"
|
||||
sha256: aea119c8117953fa5decf4a313b431e556b0959cd35ff88f8fbdc0eda9bedb06
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "22.2.11"
|
||||
version: "23.1.36"
|
||||
syncfusion_flutter_gauges:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: syncfusion_flutter_gauges
|
||||
sha256: c086f17e84452e809b12f9832763ec4cea347b9f6e1e662a0e8addabca6cc2e5
|
||||
sha256: ae46df959f60f0fed6a8c86c8c971883ed790450f8d32f546dc8a02cb4500cbd
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "22.2.11"
|
||||
version: "23.1.36"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1014,10 +1014,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "9e82a402b7f3d518fb9c02d0e9ae45952df31b9bf34d77baf19da2de03fc2aaa"
|
||||
sha256: c97defd418eef4ec88c0d1652cdce84b9f7b63dd7198e266d06ac1710d527067
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.7"
|
||||
version: "5.0.8"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -2,7 +2,7 @@ name: tetra_stats
|
|||
description: Track your and other player stats in TETR.IO
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.2.2+10
|
||||
version: 1.2.3+11
|
||||
|
||||
environment:
|
||||
sdk: '>=2.19.6 <3.0.0'
|
||||
|
@ -31,7 +31,7 @@ dependencies:
|
|||
package_info_plus: ^4.0.2
|
||||
shared_preferences: ^2.1.1
|
||||
intl: ^0.18.0
|
||||
syncfusion_flutter_gauges: ^22.1.34
|
||||
syncfusion_flutter_gauges: ^23.1.36
|
||||
file_selector: ^1.0.1
|
||||
file_picker: ^5.3.2
|
||||
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",
|
||||
"stateViewTitle": "${nickname} account on ${date}",
|
||||
"statesViewTitle": "${number} states of ${nickname} account",
|
||||
"matchesViewTitle": "${nickname} TL matches",
|
||||
"statesViewEntry": "Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD",
|
||||
"stateRemoved": "${date} state was removed from database!",
|
||||
"matchRemoved": "${date} match was removed from database!",
|
||||
"viewAllMatches": "View all matches",
|
||||
"trackedPlayersViewTitle": "Stored data",
|
||||
"trackedPlayersZeroEntrys": "Empty list. Press \"Track\" button in previous view to add current player here",
|
||||
"trackedPlayersOneEntry": "There is only one player",
|
||||
|
@ -209,7 +212,17 @@
|
|||
"errors":{
|
||||
"connection": "Some issue with connection: ${code} ${message}",
|
||||
"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)": {
|
||||
"": "Not selected",
|
||||
|
|
|
@ -72,8 +72,11 @@
|
|||
"aboutAppText": "${appName} (${packageName}) Версия ${version} Сборка ${buildNumber}\n\nРазработал dan63047\nФормулы предоставил kerrmunism\nИсторию предоставляет p1nkl0bst3r",
|
||||
"stateViewTitle": "Аккаунт ${nickname} ${date}",
|
||||
"statesViewTitle": "${number} состояний аккаунта ${nickname}",
|
||||
"matchesViewTitle": "Матчи аккаунта ${nickname}",
|
||||
"statesViewEntry": "${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD",
|
||||
"stateRemoved": "Состояние от ${date} было удалено из локальной базы данных!",
|
||||
"matchRemoved": "Матч от ${date} был удален из локальной базы данных!",
|
||||
"viewAllMatches": "Все матчи",
|
||||
"trackedPlayersViewTitle": "Сохранённые данные",
|
||||
"trackedPlayersZeroEntrys": "Пустой список. Вернитесь на предыдущий экран и нажмите кнопку \"Отслеживать\", чтобы текущий игрок появился здесь",
|
||||
"trackedPlayersOneEntry": "В списке только один игрок",
|
||||
|
@ -209,7 +212,17 @@
|
|||
"errors":{
|
||||
"connection": "Проблема с подключением: ${code} ${message}",
|
||||
"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)": {
|
||||
"": "Не выбрана",
|
||||
|
|
Loading…
Reference in New Issue