Now it's possible to compare players with rank averages.
Also I started working on i18n
This commit is contained in:
parent
3fb7b1fabb
commit
04483bfb64
|
@ -17,8 +17,8 @@
|
||||||
- ~~Better UI with delta and hints for stats~~ *v0.2.0, we are here*
|
- ~~Better UI with delta and hints for stats~~ *v0.2.0, we are here*
|
||||||
- ~~Ability to compare player with APM-PPS-VS stats~~
|
- ~~Ability to compare player with APM-PPS-VS stats~~
|
||||||
- ~~Ability to fetch Tetra League leaderboard~~
|
- ~~Ability to fetch Tetra League leaderboard~~
|
||||||
- ~~Average stats for ranks~~ *dev build are here*
|
- ~~Average stats for ranks~~
|
||||||
- Ability to compare player with avgRank
|
- ~~Ability to compare player with avgRank~~ *dev build are here*
|
||||||
- UI Animations
|
- UI Animations
|
||||||
- i18n, EN and RU locales
|
- i18n, EN and RU locales
|
||||||
- Talk with osk about CORS and EndContext in TL matches
|
- Talk with osk about CORS and EndContext in TL matches
|
||||||
|
|
|
@ -0,0 +1,572 @@
|
||||||
|
/// Generated file. Do not edit.
|
||||||
|
///
|
||||||
|
/// Locales: 2
|
||||||
|
/// Strings: 154 (77 per locale)
|
||||||
|
///
|
||||||
|
/// Built on 2023-07-10 at 17:08 UTC
|
||||||
|
|
||||||
|
// coverage:ignore-file
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:slang/builder/model/node.dart';
|
||||||
|
import 'package:slang_flutter/slang_flutter.dart';
|
||||||
|
export 'package:slang_flutter/slang_flutter.dart';
|
||||||
|
|
||||||
|
const AppLocale _baseLocale = AppLocale.en;
|
||||||
|
|
||||||
|
/// Supported locales, see extension methods below.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// - LocaleSettings.setLocale(AppLocale.en) // set locale
|
||||||
|
/// - Locale locale = AppLocale.en.flutterLocale // get flutter locale from enum
|
||||||
|
/// - if (LocaleSettings.currentLocale == AppLocale.en) // locale check
|
||||||
|
enum AppLocale with BaseAppLocale<AppLocale, _StringsEn> {
|
||||||
|
en(languageCode: 'en', build: _StringsEn.build),
|
||||||
|
ru(languageCode: 'ru', build: _StringsRu.build);
|
||||||
|
|
||||||
|
const AppLocale({required this.languageCode, this.scriptCode, this.countryCode, required this.build}); // ignore: unused_element
|
||||||
|
|
||||||
|
@override final String languageCode;
|
||||||
|
@override final String? scriptCode;
|
||||||
|
@override final String? countryCode;
|
||||||
|
@override final TranslationBuilder<AppLocale, _StringsEn> build;
|
||||||
|
|
||||||
|
/// Gets current instance managed by [LocaleSettings].
|
||||||
|
_StringsEn get translations => LocaleSettings.instance.translationMap[this]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Method A: Simple
|
||||||
|
///
|
||||||
|
/// No rebuild after locale change.
|
||||||
|
/// Translation happens during initialization of the widget (call of t).
|
||||||
|
/// Configurable via 'translate_var'.
|
||||||
|
///
|
||||||
|
/// Usage:
|
||||||
|
/// String a = t.someKey.anotherKey;
|
||||||
|
/// String b = t['someKey.anotherKey']; // Only for edge cases!
|
||||||
|
_StringsEn get t => LocaleSettings.instance.currentTranslations;
|
||||||
|
|
||||||
|
/// Method B: Advanced
|
||||||
|
///
|
||||||
|
/// All widgets using this method will trigger a rebuild when locale changes.
|
||||||
|
/// Use this if you have e.g. a settings page where the user can select the locale during runtime.
|
||||||
|
///
|
||||||
|
/// Step 1:
|
||||||
|
/// wrap your App with
|
||||||
|
/// TranslationProvider(
|
||||||
|
/// child: MyApp()
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// Step 2:
|
||||||
|
/// final t = Translations.of(context); // Get t variable.
|
||||||
|
/// String a = t.someKey.anotherKey; // Use t variable.
|
||||||
|
/// String b = t['someKey.anotherKey']; // Only for edge cases!
|
||||||
|
class Translations {
|
||||||
|
Translations._(); // no constructor
|
||||||
|
|
||||||
|
static _StringsEn of(BuildContext context) => InheritedLocaleData.of<AppLocale, _StringsEn>(context).translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The provider for method B
|
||||||
|
class TranslationProvider extends BaseTranslationProvider<AppLocale, _StringsEn> {
|
||||||
|
TranslationProvider({required super.child}) : super(settings: LocaleSettings.instance);
|
||||||
|
|
||||||
|
static InheritedLocaleData<AppLocale, _StringsEn> of(BuildContext context) => InheritedLocaleData.of<AppLocale, _StringsEn>(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Method B shorthand via [BuildContext] extension method.
|
||||||
|
/// Configurable via 'translate_var'.
|
||||||
|
///
|
||||||
|
/// Usage (e.g. in a widget's build method):
|
||||||
|
/// context.t.someKey.anotherKey
|
||||||
|
extension BuildContextTranslationsExtension on BuildContext {
|
||||||
|
_StringsEn get t => TranslationProvider.of(this).translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manages all translation instances and the current locale
|
||||||
|
class LocaleSettings extends BaseFlutterLocaleSettings<AppLocale, _StringsEn> {
|
||||||
|
LocaleSettings._() : super(utils: AppLocaleUtils.instance);
|
||||||
|
|
||||||
|
static final instance = LocaleSettings._();
|
||||||
|
|
||||||
|
// static aliases (checkout base methods for documentation)
|
||||||
|
static AppLocale get currentLocale => instance.currentLocale;
|
||||||
|
static Stream<AppLocale> getLocaleStream() => instance.getLocaleStream();
|
||||||
|
static AppLocale setLocale(AppLocale locale, {bool? listenToDeviceLocale = false}) => instance.setLocale(locale, listenToDeviceLocale: listenToDeviceLocale);
|
||||||
|
static AppLocale setLocaleRaw(String rawLocale, {bool? listenToDeviceLocale = false}) => instance.setLocaleRaw(rawLocale, listenToDeviceLocale: listenToDeviceLocale);
|
||||||
|
static AppLocale useDeviceLocale() => instance.useDeviceLocale();
|
||||||
|
@Deprecated('Use [AppLocaleUtils.supportedLocales]') static List<Locale> get supportedLocales => instance.supportedLocales;
|
||||||
|
@Deprecated('Use [AppLocaleUtils.supportedLocalesRaw]') static List<String> get supportedLocalesRaw => instance.supportedLocalesRaw;
|
||||||
|
static void setPluralResolver({String? language, AppLocale? locale, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver}) => instance.setPluralResolver(
|
||||||
|
language: language,
|
||||||
|
locale: locale,
|
||||||
|
cardinalResolver: cardinalResolver,
|
||||||
|
ordinalResolver: ordinalResolver,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides utility functions without any side effects.
|
||||||
|
class AppLocaleUtils extends BaseAppLocaleUtils<AppLocale, _StringsEn> {
|
||||||
|
AppLocaleUtils._() : super(baseLocale: _baseLocale, locales: AppLocale.values);
|
||||||
|
|
||||||
|
static final instance = AppLocaleUtils._();
|
||||||
|
|
||||||
|
// static aliases (checkout base methods for documentation)
|
||||||
|
static AppLocale parse(String rawLocale) => instance.parse(rawLocale);
|
||||||
|
static AppLocale parseLocaleParts({required String languageCode, String? scriptCode, String? countryCode}) => instance.parseLocaleParts(languageCode: languageCode, scriptCode: scriptCode, countryCode: countryCode);
|
||||||
|
static AppLocale findDeviceLocale() => instance.findDeviceLocale();
|
||||||
|
static List<Locale> get supportedLocales => instance.supportedLocales;
|
||||||
|
static List<String> get supportedLocalesRaw => instance.supportedLocalesRaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// translations
|
||||||
|
|
||||||
|
// Path: <root>
|
||||||
|
class _StringsEn implements BaseTranslations<AppLocale, _StringsEn> {
|
||||||
|
|
||||||
|
/// You can call this constructor and build your own translation instance of this locale.
|
||||||
|
/// Constructing via the enum [AppLocale.build] is preferred.
|
||||||
|
_StringsEn.build({Map<String, Node>? overrides, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver})
|
||||||
|
: assert(overrides == null, 'Set "translation_overrides: true" in order to enable this feature.'),
|
||||||
|
$meta = TranslationMetadata(
|
||||||
|
locale: AppLocale.en,
|
||||||
|
overrides: overrides ?? {},
|
||||||
|
cardinalResolver: cardinalResolver,
|
||||||
|
ordinalResolver: ordinalResolver,
|
||||||
|
) {
|
||||||
|
$meta.setFlatMapFunction(_flatMapFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Metadata for the translations of <en>.
|
||||||
|
@override final TranslationMetadata<AppLocale, _StringsEn> $meta;
|
||||||
|
|
||||||
|
/// Access flat map
|
||||||
|
dynamic operator[](String key) => $meta.getTranslation(key);
|
||||||
|
|
||||||
|
late final _StringsEn _root = this; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
Map<String, String> get locales => {
|
||||||
|
'en': 'English',
|
||||||
|
'ru': 'Russian (Русский)',
|
||||||
|
};
|
||||||
|
String get tetraLeague => 'Tetra League';
|
||||||
|
String get tlRecords => 'TL Records';
|
||||||
|
String get history => 'History';
|
||||||
|
String get sprint => '40 Lines';
|
||||||
|
String get blitz => 'Blitz';
|
||||||
|
String get other => 'Other';
|
||||||
|
String get zen => 'Zen';
|
||||||
|
String get bio => 'Bio';
|
||||||
|
String get refresh => 'Refresh';
|
||||||
|
String get showStoredData => 'Show stored data';
|
||||||
|
String get statsCalc => 'Stats Calculator';
|
||||||
|
String get settings => 'Settings';
|
||||||
|
String get track => 'Track';
|
||||||
|
String get stopTracking => 'Stop\ntracking';
|
||||||
|
String get becameTracked => 'Added to tracking list!';
|
||||||
|
String get compare => 'Compare';
|
||||||
|
String get stoppedBeingTracked => 'Removed from tracking list!';
|
||||||
|
String get tlLeaderboard => 'Tetra League leaderboard';
|
||||||
|
String get noRecords => 'No records';
|
||||||
|
String get noRecord => 'No record';
|
||||||
|
String get notEnoughData => 'Not enough data';
|
||||||
|
String get noHistorySaved => 'No history saved';
|
||||||
|
String obtainDate({required Object date}) => 'Obtained ${date}';
|
||||||
|
String fetchDate({required Object date}) => 'Fetched ${date}';
|
||||||
|
String get exactGametime => 'Exact gametime';
|
||||||
|
String get bigRedBanned => 'BANNED';
|
||||||
|
String get bigRedBadStanding => 'BAD STANDING';
|
||||||
|
String get copiedToClipboard => 'Copied to clipboard!';
|
||||||
|
String get playerRoleAccount => ' account ';
|
||||||
|
String get wasFromBeginning => 'that was from very beginning';
|
||||||
|
String get created => 'created';
|
||||||
|
String get botCreatedBy => 'by';
|
||||||
|
String get notSupporter => 'Not a supporter';
|
||||||
|
String get assignedManualy => 'That badge was assigned manualy by TETR.IO admins';
|
||||||
|
String supporter({required Object tier}) => 'Supporter tier ${tier}';
|
||||||
|
String comparingWith({required Object date}) => 'Comparing with data from ${date}';
|
||||||
|
String get top => 'Top';
|
||||||
|
String get topRank => 'Top Rank';
|
||||||
|
String get decaying => 'Decaying';
|
||||||
|
String gamesUntilRanked({required Object left}) => '${left} games until being ranked';
|
||||||
|
String get nerdStats => 'Nerd Stats';
|
||||||
|
late final _StringsStatCellNumEn statCellNum = _StringsStatCellNumEn._(_root);
|
||||||
|
Map<String, String> get playerRole => {
|
||||||
|
'user': 'User',
|
||||||
|
'banned': 'Banned',
|
||||||
|
'bot': 'Bot',
|
||||||
|
'sysop': 'System operator',
|
||||||
|
'admin': 'Admin',
|
||||||
|
'mod': 'Moderator',
|
||||||
|
'halfmod': 'Community moderator',
|
||||||
|
};
|
||||||
|
late final _StringsNumOfGameActionsEn numOfGameActions = _StringsNumOfGameActionsEn._(_root);
|
||||||
|
late final _StringsPopupActionsEn popupActions = _StringsPopupActionsEn._(_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path: statCellNum
|
||||||
|
class _StringsStatCellNumEn {
|
||||||
|
_StringsStatCellNumEn._(this._root);
|
||||||
|
|
||||||
|
final _StringsEn _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
String get xpLevel => 'XP Level';
|
||||||
|
String get hoursPlayed => 'Hours\nPlayed';
|
||||||
|
String get onlineGames => 'Online\nGames';
|
||||||
|
String get gamesWon => 'Games\nWon';
|
||||||
|
String get friends => 'Friends';
|
||||||
|
String get apm => 'Attack\nPer Minute';
|
||||||
|
String get vs => 'Versus\nScore';
|
||||||
|
String get lbp => 'Leaderboard\nplacement';
|
||||||
|
String get lbpc => 'Country LB\nplacement';
|
||||||
|
String get gamesPlayed => 'Games\nplayed';
|
||||||
|
String get gamesWonTL => 'Games\nWon';
|
||||||
|
String get winrate => 'Winrate\nprecentage';
|
||||||
|
String get level => 'Level';
|
||||||
|
String get score => 'Score';
|
||||||
|
String get spp => 'Score\nPer Piece';
|
||||||
|
String get pieces => 'Pieces\nPlaced';
|
||||||
|
String get pps => 'Pieces\nPer Second';
|
||||||
|
String get finesseFaults => 'Finesse\nFaults';
|
||||||
|
String get finessePercentage => 'Finesse\nPercentage';
|
||||||
|
String get keys => 'Key\nPresses';
|
||||||
|
String get kpp => 'KP Per\nPiece';
|
||||||
|
String get kps => 'KP Per\nSecond';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path: numOfGameActions
|
||||||
|
class _StringsNumOfGameActionsEn {
|
||||||
|
_StringsNumOfGameActionsEn._(this._root);
|
||||||
|
|
||||||
|
final _StringsEn _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
String get pc => 'All Clears';
|
||||||
|
String get hold => 'Holds';
|
||||||
|
String get tspinsTotal => 'T-spins total';
|
||||||
|
String get lineClears => 'Line clears';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path: popupActions
|
||||||
|
class _StringsPopupActionsEn {
|
||||||
|
_StringsPopupActionsEn._(this._root);
|
||||||
|
|
||||||
|
final _StringsEn _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
String get ok => 'OK';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path: <root>
|
||||||
|
class _StringsRu implements _StringsEn {
|
||||||
|
|
||||||
|
/// You can call this constructor and build your own translation instance of this locale.
|
||||||
|
/// Constructing via the enum [AppLocale.build] is preferred.
|
||||||
|
_StringsRu.build({Map<String, Node>? overrides, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver})
|
||||||
|
: assert(overrides == null, 'Set "translation_overrides: true" in order to enable this feature.'),
|
||||||
|
$meta = TranslationMetadata(
|
||||||
|
locale: AppLocale.ru,
|
||||||
|
overrides: overrides ?? {},
|
||||||
|
cardinalResolver: cardinalResolver,
|
||||||
|
ordinalResolver: ordinalResolver,
|
||||||
|
) {
|
||||||
|
$meta.setFlatMapFunction(_flatMapFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Metadata for the translations of <ru>.
|
||||||
|
@override final TranslationMetadata<AppLocale, _StringsEn> $meta;
|
||||||
|
|
||||||
|
/// Access flat map
|
||||||
|
@override dynamic operator[](String key) => $meta.getTranslation(key);
|
||||||
|
|
||||||
|
@override late final _StringsRu _root = this; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
@override Map<String, String> get locales => {
|
||||||
|
'en': 'Английский (English)',
|
||||||
|
'ru': 'Русский',
|
||||||
|
};
|
||||||
|
@override String get tetraLeague => 'Тетра Лига';
|
||||||
|
@override String get tlRecords => 'Матчи ТЛ';
|
||||||
|
@override String get history => 'История';
|
||||||
|
@override String get sprint => '40 линий';
|
||||||
|
@override String get blitz => 'Блиц';
|
||||||
|
@override String get other => 'Другое';
|
||||||
|
@override String get zen => 'Дзен';
|
||||||
|
@override String get bio => 'Биография';
|
||||||
|
@override String get refresh => 'Обновить';
|
||||||
|
@override String get showStoredData => 'Показать сохранённые данные';
|
||||||
|
@override String get statsCalc => 'Калькулятор статистики';
|
||||||
|
@override String get settings => 'Настройки';
|
||||||
|
@override String get track => 'Отслеживать';
|
||||||
|
@override String get stopTracking => 'Перестать\nотслеживать';
|
||||||
|
@override String get becameTracked => 'Добавлен в список отслеживания!';
|
||||||
|
@override String get stoppedBeingTracked => 'Удалён из списка отслеживания!';
|
||||||
|
@override String get compare => 'Сравнить';
|
||||||
|
@override String get tlLeaderboard => 'Таблица лидеров Тетра Лиги';
|
||||||
|
@override String get noRecords => 'Нет записей';
|
||||||
|
@override String get noRecord => 'Нет рекорда';
|
||||||
|
@override String get notEnoughData => 'Недостаточно данных';
|
||||||
|
@override String get noHistorySaved => 'Нет сохранённой истории';
|
||||||
|
@override String obtainDate({required Object date}) => 'Получено ${date}';
|
||||||
|
@override String fetchDate({required Object date}) => 'На момент ${date}';
|
||||||
|
@override String get exactGametime => 'Время, проведённое в игре';
|
||||||
|
@override String get bigRedBanned => 'ЗАБАНЕН';
|
||||||
|
@override String get bigRedBadStanding => 'ПЛОХАЯ РЕПУТАЦИЯ';
|
||||||
|
@override String get copiedToClipboard => 'Скопировано в буфер обмена!';
|
||||||
|
@override String get playerRoleAccount => ', аккаунт которого ';
|
||||||
|
@override String get wasFromBeginning => 'существовал с самого начала';
|
||||||
|
@override String get created => 'создан';
|
||||||
|
@override String get botCreatedBy => 'игроком';
|
||||||
|
@override String get notSupporter => 'Нет саппортерки';
|
||||||
|
@override String supporter({required Object tier}) => 'Саппортерка ${tier} уровня';
|
||||||
|
@override String get assignedManualy => 'Этот значок был присвоен вручную администрацией TETR.IO';
|
||||||
|
@override String comparingWith({required Object date}) => 'Сравнивая с данными от ${date}';
|
||||||
|
@override String get top => 'Топ';
|
||||||
|
@override String get topRank => 'Топ Ранг';
|
||||||
|
@override String get decaying => 'Загнивает';
|
||||||
|
@override String gamesUntilRanked({required Object left}) => '${left} матчей до получения рейтинга';
|
||||||
|
@override String get nerdStats => 'Для задротов';
|
||||||
|
@override late final _StringsStatCellNumRu statCellNum = _StringsStatCellNumRu._(_root);
|
||||||
|
@override Map<String, String> get playerRole => {
|
||||||
|
'user': 'Пользователь',
|
||||||
|
'banned': 'Заблокированный пользователь',
|
||||||
|
'bot': 'Бот',
|
||||||
|
'sysop': 'Системный оператор',
|
||||||
|
'admin': 'Администратор',
|
||||||
|
'mod': 'Модератор',
|
||||||
|
'halfmod': 'Модератор сообщества',
|
||||||
|
};
|
||||||
|
@override late final _StringsNumOfGameActionsRu numOfGameActions = _StringsNumOfGameActionsRu._(_root);
|
||||||
|
@override late final _StringsPopupActionsRu popupActions = _StringsPopupActionsRu._(_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path: statCellNum
|
||||||
|
class _StringsStatCellNumRu implements _StringsStatCellNumEn {
|
||||||
|
_StringsStatCellNumRu._(this._root);
|
||||||
|
|
||||||
|
@override final _StringsRu _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
@override String get xpLevel => 'Уровень\nопыта';
|
||||||
|
@override String get hoursPlayed => 'Часов\nСыграно';
|
||||||
|
@override String get onlineGames => 'Онлайн\nИгр';
|
||||||
|
@override String get gamesWon => 'Онлайн\nПобед';
|
||||||
|
@override String get friends => 'Друзей';
|
||||||
|
@override String get apm => 'Атака в\nМинуту';
|
||||||
|
@override String get vs => 'Показатель\nVersus';
|
||||||
|
@override String get lbp => 'Положение\nв рейтинге';
|
||||||
|
@override String get lbpc => 'Положение\nв рейтинге страны';
|
||||||
|
@override String get gamesPlayed => 'Игр\nСыграно';
|
||||||
|
@override String get gamesWonTL => 'Побед';
|
||||||
|
@override String get winrate => 'Процент\nпобед';
|
||||||
|
@override String get level => 'Уровень';
|
||||||
|
@override String get score => 'Счёт';
|
||||||
|
@override String get spp => 'Очков\nна Фигуру';
|
||||||
|
@override String get pieces => 'Фигур\nУстановлено';
|
||||||
|
@override String get pps => 'Фигур в\nСекунду';
|
||||||
|
@override String get finesseFaults => 'Ошибок\nТехники';
|
||||||
|
@override String get finessePercentage => '% Качества\nТехники';
|
||||||
|
@override String get keys => 'Нажатий\nКлавиш';
|
||||||
|
@override String get kpp => 'Нажатий\nна Фигуру';
|
||||||
|
@override String get kps => 'Нажатий\nв Секунду';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path: numOfGameActions
|
||||||
|
class _StringsNumOfGameActionsRu implements _StringsNumOfGameActionsEn {
|
||||||
|
_StringsNumOfGameActionsRu._(this._root);
|
||||||
|
|
||||||
|
@override final _StringsRu _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
@override String get pc => 'Все чисто';
|
||||||
|
@override String get hold => 'В запас';
|
||||||
|
@override String get tspinsTotal => 'T-spins всего';
|
||||||
|
@override String get lineClears => 'Линий очищено';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path: popupActions
|
||||||
|
class _StringsPopupActionsRu implements _StringsPopupActionsEn {
|
||||||
|
_StringsPopupActionsRu._(this._root);
|
||||||
|
|
||||||
|
@override final _StringsRu _root; // ignore: unused_field
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
@override String get ok => 'OK';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flat map(s) containing all translations.
|
||||||
|
/// Only for edge cases! For simple maps, use the map function of this library.
|
||||||
|
|
||||||
|
extension on _StringsEn {
|
||||||
|
dynamic _flatMapFunction(String path) {
|
||||||
|
switch (path) {
|
||||||
|
case 'locales.en': return 'English';
|
||||||
|
case 'locales.ru': return 'Russian (Русский)';
|
||||||
|
case 'tetraLeague': return 'Tetra League';
|
||||||
|
case 'tlRecords': return 'TL Records';
|
||||||
|
case 'history': return 'History';
|
||||||
|
case 'sprint': return '40 Lines';
|
||||||
|
case 'blitz': return 'Blitz';
|
||||||
|
case 'other': return 'Other';
|
||||||
|
case 'zen': return 'Zen';
|
||||||
|
case 'bio': return 'Bio';
|
||||||
|
case 'refresh': return 'Refresh';
|
||||||
|
case 'showStoredData': return 'Show stored data';
|
||||||
|
case 'statsCalc': return 'Stats Calculator';
|
||||||
|
case 'settings': return 'Settings';
|
||||||
|
case 'track': return 'Track';
|
||||||
|
case 'stopTracking': return 'Stop\ntracking';
|
||||||
|
case 'becameTracked': return 'Added to tracking list!';
|
||||||
|
case 'compare': return 'Compare';
|
||||||
|
case 'stoppedBeingTracked': return 'Removed from tracking list!';
|
||||||
|
case 'tlLeaderboard': return 'Tetra League leaderboard';
|
||||||
|
case 'noRecords': return 'No records';
|
||||||
|
case 'noRecord': return 'No record';
|
||||||
|
case 'notEnoughData': return 'Not enough data';
|
||||||
|
case 'noHistorySaved': return 'No history saved';
|
||||||
|
case 'obtainDate': return ({required Object date}) => 'Obtained ${date}';
|
||||||
|
case 'fetchDate': return ({required Object date}) => 'Fetched ${date}';
|
||||||
|
case 'exactGametime': return 'Exact gametime';
|
||||||
|
case 'bigRedBanned': return 'BANNED';
|
||||||
|
case 'bigRedBadStanding': return 'BAD STANDING';
|
||||||
|
case 'copiedToClipboard': return 'Copied to clipboard!';
|
||||||
|
case 'playerRoleAccount': return ' account ';
|
||||||
|
case 'wasFromBeginning': return 'that was from very beginning';
|
||||||
|
case 'created': return 'created';
|
||||||
|
case 'botCreatedBy': return 'by';
|
||||||
|
case 'notSupporter': return 'Not a supporter';
|
||||||
|
case 'assignedManualy': return 'That badge was assigned manualy by TETR.IO admins';
|
||||||
|
case 'supporter': return ({required Object tier}) => 'Supporter tier ${tier}';
|
||||||
|
case 'comparingWith': return ({required Object date}) => 'Comparing with data from ${date}';
|
||||||
|
case 'top': return 'Top';
|
||||||
|
case 'topRank': return 'Top Rank';
|
||||||
|
case 'decaying': return 'Decaying';
|
||||||
|
case 'gamesUntilRanked': return ({required Object left}) => '${left} games until being ranked';
|
||||||
|
case 'nerdStats': return 'Nerd Stats';
|
||||||
|
case 'statCellNum.xpLevel': return 'XP Level';
|
||||||
|
case 'statCellNum.hoursPlayed': return 'Hours\nPlayed';
|
||||||
|
case 'statCellNum.onlineGames': return 'Online\nGames';
|
||||||
|
case 'statCellNum.gamesWon': return 'Games\nWon';
|
||||||
|
case 'statCellNum.friends': return 'Friends';
|
||||||
|
case 'statCellNum.apm': return 'Attack\nPer Minute';
|
||||||
|
case 'statCellNum.vs': return 'Versus\nScore';
|
||||||
|
case 'statCellNum.lbp': return 'Leaderboard\nplacement';
|
||||||
|
case 'statCellNum.lbpc': return 'Country LB\nplacement';
|
||||||
|
case 'statCellNum.gamesPlayed': return 'Games\nplayed';
|
||||||
|
case 'statCellNum.gamesWonTL': return 'Games\nWon';
|
||||||
|
case 'statCellNum.winrate': return 'Winrate\nprecentage';
|
||||||
|
case 'statCellNum.level': return 'Level';
|
||||||
|
case 'statCellNum.score': return 'Score';
|
||||||
|
case 'statCellNum.spp': return 'Score\nPer Piece';
|
||||||
|
case 'statCellNum.pieces': return 'Pieces\nPlaced';
|
||||||
|
case 'statCellNum.pps': return 'Pieces\nPer Second';
|
||||||
|
case 'statCellNum.finesseFaults': return 'Finesse\nFaults';
|
||||||
|
case 'statCellNum.finessePercentage': return 'Finesse\nPercentage';
|
||||||
|
case 'statCellNum.keys': return 'Key\nPresses';
|
||||||
|
case 'statCellNum.kpp': return 'KP Per\nPiece';
|
||||||
|
case 'statCellNum.kps': return 'KP Per\nSecond';
|
||||||
|
case 'playerRole.user': return 'User';
|
||||||
|
case 'playerRole.banned': return 'Banned';
|
||||||
|
case 'playerRole.bot': return 'Bot';
|
||||||
|
case 'playerRole.sysop': return 'System operator';
|
||||||
|
case 'playerRole.admin': return 'Admin';
|
||||||
|
case 'playerRole.mod': return 'Moderator';
|
||||||
|
case 'playerRole.halfmod': return 'Community moderator';
|
||||||
|
case 'numOfGameActions.pc': return 'All Clears';
|
||||||
|
case 'numOfGameActions.hold': return 'Holds';
|
||||||
|
case 'numOfGameActions.tspinsTotal': return 'T-spins total';
|
||||||
|
case 'numOfGameActions.lineClears': return 'Line clears';
|
||||||
|
case 'popupActions.ok': return 'OK';
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on _StringsRu {
|
||||||
|
dynamic _flatMapFunction(String path) {
|
||||||
|
switch (path) {
|
||||||
|
case 'locales.en': return 'Английский (English)';
|
||||||
|
case 'locales.ru': return 'Русский';
|
||||||
|
case 'tetraLeague': return 'Тетра Лига';
|
||||||
|
case 'tlRecords': return 'Матчи ТЛ';
|
||||||
|
case 'history': return 'История';
|
||||||
|
case 'sprint': return '40 линий';
|
||||||
|
case 'blitz': return 'Блиц';
|
||||||
|
case 'other': return 'Другое';
|
||||||
|
case 'zen': return 'Дзен';
|
||||||
|
case 'bio': return 'Биография';
|
||||||
|
case 'refresh': return 'Обновить';
|
||||||
|
case 'showStoredData': return 'Показать сохранённые данные';
|
||||||
|
case 'statsCalc': return 'Калькулятор статистики';
|
||||||
|
case 'settings': return 'Настройки';
|
||||||
|
case 'track': return 'Отслеживать';
|
||||||
|
case 'stopTracking': return 'Перестать\nотслеживать';
|
||||||
|
case 'becameTracked': return 'Добавлен в список отслеживания!';
|
||||||
|
case 'stoppedBeingTracked': return 'Удалён из списка отслеживания!';
|
||||||
|
case 'compare': return 'Сравнить';
|
||||||
|
case 'tlLeaderboard': return 'Таблица лидеров Тетра Лиги';
|
||||||
|
case 'noRecords': return 'Нет записей';
|
||||||
|
case 'noRecord': return 'Нет рекорда';
|
||||||
|
case 'notEnoughData': return 'Недостаточно данных';
|
||||||
|
case 'noHistorySaved': return 'Нет сохранённой истории';
|
||||||
|
case 'obtainDate': return ({required Object date}) => 'Получено ${date}';
|
||||||
|
case 'fetchDate': return ({required Object date}) => 'На момент ${date}';
|
||||||
|
case 'exactGametime': return 'Время, проведённое в игре';
|
||||||
|
case 'bigRedBanned': return 'ЗАБАНЕН';
|
||||||
|
case 'bigRedBadStanding': return 'ПЛОХАЯ РЕПУТАЦИЯ';
|
||||||
|
case 'copiedToClipboard': return 'Скопировано в буфер обмена!';
|
||||||
|
case 'playerRoleAccount': return ', аккаунт которого ';
|
||||||
|
case 'wasFromBeginning': return 'существовал с самого начала';
|
||||||
|
case 'created': return 'создан';
|
||||||
|
case 'botCreatedBy': return 'игроком';
|
||||||
|
case 'notSupporter': return 'Нет саппортерки';
|
||||||
|
case 'supporter': return ({required Object tier}) => 'Саппортерка ${tier} уровня';
|
||||||
|
case 'assignedManualy': return 'Этот значок был присвоен вручную администрацией TETR.IO';
|
||||||
|
case 'comparingWith': return ({required Object date}) => 'Сравнивая с данными от ${date}';
|
||||||
|
case 'top': return 'Топ';
|
||||||
|
case 'topRank': return 'Топ Ранг';
|
||||||
|
case 'decaying': return 'Загнивает';
|
||||||
|
case 'gamesUntilRanked': return ({required Object left}) => '${left} матчей до получения рейтинга';
|
||||||
|
case 'nerdStats': return 'Для задротов';
|
||||||
|
case 'statCellNum.xpLevel': return 'Уровень\nопыта';
|
||||||
|
case 'statCellNum.hoursPlayed': return 'Часов\nСыграно';
|
||||||
|
case 'statCellNum.onlineGames': return 'Онлайн\nИгр';
|
||||||
|
case 'statCellNum.gamesWon': return 'Онлайн\nПобед';
|
||||||
|
case 'statCellNum.friends': return 'Друзей';
|
||||||
|
case 'statCellNum.apm': return 'Атака в\nМинуту';
|
||||||
|
case 'statCellNum.vs': return 'Показатель\nVersus';
|
||||||
|
case 'statCellNum.lbp': return 'Положение\nв рейтинге';
|
||||||
|
case 'statCellNum.lbpc': return 'Положение\nв рейтинге страны';
|
||||||
|
case 'statCellNum.gamesPlayed': return 'Игр\nСыграно';
|
||||||
|
case 'statCellNum.gamesWonTL': return 'Побед';
|
||||||
|
case 'statCellNum.winrate': return 'Процент\nпобед';
|
||||||
|
case 'statCellNum.level': return 'Уровень';
|
||||||
|
case 'statCellNum.score': return 'Счёт';
|
||||||
|
case 'statCellNum.spp': return 'Очков\nна Фигуру';
|
||||||
|
case 'statCellNum.pieces': return 'Фигур\nУстановлено';
|
||||||
|
case 'statCellNum.pps': return 'Фигур в\nСекунду';
|
||||||
|
case 'statCellNum.finesseFaults': return 'Ошибок\nТехники';
|
||||||
|
case 'statCellNum.finessePercentage': return '% Качества\nТехники';
|
||||||
|
case 'statCellNum.keys': return 'Нажатий\nКлавиш';
|
||||||
|
case 'statCellNum.kpp': return 'Нажатий\nна Фигуру';
|
||||||
|
case 'statCellNum.kps': return 'Нажатий\nв Секунду';
|
||||||
|
case 'playerRole.user': return 'Пользователь';
|
||||||
|
case 'playerRole.banned': return 'Заблокированный пользователь';
|
||||||
|
case 'playerRole.bot': return 'Бот';
|
||||||
|
case 'playerRole.sysop': return 'Системный оператор';
|
||||||
|
case 'playerRole.admin': return 'Администратор';
|
||||||
|
case 'playerRole.mod': return 'Модератор';
|
||||||
|
case 'playerRole.halfmod': return 'Модератор сообщества';
|
||||||
|
case 'numOfGameActions.pc': return 'Все чисто';
|
||||||
|
case 'numOfGameActions.hold': return 'В запас';
|
||||||
|
case 'numOfGameActions.tspinsTotal': return 'T-spins всего';
|
||||||
|
case 'numOfGameActions.lineClears': return 'Линий очищено';
|
||||||
|
case 'popupActions.ok': return 'OK';
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||||
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:tetra_stats/views/main_view.dart';
|
import 'package:tetra_stats/views/main_view.dart';
|
||||||
import 'package:tetra_stats/views/settings_view.dart';
|
import 'package:tetra_stats/views/settings_view.dart';
|
||||||
import 'package:tetra_stats/views/tracked_players_view.dart';
|
import 'package:tetra_stats/views/tracked_players_view.dart';
|
||||||
|
@ -11,11 +13,26 @@ void main() {
|
||||||
sqfliteFfiInit();
|
sqfliteFfiInit();
|
||||||
databaseFactory = databaseFactoryFfi;
|
databaseFactory = databaseFactoryFfi;
|
||||||
}
|
}
|
||||||
runApp(MaterialApp(
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
runApp(TranslationProvider(
|
||||||
|
child: MyApp(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
home: const MainView(),
|
home: const MainView(),
|
||||||
|
locale: TranslationProvider.of(context).flutterLocale,
|
||||||
|
supportedLocales: AppLocaleUtils.supportedLocales,
|
||||||
|
localizationsDelegates: GlobalMaterialLocalizations.delegates,
|
||||||
routes: {"/settings": (context) => const SettingsView(), "/states": (context) => const TrackedPlayersView(), "/calc": (context) => const CalcView()},
|
routes: {"/settings": (context) => const SettingsView(), "/states": (context) => const TrackedPlayersView(), "/calc": (context) => const CalcView()},
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
fontFamily: 'Eurostile Round',
|
fontFamily: 'Eurostile Round',
|
||||||
colorScheme: const ColorScheme.dark(primary: Colors.cyanAccent, secondary: Colors.white),
|
colorScheme: const ColorScheme.dark(primary: Colors.cyanAccent, secondary: Colors.white),
|
||||||
scaffoldBackgroundColor: Colors.black)));
|
scaffoldBackgroundColor: Colors.black
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,18 @@ class CompareState extends State<CompareView> {
|
||||||
|
|
||||||
void fetchRedSide(String user) async {
|
void fetchRedSide(String user) async {
|
||||||
try {
|
try {
|
||||||
|
if (user.startsWith("\$avg")){
|
||||||
|
try{
|
||||||
|
var average = (await teto.fetchTLLeaderboard()).getAverageOfRank(user.substring(4).toLowerCase())[0];
|
||||||
|
redSideMode = Mode.averages;
|
||||||
|
theRedSide = [null, null, average];
|
||||||
|
return setState(() {});
|
||||||
|
}on Exception {
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
.showSnackBar(SnackBar(content: Text("Falied to assign $user")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
var tearDownToNumbers = numbersReg.allMatches(user);
|
var tearDownToNumbers = numbersReg.allMatches(user);
|
||||||
if (tearDownToNumbers.length == 3) {
|
if (tearDownToNumbers.length == 3) {
|
||||||
redSideMode = Mode.stats;
|
redSideMode = Mode.stats;
|
||||||
|
@ -118,6 +130,18 @@ class CompareState extends State<CompareView> {
|
||||||
|
|
||||||
void fetchGreenSide(String user) async {
|
void fetchGreenSide(String user) async {
|
||||||
try {
|
try {
|
||||||
|
if (user.startsWith("\$avg")){
|
||||||
|
try{
|
||||||
|
var average = (await teto.fetchTLLeaderboard()).getAverageOfRank(user.substring(4).toLowerCase())[0];
|
||||||
|
greenSideMode = Mode.averages;
|
||||||
|
theGreenSide = [null, null, average];
|
||||||
|
return setState(() {});
|
||||||
|
}on Exception {
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
.showSnackBar(SnackBar(content: Text("Falied to assign $user")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
var tearDownToNumbers = numbersReg.allMatches(user);
|
var tearDownToNumbers = numbersReg.allMatches(user);
|
||||||
if (tearDownToNumbers.length == 3) {
|
if (tearDownToNumbers.length == 3) {
|
||||||
greenSideMode = Mode.stats;
|
greenSideMode = Mode.stats;
|
||||||
|
@ -211,7 +235,7 @@ class CompareState extends State<CompareView> {
|
||||||
titleGreenSide = "${theGreenSide[2].apm} APM, ${theGreenSide[2].pps} PPS, ${theGreenSide[2].vs} VS";
|
titleGreenSide = "${theGreenSide[2].apm} APM, ${theGreenSide[2].pps} PPS, ${theGreenSide[2].vs} VS";
|
||||||
break;
|
break;
|
||||||
case Mode.averages:
|
case Mode.averages:
|
||||||
titleGreenSide = "average";
|
titleGreenSide = "Average ${theGreenSide[2].rank.toUpperCase()} rank";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch (redSideMode){
|
switch (redSideMode){
|
||||||
|
@ -222,7 +246,7 @@ class CompareState extends State<CompareView> {
|
||||||
titleRedSide = "${theRedSide[2].apm} APM, ${theRedSide[2].pps} PPS, ${theRedSide[2].vs} VS";
|
titleRedSide = "${theRedSide[2].apm} APM, ${theRedSide[2].pps} PPS, ${theRedSide[2].vs} VS";
|
||||||
break;
|
break;
|
||||||
case Mode.averages:
|
case Mode.averages:
|
||||||
titleRedSide = "average";
|
titleRedSide = "Average ${theRedSide[2].rank.toUpperCase()} rank";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -376,8 +400,8 @@ class CompareState extends State<CompareView> {
|
||||||
),
|
),
|
||||||
if (theGreenSide[2].gamesPlayed > 9 &&
|
if (theGreenSide[2].gamesPlayed > 9 &&
|
||||||
theRedSide[2].gamesPlayed > 9 &&
|
theRedSide[2].gamesPlayed > 9 &&
|
||||||
greenSideMode == Mode.player &&
|
greenSideMode != Mode.stats &&
|
||||||
redSideMode == Mode.player)
|
redSideMode != Mode.stats)
|
||||||
CompareThingy(
|
CompareThingy(
|
||||||
label: "TR",
|
label: "TR",
|
||||||
greenSide: theGreenSide[2].rating,
|
greenSide: theGreenSide[2].rating,
|
||||||
|
@ -385,24 +409,24 @@ class CompareState extends State<CompareView> {
|
||||||
fractionDigits: 2,
|
fractionDigits: 2,
|
||||||
higherIsBetter: true,
|
higherIsBetter: true,
|
||||||
),
|
),
|
||||||
if (greenSideMode == Mode.player &&
|
if (greenSideMode != Mode.stats &&
|
||||||
redSideMode == Mode.player)
|
redSideMode != Mode.stats)
|
||||||
CompareThingy(
|
CompareThingy(
|
||||||
label: "Games Played",
|
label: "Games Played",
|
||||||
greenSide: theGreenSide[2].gamesPlayed,
|
greenSide: theGreenSide[2].gamesPlayed,
|
||||||
redSide: theRedSide[2].gamesPlayed,
|
redSide: theRedSide[2].gamesPlayed,
|
||||||
higherIsBetter: true,
|
higherIsBetter: true,
|
||||||
),
|
),
|
||||||
if (greenSideMode == Mode.player &&
|
if (greenSideMode != Mode.stats &&
|
||||||
redSideMode == Mode.player)
|
redSideMode != Mode.stats)
|
||||||
CompareThingy(
|
CompareThingy(
|
||||||
label: "Games Won",
|
label: "Games Won",
|
||||||
greenSide: theGreenSide[2].gamesWon,
|
greenSide: theGreenSide[2].gamesWon,
|
||||||
redSide: theRedSide[2].gamesWon,
|
redSide: theRedSide[2].gamesWon,
|
||||||
higherIsBetter: true,
|
higherIsBetter: true,
|
||||||
),
|
),
|
||||||
if (greenSideMode == Mode.player &&
|
if (greenSideMode != Mode.stats &&
|
||||||
redSideMode == Mode.player)
|
redSideMode != Mode.stats)
|
||||||
CompareThingy(
|
CompareThingy(
|
||||||
label: "WR %",
|
label: "WR %",
|
||||||
greenSide:
|
greenSide:
|
||||||
|
@ -413,8 +437,8 @@ class CompareState extends State<CompareView> {
|
||||||
),
|
),
|
||||||
if (theGreenSide[2].gamesPlayed > 9 &&
|
if (theGreenSide[2].gamesPlayed > 9 &&
|
||||||
theRedSide[2].gamesPlayed > 9 &&
|
theRedSide[2].gamesPlayed > 9 &&
|
||||||
greenSideMode == Mode.player &&
|
greenSideMode != Mode.stats &&
|
||||||
redSideMode == Mode.player)
|
redSideMode != Mode.stats)
|
||||||
CompareThingy(
|
CompareThingy(
|
||||||
label: "Glicko",
|
label: "Glicko",
|
||||||
greenSide: theGreenSide[2].glicko!,
|
greenSide: theGreenSide[2].glicko!,
|
||||||
|
@ -424,8 +448,8 @@ class CompareState extends State<CompareView> {
|
||||||
),
|
),
|
||||||
if (theGreenSide[2].gamesPlayed > 9 &&
|
if (theGreenSide[2].gamesPlayed > 9 &&
|
||||||
theRedSide[2].gamesPlayed > 9 &&
|
theRedSide[2].gamesPlayed > 9 &&
|
||||||
greenSideMode == Mode.player &&
|
greenSideMode != Mode.stats &&
|
||||||
redSideMode == Mode.player)
|
redSideMode != Mode.stats)
|
||||||
CompareThingy(
|
CompareThingy(
|
||||||
label: "RD",
|
label: "RD",
|
||||||
greenSide: theGreenSide[2].rd!,
|
greenSide: theGreenSide[2].rd!,
|
||||||
|
@ -575,8 +599,8 @@ class CompareState extends State<CompareView> {
|
||||||
),
|
),
|
||||||
if (theGreenSide[2].gamesPlayed > 9 &&
|
if (theGreenSide[2].gamesPlayed > 9 &&
|
||||||
theGreenSide[2].gamesPlayed > 9 &&
|
theGreenSide[2].gamesPlayed > 9 &&
|
||||||
greenSideMode == Mode.player &&
|
greenSideMode != Mode.stats &&
|
||||||
redSideMode == Mode.player)
|
redSideMode != Mode.stats)
|
||||||
CompareThingy(
|
CompareThingy(
|
||||||
label: "Acc. of Est.",
|
label: "Acc. of Est.",
|
||||||
greenSide: theGreenSide[2].esttracc!,
|
greenSide: theGreenSide[2].esttracc!,
|
||||||
|
@ -781,7 +805,8 @@ class CompareState extends State<CompareView> {
|
||||||
fontFamily: "Eurostile Round Extended",
|
fontFamily: "Eurostile Round Extended",
|
||||||
fontSize: bigScreen ? 42 : 28)),
|
fontSize: bigScreen ? 42 : 28)),
|
||||||
),
|
),
|
||||||
if (greenSideMode == Mode.player && redSideMode == Mode.player)
|
if (greenSideMode != Mode.stats && redSideMode != Mode.stats &&
|
||||||
|
theGreenSide[2].gamesPlayed > 9 && theRedSide[2].gamesPlayed > 9)
|
||||||
CompareThingy(
|
CompareThingy(
|
||||||
label: "By Glicko",
|
label: "By Glicko",
|
||||||
greenSide: getWinrateByTR(
|
greenSide: getWinrateByTR(
|
||||||
|
@ -803,15 +828,15 @@ class CompareState extends State<CompareView> {
|
||||||
label: "By Est. TR",
|
label: "By Est. TR",
|
||||||
greenSide: getWinrateByTR(
|
greenSide: getWinrateByTR(
|
||||||
theGreenSide[2].estTr!.estglicko,
|
theGreenSide[2].estTr!.estglicko,
|
||||||
theGreenSide[2].rd!,
|
theGreenSide[2].rd ?? noTrRd,
|
||||||
theRedSide[2].estTr!.estglicko,
|
theRedSide[2].estTr!.estglicko,
|
||||||
theRedSide[2].rd!) *
|
theRedSide[2].rd ?? noTrRd) *
|
||||||
100,
|
100,
|
||||||
redSide: getWinrateByTR(
|
redSide: getWinrateByTR(
|
||||||
theRedSide[2].estTr!.estglicko,
|
theRedSide[2].estTr!.estglicko,
|
||||||
theRedSide[2].rd!,
|
theRedSide[2].rd ?? noTrRd,
|
||||||
theGreenSide[2].estTr!.estglicko,
|
theGreenSide[2].estTr!.estglicko,
|
||||||
theGreenSide[2].rd!) *
|
theGreenSide[2].rd ?? noTrRd) *
|
||||||
100,
|
100,
|
||||||
fractionDigits: 2,
|
fractionDigits: 2,
|
||||||
higherIsBetter: true,
|
higherIsBetter: true,
|
||||||
|
@ -834,7 +859,7 @@ class PlayerSelector extends StatelessWidget {
|
||||||
final Function fetch;
|
final Function fetch;
|
||||||
final Function change;
|
final Function change;
|
||||||
final Function updateState;
|
final Function updateState;
|
||||||
const PlayerSelector(
|
PlayerSelector(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.data,
|
required this.data,
|
||||||
required this.mode,
|
required this.mode,
|
||||||
|
@ -845,16 +870,30 @@ class PlayerSelector extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final TextEditingController playerController = TextEditingController();
|
final TextEditingController playerController = TextEditingController();
|
||||||
|
String underFieldString = "";
|
||||||
if (!listEquals(data, [null, null, null])){
|
if (!listEquals(data, [null, null, null])){
|
||||||
switch (mode){
|
switch (mode){
|
||||||
case Mode.player:
|
case Mode.player:
|
||||||
playerController.text = data[0] != null ? data[0].username : "???";
|
playerController.text = data[0] != null ? data[0].username : "";
|
||||||
break;
|
break;
|
||||||
case Mode.stats:
|
case Mode.stats:
|
||||||
playerController.text = "${data[2].apm} ${data[2].pps} ${data[2].vs}";
|
playerController.text = "${data[2].apm} ${data[2].pps} ${data[2].vs}";
|
||||||
break;
|
break;
|
||||||
case Mode.averages:
|
case Mode.averages:
|
||||||
playerController.text = "average";
|
playerController.text = "\$avg${data[2].rank.toUpperCase()}";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!listEquals(data, [null, null, null])){
|
||||||
|
switch (mode){
|
||||||
|
case Mode.player:
|
||||||
|
underFieldString = data[0] != null ? data[0].toString() : "???";
|
||||||
|
break;
|
||||||
|
case Mode.stats:
|
||||||
|
underFieldString = "${data[2].apm} APM, ${data[2].pps} PPS, ${data[2].vs} VS";
|
||||||
|
break;
|
||||||
|
case Mode.averages:
|
||||||
|
underFieldString = "Average ${data[2].rank.toUpperCase()} rank";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -867,11 +906,20 @@ class PlayerSelector extends StatelessWidget {
|
||||||
controller: playerController,
|
controller: playerController,
|
||||||
decoration: const InputDecoration(counter: Offstage()),
|
decoration: const InputDecoration(counter: Offstage()),
|
||||||
onSubmitted: (String value) {
|
onSubmitted: (String value) {
|
||||||
|
underFieldString = "Fetching...";
|
||||||
fetch(value);
|
fetch(value);
|
||||||
}),
|
}),
|
||||||
if (data[0] != null && data[1] == null)
|
if (data[0] != null && data[1] != null)
|
||||||
Text(
|
Padding(
|
||||||
data[0].toString(),
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: DropdownButton(
|
||||||
|
items: data[1],
|
||||||
|
value: data[0],
|
||||||
|
onChanged: (value) => change(value!),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else Text(
|
||||||
|
underFieldString,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
shadows: <Shadow>[
|
shadows: <Shadow>[
|
||||||
Shadow(
|
Shadow(
|
||||||
|
@ -887,15 +935,6 @@ class PlayerSelector extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (data[0] != null && data[1] != null)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 8.0),
|
|
||||||
child: DropdownButton(
|
|
||||||
items: data[1],
|
|
||||||
value: data[0],
|
|
||||||
onChanged: (value) => change(value!),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.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/services/tetrio_crud.dart';
|
import 'package:tetra_stats/services/tetrio_crud.dart';
|
||||||
import 'package:tetra_stats/services/crud_exceptions.dart';
|
import 'package:tetra_stats/services/crud_exceptions.dart';
|
||||||
import 'package:tetra_stats/views/tl_leaderboard_view.dart' show TLLeaderboardView;
|
import 'package:tetra_stats/views/tl_leaderboard_view.dart' show TLLeaderboardView;
|
||||||
|
@ -28,7 +29,7 @@ const givenTextHeightByScreenPercentage = 0.3;
|
||||||
final NumberFormat timeInSec = NumberFormat("#,###.###s.");
|
final NumberFormat timeInSec = NumberFormat("#,###.###s.");
|
||||||
final NumberFormat f2 = NumberFormat.decimalPatternDigits(decimalDigits: 2);
|
final NumberFormat f2 = NumberFormat.decimalPatternDigits(decimalDigits: 2);
|
||||||
final NumberFormat f4 = NumberFormat.decimalPatternDigits(decimalDigits: 4);
|
final NumberFormat f4 = NumberFormat.decimalPatternDigits(decimalDigits: 4);
|
||||||
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
|
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale).add_Hms();
|
||||||
|
|
||||||
class MainView extends StatefulWidget {
|
class MainView extends StatefulWidget {
|
||||||
final String? player;
|
final String? player;
|
||||||
|
@ -46,14 +47,6 @@ Future<void> copyToClipboard(String text) async {
|
||||||
|
|
||||||
class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
final bodyGlobalKey = GlobalKey();
|
final bodyGlobalKey = GlobalKey();
|
||||||
final List<Widget> myTabs = [
|
|
||||||
const Tab(text: "Tetra League"),
|
|
||||||
const Tab(text: "TL Records"),
|
|
||||||
const Tab(text: "History"),
|
|
||||||
const Tab(text: "40 Lines"),
|
|
||||||
const Tab(text: "Blitz"),
|
|
||||||
const Tab(text: "Other"),
|
|
||||||
];
|
|
||||||
bool _searchBoolean = false;
|
bool _searchBoolean = false;
|
||||||
late TabController _tabController;
|
late TabController _tabController;
|
||||||
late ScrollController _scrollController;
|
late ScrollController _scrollController;
|
||||||
|
@ -182,6 +175,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final t = Translations.of(context);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
drawer: widget.player == null ? NavDrawer(changePlayer) : null,
|
drawer: widget.player == null ? NavDrawer(changePlayer) : null,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
@ -227,21 +221,21 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
),
|
),
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
||||||
const PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: "refresh",
|
value: "refresh",
|
||||||
child: Text('Refresh'),
|
child: Text(t.refresh),
|
||||||
),
|
),
|
||||||
const PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: "/states",
|
value: "/states",
|
||||||
child: Text('Show stored data'),
|
child: Text(t.showStoredData),
|
||||||
),
|
),
|
||||||
const PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: "/calc",
|
value: "/calc",
|
||||||
child: Text('Stats Calculator'),
|
child: Text(t.statsCalc),
|
||||||
),
|
),
|
||||||
const PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: "/settings",
|
value: "/settings",
|
||||||
child: Text('Settings'),
|
child: Text(t.settings),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
|
@ -258,22 +252,9 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
switch (snapshot.connectionState) {
|
switch (snapshot.connectionState) {
|
||||||
case ConnectionState.none:
|
case ConnectionState.none:
|
||||||
return const Center(
|
|
||||||
child: Text('none case of FutureBuilder',
|
|
||||||
style: TextStyle(
|
|
||||||
fontFamily: "Eurostile Round Extended",
|
|
||||||
fontSize: 42),
|
|
||||||
textAlign: TextAlign.center));
|
|
||||||
case ConnectionState.waiting:
|
case ConnectionState.waiting:
|
||||||
return const Center(
|
|
||||||
child: CircularProgressIndicator(color: Colors.white));
|
|
||||||
case ConnectionState.active:
|
case ConnectionState.active:
|
||||||
return const Center(
|
return const Center(child: CircularProgressIndicator(color: Colors.white));
|
||||||
child: Text('active case of FutureBuilder',
|
|
||||||
style: TextStyle(
|
|
||||||
fontFamily: "Eurostile Round Extended",
|
|
||||||
fontSize: 42),
|
|
||||||
textAlign: TextAlign.center));
|
|
||||||
case ConnectionState.done:
|
case ConnectionState.done:
|
||||||
//bool bigScreen = MediaQuery.of(context).size.width > 1024;
|
//bool bigScreen = MediaQuery.of(context).size.width > 1024;
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
|
@ -303,7 +284,14 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
child: TabBar(
|
child: TabBar(
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
isScrollable: true,
|
isScrollable: true,
|
||||||
tabs: myTabs,
|
tabs: [
|
||||||
|
Tab(text: t.tetraLeague),
|
||||||
|
Tab(text: t.tlRecords),
|
||||||
|
Tab(text: t.history),
|
||||||
|
Tab(text: t.sprint),
|
||||||
|
Tab(text: t.blitz),
|
||||||
|
Tab(text: t.other),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
@ -314,7 +302,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
TLThingy(
|
TLThingy(
|
||||||
tl: snapshot.data![0].tlSeason1,
|
tl: snapshot.data![0].tlSeason1,
|
||||||
userID: snapshot.data![0].userId,
|
userID: snapshot.data![0].userId,
|
||||||
oldTl: snapshot.data![4],),
|
oldTl: snapshot.data![4]),
|
||||||
_TLRecords(userID: snapshot.data![0].userId, data: snapshot.data![3]),
|
_TLRecords(userID: snapshot.data![0].userId, data: snapshot.data![3]),
|
||||||
_History(states: snapshot.data![2], update: _justUpdate),
|
_History(states: snapshot.data![2], update: _justUpdate),
|
||||||
_RecordThingy(
|
_RecordThingy(
|
||||||
|
@ -406,7 +394,6 @@ class _NavDrawerState extends State<NavDrawer> {
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
switch (snapshot.connectionState) {
|
switch (snapshot.connectionState) {
|
||||||
case ConnectionState.none:
|
case ConnectionState.none:
|
||||||
return const Center(child: Text('none case of StreamBuilder'));
|
|
||||||
case ConnectionState.waiting:
|
case ConnectionState.waiting:
|
||||||
case ConnectionState.active:
|
case ConnectionState.active:
|
||||||
final allPlayers = (snapshot.data != null)
|
final allPlayers = (snapshot.data != null)
|
||||||
|
@ -436,7 +423,7 @@ class _NavDrawerState extends State<NavDrawer> {
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.leaderboard),
|
leading: const Icon(Icons.leaderboard),
|
||||||
title: const Text("Tetra League leaderboard"),
|
title: Text(t.tlLeaderboard),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
|
@ -501,7 +488,7 @@ class _TLRecords extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);},
|
);},
|
||||||
)]
|
)]
|
||||||
: [const Center(child: Text("No records", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))],
|
: [Center(child: Text(t.noRecords, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -527,10 +514,10 @@ class _History extends StatelessWidget{
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
if(chartsData[chartsIndex].value!.length > 1) _HistoryChartThigy(data: chartsData[chartsIndex].value!, title: "ss", yAxisTitle: chartsShortTitles[chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(),)
|
if(chartsData[chartsIndex].value!.length > 1) _HistoryChartThigy(data: chartsData[chartsIndex].value!, title: "ss", yAxisTitle: chartsShortTitles[chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(),)
|
||||||
else const Center(child: Text("Not enough data", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))
|
else Center(child: Text(t.notEnoughData, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
] : [const Center(child: Text("No history saved", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))]);
|
] : [Center(child: Text(t.noHistorySaved, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,12 +586,12 @@ class _RecordThingy extends StatelessWidget {
|
||||||
children: (record != null)
|
children: (record != null)
|
||||||
? [
|
? [
|
||||||
if (record!.stream.contains("40l"))
|
if (record!.stream.contains("40l"))
|
||||||
Text("40 Lines",
|
Text(t.sprint,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: "Eurostile Round Extended",
|
fontFamily: "Eurostile Round Extended",
|
||||||
fontSize: bigScreen ? 42 : 28))
|
fontSize: bigScreen ? 42 : 28))
|
||||||
else if (record!.stream.contains("blitz"))
|
else if (record!.stream.contains("blitz"))
|
||||||
Text("Blitz",
|
Text(t.blitz,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: "Eurostile Round Extended",
|
fontFamily: "Eurostile Round Extended",
|
||||||
fontSize: bigScreen ? 42 : 28)),
|
fontSize: bigScreen ? 42 : 28)),
|
||||||
|
@ -629,7 +616,7 @@ class _RecordThingy extends StatelessWidget {
|
||||||
playerStatLabel: "Leaderboard Placement",
|
playerStatLabel: "Leaderboard Placement",
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
higherIsBetter: false),
|
higherIsBetter: false),
|
||||||
Text("Obtained ${dateFormat.format(record!.timestamp!)}",
|
Text(t.obtainDate(date: dateFormat.format(record!.timestamp!)),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontFamily: "Eurostile Round",
|
fontFamily: "Eurostile Round",
|
||||||
|
@ -647,53 +634,53 @@ class _RecordThingy extends StatelessWidget {
|
||||||
if (record!.stream.contains("blitz"))
|
if (record!.stream.contains("blitz"))
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat: record!.endContext!.level,
|
playerStat: record!.endContext!.level,
|
||||||
playerStatLabel: "Level",
|
playerStatLabel: t.statCellNum.level,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
higherIsBetter: true,),
|
higherIsBetter: true,),
|
||||||
if (record!.stream.contains("blitz"))
|
if (record!.stream.contains("blitz"))
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat: record!.endContext!.spp,
|
playerStat: record!.endContext!.spp,
|
||||||
playerStatLabel: "Score\nPer Piece",
|
playerStatLabel: t.statCellNum.spp,
|
||||||
fractionDigits: 2,
|
fractionDigits: 2,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
higherIsBetter: true,),
|
higherIsBetter: true,),
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat: record!.endContext!.piecesPlaced,
|
playerStat: record!.endContext!.piecesPlaced,
|
||||||
playerStatLabel: "Pieces\nPlaced",
|
playerStatLabel: t.statCellNum.pieces,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
higherIsBetter: true,),
|
higherIsBetter: true,),
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat: record!.endContext!.pps,
|
playerStat: record!.endContext!.pps,
|
||||||
playerStatLabel: "Pieces\nPer Second",
|
playerStatLabel: t.statCellNum.pps,
|
||||||
fractionDigits: 2,
|
fractionDigits: 2,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
higherIsBetter: true,),
|
higherIsBetter: true,),
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat: record!.endContext!.finesse.faults,
|
playerStat: record!.endContext!.finesse.faults,
|
||||||
playerStatLabel: "Finesse\nFaults",
|
playerStatLabel: t.statCellNum.finesseFaults,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
higherIsBetter: false,),
|
higherIsBetter: false,),
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat:
|
playerStat:
|
||||||
record!.endContext!.finessePercentage * 100,
|
record!.endContext!.finessePercentage * 100,
|
||||||
playerStatLabel: "Finesse\nPercentage",
|
playerStatLabel: t.statCellNum.finessePercentage,
|
||||||
fractionDigits: 2,
|
fractionDigits: 2,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
higherIsBetter: true,),
|
higherIsBetter: true,),
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat: record!.endContext!.inputs,
|
playerStat: record!.endContext!.inputs,
|
||||||
playerStatLabel: "Key\nPresses",
|
playerStatLabel: t.statCellNum.keys,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
higherIsBetter: false,),
|
higherIsBetter: false,),
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat: record!.endContext!.kpp,
|
playerStat: record!.endContext!.kpp,
|
||||||
playerStatLabel: "KP Per\nPiece",
|
playerStatLabel: t.statCellNum.kpp,
|
||||||
fractionDigits: 2,
|
fractionDigits: 2,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
higherIsBetter: false,),
|
higherIsBetter: false,),
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat: record!.endContext!.kps,
|
playerStat: record!.endContext!.kps,
|
||||||
playerStatLabel: "KP Per\nSecond",
|
playerStatLabel: t.statCellNum.kps,
|
||||||
fractionDigits: 2,
|
fractionDigits: 2,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
higherIsBetter: true,),
|
higherIsBetter: true,),
|
||||||
|
@ -713,8 +700,8 @@ class _RecordThingy extends StatelessWidget {
|
||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
MainAxisAlignment.spaceBetween,
|
MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
const Text("All Clears:",
|
Text("${t.numOfGameActions.pc}:",
|
||||||
style: TextStyle(fontSize: 24)),
|
style: const TextStyle(fontSize: 24)),
|
||||||
Text(
|
Text(
|
||||||
record!.endContext!.clears.allClears
|
record!.endContext!.clears.allClears
|
||||||
.toString(),
|
.toString(),
|
||||||
|
@ -726,8 +713,8 @@ class _RecordThingy extends StatelessWidget {
|
||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
MainAxisAlignment.spaceBetween,
|
MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
const Text("Holds:",
|
Text("${t.numOfGameActions.hold}:",
|
||||||
style: TextStyle(fontSize: 24)),
|
style: const TextStyle(fontSize: 24)),
|
||||||
Text(
|
Text(
|
||||||
record!.endContext!.holds.toString(),
|
record!.endContext!.holds.toString(),
|
||||||
style: const TextStyle(fontSize: 24),
|
style: const TextStyle(fontSize: 24),
|
||||||
|
@ -738,8 +725,8 @@ class _RecordThingy extends StatelessWidget {
|
||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
MainAxisAlignment.spaceBetween,
|
MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
const Text("T-spins total:",
|
Text("${t.numOfGameActions.tspinsTotal}:",
|
||||||
style: TextStyle(fontSize: 24)),
|
style: const TextStyle(fontSize: 24)),
|
||||||
Text(
|
Text(
|
||||||
record!.endContext!.tSpins.toString(),
|
record!.endContext!.tSpins.toString(),
|
||||||
style: const TextStyle(fontSize: 24),
|
style: const TextStyle(fontSize: 24),
|
||||||
|
@ -841,8 +828,8 @@ class _RecordThingy extends StatelessWidget {
|
||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
MainAxisAlignment.spaceBetween,
|
MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
const Text("Line clears:",
|
Text("${t.numOfGameActions.lineClears}:",
|
||||||
style: TextStyle(fontSize: 24)),
|
style: const TextStyle(fontSize: 24)),
|
||||||
Text(
|
Text(
|
||||||
record!.endContext!.lines.toString(),
|
record!.endContext!.lines.toString(),
|
||||||
style: const TextStyle(fontSize: 24),
|
style: const TextStyle(fontSize: 24),
|
||||||
|
@ -906,7 +893,7 @@ class _RecordThingy extends StatelessWidget {
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
const Text("No record", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28))
|
Text(t.noRecord, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -930,7 +917,7 @@ class _OtherThingy extends StatelessWidget {
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Text("Other info",
|
Text(t.other,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: "Eurostile Round Extended",
|
fontFamily: "Eurostile Round Extended",
|
||||||
fontSize: bigScreen ? 42 : 28)),
|
fontSize: bigScreen ? 42 : 28)),
|
||||||
|
@ -939,17 +926,17 @@ class _OtherThingy extends StatelessWidget {
|
||||||
padding: const EdgeInsets.fromLTRB(0, 48, 0, 48),
|
padding: const EdgeInsets.fromLTRB(0, 48, 0, 48),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Text("Zen",
|
Text(t.zen,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: "Eurostile Round Extended",
|
fontFamily: "Eurostile Round Extended",
|
||||||
fontSize: bigScreen ? 42 : 28)),
|
fontSize: bigScreen ? 42 : 28)),
|
||||||
Text(
|
Text(
|
||||||
"Level ${NumberFormat.decimalPattern().format(zen!.level)}",
|
"${t.statCellNum.level} ${NumberFormat.decimalPattern().format(zen!.level)}",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: "Eurostile Round Extended",
|
fontFamily: "Eurostile Round Extended",
|
||||||
fontSize: bigScreen ? 42 : 28)),
|
fontSize: bigScreen ? 42 : 28)),
|
||||||
Text(
|
Text(
|
||||||
"Score ${NumberFormat.decimalPattern().format(zen!.score)}",
|
"${t.statCellNum.score} ${NumberFormat.decimalPattern().format(zen!.score)}",
|
||||||
style: const TextStyle(fontSize: 18)),
|
style: const TextStyle(fontSize: 18)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -959,7 +946,7 @@ class _OtherThingy extends StatelessWidget {
|
||||||
padding: const EdgeInsets.fromLTRB(0, 0, 0, 48),
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 48),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Text("Bio",
|
Text(t.bio,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: "Eurostile Round Extended",
|
fontFamily: "Eurostile Round Extended",
|
||||||
fontSize: bigScreen ? 42 : 28)),
|
fontSize: bigScreen ? 42 : 28)),
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
import 'package:tetra_stats/services/crud_exceptions.dart';
|
import 'package:tetra_stats/services/crud_exceptions.dart';
|
||||||
import 'package:tetra_stats/services/tetrio_crud.dart';
|
import 'package:tetra_stats/services/tetrio_crud.dart';
|
||||||
|
|
||||||
|
@ -65,6 +66,12 @@ class SettingsState extends State<SettingsView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final t = Translations.of(context);
|
||||||
|
List<DropdownMenuItem<AppLocale>>? locales = <DropdownMenuItem<AppLocale>>[];
|
||||||
|
for (var v in AppLocale.values){
|
||||||
|
locales.add(DropdownMenuItem<AppLocale>(
|
||||||
|
value: v, child: Text(t.locales[v.languageTag]!)));
|
||||||
|
}
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Settings"),
|
title: const Text("Settings"),
|
||||||
|
@ -212,6 +219,14 @@ class SettingsState extends State<SettingsView> {
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
title: const Text("Language"),
|
||||||
|
trailing: DropdownButton(
|
||||||
|
items: locales,
|
||||||
|
value: LocaleSettings.currentLocale,
|
||||||
|
onChanged: (value) => LocaleSettings.setLocale(value!),
|
||||||
|
),
|
||||||
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text("About app"),
|
title: const Text("About app"),
|
||||||
|
|
|
@ -3,6 +3,8 @@ import 'package:intl/intl.dart';
|
||||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
||||||
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
|
import 'package:tetra_stats/views/calc_view.dart';
|
||||||
import 'package:tetra_stats/widgets/stat_sell_num.dart';
|
import 'package:tetra_stats/widgets/stat_sell_num.dart';
|
||||||
|
|
||||||
var fDiff = NumberFormat("+#,###.###;-#,###.###");
|
var fDiff = NumberFormat("+#,###.###;-#,###.###");
|
||||||
|
@ -17,6 +19,8 @@ class TLThingy extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final t = Translations.of(context);
|
||||||
|
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
|
||||||
return LayoutBuilder(builder: (context, constraints) {
|
return LayoutBuilder(builder: (context, constraints) {
|
||||||
bool bigScreen = constraints.maxWidth > 768;
|
bool bigScreen = constraints.maxWidth > 768;
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
|
@ -26,8 +30,8 @@ class TLThingy extends StatelessWidget {
|
||||||
return Column(
|
return Column(
|
||||||
children: (tl.gamesPlayed > 0)
|
children: (tl.gamesPlayed > 0)
|
||||||
? [
|
? [
|
||||||
Text("Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
Text(t.tetraLeague, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
||||||
if (oldTl != null) Text("Comparing with data from ${DateFormat.yMMMd().add_Hms().format(oldTl!.timestamp)}"),
|
if (oldTl != null) Text(t.comparingWith(date: dateFormat.format(oldTl!.timestamp))),
|
||||||
if (tl.gamesPlayed >= 10)
|
if (tl.gamesPlayed >= 10)
|
||||||
Wrap(
|
Wrap(
|
||||||
direction: Axis.horizontal,
|
direction: Axis.horizontal,
|
||||||
|
@ -51,7 +55,7 @@ class TLThingy extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"Top ${f2.format(tl.percentile * 100)}% (${tl.percentileRank.toUpperCase()}) • Top Rank: ${tl.bestRank.toUpperCase()} • Glicko: ${f2.format(tl.glicko!)}±${f2.format(tl.rd!)}${tl.decaying ? ' • Decaying' : ''}",
|
"${t.top} ${f2.format(tl.percentile * 100)}% (${tl.percentileRank.toUpperCase()}) • ${t.topRank}: ${tl.bestRank.toUpperCase()} • Glicko: ${f2.format(tl.glicko!)}±${f2.format(tl.rd!)}${tl.decaying ? ' • ${t.decaying}' : ''}",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -73,7 +77,7 @@ class TLThingy extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (tl.gamesPlayed < 10)
|
if (tl.gamesPlayed < 10)
|
||||||
Text("${10 - tl.gamesPlayed} games until being ranked",
|
Text(t.gamesUntilRanked(left: 10 - tl.gamesPlayed),
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -90,21 +94,21 @@ class TLThingy extends StatelessWidget {
|
||||||
crossAxisAlignment: WrapCrossAlignment.start,
|
crossAxisAlignment: WrapCrossAlignment.start,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
children: [
|
children: [
|
||||||
if (tl.apm != null) StatCellNum(playerStat: tl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Attack\nPer Minute", higherIsBetter: true, oldPlayerStat: oldTl?.apm),
|
if (tl.apm != null) StatCellNum(playerStat: tl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.apm, higherIsBetter: true, oldPlayerStat: oldTl?.apm),
|
||||||
if (tl.pps != null) StatCellNum(playerStat: tl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Pieces\nPer Second", higherIsBetter: true, oldPlayerStat: oldTl?.pps),
|
if (tl.pps != null) StatCellNum(playerStat: tl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.pps, higherIsBetter: true, oldPlayerStat: oldTl?.pps),
|
||||||
if (tl.vs != null) StatCellNum(playerStat: tl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Versus\nScore", higherIsBetter: true, oldPlayerStat: oldTl?.vs),
|
if (tl.vs != null) StatCellNum(playerStat: tl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.vs, higherIsBetter: true, oldPlayerStat: oldTl?.vs),
|
||||||
if (tl.standing > 0) StatCellNum(playerStat: tl.standing, isScreenBig: bigScreen, playerStatLabel: "Leaderboard\nplacement", higherIsBetter: false, oldPlayerStat: oldTl?.standing),
|
if (tl.standing > 0) StatCellNum(playerStat: tl.standing, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.lbp, higherIsBetter: false, oldPlayerStat: oldTl?.standing),
|
||||||
if (tl.standingLocal > 0) StatCellNum(playerStat: tl.standingLocal, isScreenBig: bigScreen, playerStatLabel: "Country LB\nplacement", higherIsBetter: false, oldPlayerStat: oldTl?.standingLocal),
|
if (tl.standingLocal > 0) StatCellNum(playerStat: tl.standingLocal, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.lbpc, higherIsBetter: false, oldPlayerStat: oldTl?.standingLocal),
|
||||||
StatCellNum(playerStat: tl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Games\nplayed", higherIsBetter: true, oldPlayerStat: oldTl?.gamesPlayed),
|
StatCellNum(playerStat: tl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.gamesPlayed, higherIsBetter: true, oldPlayerStat: oldTl?.gamesPlayed),
|
||||||
StatCellNum(playerStat: tl.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nwon", higherIsBetter: true, oldPlayerStat: oldTl?.gamesWon),
|
StatCellNum(playerStat: tl.gamesWon, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.gamesWonTL, higherIsBetter: true, oldPlayerStat: oldTl?.gamesWon),
|
||||||
StatCellNum(playerStat: tl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Winrate\nprecentage", higherIsBetter: true, oldPlayerStat: oldTl != null ? oldTl!.winrate*100 : null),
|
StatCellNum(playerStat: tl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.winrate, higherIsBetter: true, oldPlayerStat: oldTl != null ? oldTl!.winrate*100 : null),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (tl.nerdStats != null)
|
if (tl.nerdStats != null)
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
Text("Nerd Stats", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
Text(t.nerdStats, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(0, 40, 0, 0),
|
padding: const EdgeInsets.fromLTRB(0, 40, 0, 0),
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
|
|
|
@ -1,23 +1,16 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.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/views/compare_view.dart';
|
import 'package:tetra_stats/views/compare_view.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'dart:developer' as developer;
|
import 'dart:developer' as developer;
|
||||||
import 'package:tetra_stats/widgets/stat_sell_num.dart';
|
import 'package:tetra_stats/widgets/stat_sell_num.dart';
|
||||||
|
|
||||||
extension StringExtension on String {
|
|
||||||
String capitalize() {
|
|
||||||
return "${this[0].toUpperCase()}${substring(1).toLowerCase()}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> copyToClipboard(String text) async {
|
Future<void> copyToClipboard(String text) async {
|
||||||
await Clipboard.setData(ClipboardData(text: text));
|
await Clipboard.setData(ClipboardData(text: text));
|
||||||
}
|
}
|
||||||
|
|
||||||
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
|
|
||||||
|
|
||||||
class UserThingy extends StatelessWidget {
|
class UserThingy extends StatelessWidget {
|
||||||
final TetrioPlayer player;
|
final TetrioPlayer player;
|
||||||
final bool showStateTimestamp;
|
final bool showStateTimestamp;
|
||||||
|
@ -26,6 +19,8 @@ class UserThingy extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final t = Translations.of(context);
|
||||||
|
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
|
||||||
return LayoutBuilder(builder: (context, constraints) {
|
return LayoutBuilder(builder: (context, constraints) {
|
||||||
bool bigScreen = constraints.maxWidth > 768;
|
bool bigScreen = constraints.maxWidth > 768;
|
||||||
double bannerHeight = bigScreen ? 240 : 120;
|
double bannerHeight = bigScreen ? 240 : 120;
|
||||||
|
@ -99,12 +94,12 @@ class UserThingy extends StatelessWidget {
|
||||||
child: Text(player.userId, style: const TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: 14)),
|
child: Text(player.userId, style: const TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: 14)),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
copyToClipboard(player.userId);
|
copyToClipboard(player.userId);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Copied to clipboard!")));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.copiedToClipboard)));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
showStateTimestamp
|
showStateTimestamp
|
||||||
? Text("Fetched ${dateFormat.format(player.state)}")
|
? Text(t.fetchDate(date: dateFormat.format(player.state)))
|
||||||
: Wrap(direction: Axis.horizontal, alignment: WrapAlignment.center, spacing: 25, crossAxisAlignment: WrapCrossAlignment.start, children: [
|
: Wrap(direction: Axis.horizontal, alignment: WrapAlignment.center, spacing: 25, crossAxisAlignment: WrapCrossAlignment.start, children: [
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: teto.isPlayerTracking(player.userId),
|
future: teto.isPlayerTracking(player.userId),
|
||||||
|
@ -121,10 +116,10 @@ class UserThingy extends StatelessWidget {
|
||||||
icon: const Icon(Icons.person_remove),
|
icon: const Icon(Icons.person_remove),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
teto.deletePlayerToTrack(player.userId).then((value) => setState());
|
teto.deletePlayerToTrack(player.userId).then((value) => setState());
|
||||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Removed from tracking list!")));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.stoppedBeingTracked)));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Text("Stop tracking")
|
Text(t.stopTracking, textAlign: TextAlign.center)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -135,10 +130,10 @@ class UserThingy extends StatelessWidget {
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
teto.addPlayerToTrack(player).then((value) => setState());
|
teto.addPlayerToTrack(player).then((value) => setState());
|
||||||
teto.storeState(player);
|
teto.storeState(player);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Added to tracking list!")));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.becameTracked)));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Text("Track")
|
Text(t.track, textAlign: TextAlign.center)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -157,7 +152,7 @@ class UserThingy extends StatelessWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Text("Compare")
|
Text(t.compare, textAlign: TextAlign.center)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
|
@ -173,7 +168,7 @@ class UserThingy extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat: player.level,
|
playerStat: player.level,
|
||||||
playerStatLabel: "XP Level",
|
playerStatLabel: t.statCellNum.xpLevel,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
alertWidgets: [Text("${NumberFormat.decimalPatternDigits(decimalDigits: 2).format(player.xp)} XP", style: const TextStyle(fontFamily: "Eurostile Round Extended"),), Text("Progress to next level: ${((player.level - player.level.floor()) * 100).toStringAsFixed(2)} %"), Text("Progress from 0 XP to level 5000: ${((player.xp / 67009017.7589378) * 100).toStringAsFixed(2)} %")],
|
alertWidgets: [Text("${NumberFormat.decimalPatternDigits(decimalDigits: 2).format(player.xp)} XP", style: const TextStyle(fontFamily: "Eurostile Round Extended"),), Text("Progress to next level: ${((player.level - player.level.floor()) * 100).toStringAsFixed(2)} %"), Text("Progress from 0 XP to level 5000: ${((player.xp / 67009017.7589378) * 100).toStringAsFixed(2)} %")],
|
||||||
higherIsBetter: true,
|
higherIsBetter: true,
|
||||||
|
@ -181,32 +176,32 @@ class UserThingy extends StatelessWidget {
|
||||||
if (player.gameTime >= Duration.zero)
|
if (player.gameTime >= Duration.zero)
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat: player.gameTime.inHours,
|
playerStat: player.gameTime.inHours,
|
||||||
playerStatLabel: "Hours\nPlayed",
|
playerStatLabel: t.statCellNum.hoursPlayed,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
alertWidgets: [Text("Exact gametime: ${player.gameTime.toString()}")],
|
alertWidgets: [Text("${t.exactGametime}: ${player.gameTime.toString()}")],
|
||||||
higherIsBetter: true,),
|
higherIsBetter: true,),
|
||||||
if (player.gamesPlayed >= 0)
|
if (player.gamesPlayed >= 0)
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat: player.gamesPlayed,
|
playerStat: player.gamesPlayed,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
playerStatLabel: "Online\nGames",
|
playerStatLabel: t.statCellNum.onlineGames,
|
||||||
higherIsBetter: true,),
|
higherIsBetter: true,),
|
||||||
if (player.gamesWon >= 0)
|
if (player.gamesWon >= 0)
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat: player.gamesWon,
|
playerStat: player.gamesWon,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
playerStatLabel: "Games\nWon",
|
playerStatLabel: t.statCellNum.gamesWon,
|
||||||
higherIsBetter: true,),
|
higherIsBetter: true,),
|
||||||
if (player.friendCount > 0)
|
if (player.friendCount > 0)
|
||||||
StatCellNum(
|
StatCellNum(
|
||||||
playerStat: player.friendCount,
|
playerStat: player.friendCount,
|
||||||
isScreenBig: bigScreen,
|
isScreenBig: bigScreen,
|
||||||
playerStatLabel: "Friends",
|
playerStatLabel: t.statCellNum.friends,
|
||||||
higherIsBetter: true,),
|
higherIsBetter: true,),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: Text(
|
: Text(
|
||||||
"BANNED",
|
t.bigRedBanned,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: "Eurostile Round Extended",
|
fontFamily: "Eurostile Round Extended",
|
||||||
|
@ -217,7 +212,7 @@ class UserThingy extends StatelessWidget {
|
||||||
),
|
),
|
||||||
if (player.badstanding != null && player.badstanding!)
|
if (player.badstanding != null && player.badstanding!)
|
||||||
Text(
|
Text(
|
||||||
"BAD STANDING",
|
t.bigRedBadStanding,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontFamily: "Eurostile Round Extended",
|
fontFamily: "Eurostile Round Extended",
|
||||||
|
@ -231,7 +226,7 @@ class UserThingy extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
"${player.country != null ? "${player.country?.toUpperCase()} • " : ""}${player.role.capitalize()} account ${player.registrationTime == null ? "that was from very beginning" : 'created ${dateFormat.format(player.registrationTime!)}'}${player.botmaster != null ? " by ${player.botmaster}" : ""} • ${player.supporterTier == 0 ? "Not a supporter" : "Supporter tier ${player.supporterTier}"}",
|
"${player.country != null ? "${player.country?.toUpperCase()} • " : ""}${t.playerRole[player.role]}${t.playerRoleAccount}${player.registrationTime == null ? t.wasFromBeginning : '${t.created} ${dateFormat.format(player.registrationTime!)}'}${player.botmaster != null ? " ${t.botCreatedBy} ${player.botmaster}" : ""} • ${player.supporterTier == 0 ? t.notSupporter : t.supporter(tier: player.supporterTier)}",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontFamily: "Eurostile Round",
|
fontFamily: "Eurostile Round",
|
||||||
|
@ -268,8 +263,8 @@ class UserThingy extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Image.asset("res/tetrio_badges/${badge.badgeId}.png"),
|
Image.asset("res/tetrio_badges/${badge.badgeId}.png"),
|
||||||
Text(badge.ts != null
|
Text(badge.ts != null
|
||||||
? "Obtained ${dateFormat.format(badge.ts!)}"
|
? t.obtainDate(date: dateFormat.format(badge.ts!))
|
||||||
: "That badge was assigned manualy by TETR.IO admins"),
|
: t.assignedManualy),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -277,7 +272,7 @@ class UserThingy extends StatelessWidget {
|
||||||
),
|
),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
TextButton(
|
TextButton(
|
||||||
child: const Text('OK'),
|
child: Text(t.popupActions.ok),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
|
|
49
pubspec.lock
49
pubspec.lock
|
@ -97,6 +97,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.3"
|
||||||
|
csv:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csv
|
||||||
|
sha256: "016b31a51a913744a0a1655c74ff13c9379e1200e246a03d96c81c5d9ed297b5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.2"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -230,6 +238,11 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
flutter_localizations:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -276,10 +289,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.18.1"
|
version: "0.18.0"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -288,6 +301,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.7"
|
version: "0.6.7"
|
||||||
|
json2yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: json2yaml
|
||||||
|
sha256: da94630fbc56079426fdd167ae58373286f603371075b69bf46d848d63ba3e51
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -501,6 +522,22 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.99"
|
version: "0.0.99"
|
||||||
|
slang:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: slang
|
||||||
|
sha256: a90af3c2a70ae7d302f47717c0578370e5b2e6040c84280c3e11c9221c2a34ae
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.20.0"
|
||||||
|
slang_flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: slang_flutter
|
||||||
|
sha256: f3fb0ffabc5119dbe39fb8ef134d0415a27b1da816f32f1f55c8b67d4e2ac1af
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.20.0"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -629,6 +666,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.4"
|
||||||
|
watcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: watcher
|
||||||
|
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
27
pubspec.yaml
27
pubspec.yaml
|
@ -2,18 +2,6 @@ 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'
|
||||||
|
|
||||||
# The following defines the version and build number for your application.
|
|
||||||
# A version number is three numbers separated by dots, like 1.2.43
|
|
||||||
# followed by an optional build number separated by a +.
|
|
||||||
# Both the version and the builder number may be overridden in flutter
|
|
||||||
# build by specifying --build-name and --build-number, respectively.
|
|
||||||
# In Android, build-name is used as versionName while build-number used as versionCode.
|
|
||||||
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
|
||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
|
|
||||||
# Read more about iOS versioning at
|
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
|
||||||
version: 0.2.0+4
|
version: 0.2.0+4
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
@ -29,6 +17,8 @@ dependencies:
|
||||||
http:
|
http:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_localizations:
|
||||||
|
sdk: flutter
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
vector_math: any
|
vector_math: any
|
||||||
sqflite: ^2.2.8+2
|
sqflite: ^2.2.8+2
|
||||||
|
@ -39,10 +29,12 @@ dependencies:
|
||||||
fl_chart: ^0.62.0
|
fl_chart: ^0.62.0
|
||||||
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.1
|
intl: ^0.18.0
|
||||||
syncfusion_flutter_gauges: ^22.1.34
|
syncfusion_flutter_gauges: ^22.1.34
|
||||||
file_selector: ^0.9.4
|
file_selector: ^0.9.4
|
||||||
file_picker: ^5.3.2
|
file_picker: ^5.3.2
|
||||||
|
slang: ^3.20.0
|
||||||
|
slang_flutter: ^3.20.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -69,6 +61,15 @@ flutter_launcher_icons:
|
||||||
generate: true
|
generate: true
|
||||||
image_path: "res/icons/app.png"
|
image_path: "res/icons/app.png"
|
||||||
|
|
||||||
|
targets:
|
||||||
|
$default:
|
||||||
|
builders:
|
||||||
|
slang_build_runner:
|
||||||
|
options:
|
||||||
|
input_directory: res/i18n
|
||||||
|
output_directory: lib/i18n
|
||||||
|
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
assets:
|
assets:
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
{
|
||||||
|
"locales(map)": {
|
||||||
|
"en": "English",
|
||||||
|
"ru": "Russian (Русский)"
|
||||||
|
},
|
||||||
|
"tetraLeague": "Tetra League",
|
||||||
|
"tlRecords": "TL Records",
|
||||||
|
"history": "History",
|
||||||
|
"sprint": "40 Lines",
|
||||||
|
"blitz": "Blitz",
|
||||||
|
"other": "Other",
|
||||||
|
"zen": "Zen",
|
||||||
|
"bio": "Bio",
|
||||||
|
"refresh": "Refresh",
|
||||||
|
"showStoredData": "Show stored data",
|
||||||
|
"statsCalc": "Stats Calculator",
|
||||||
|
"settings": "Settings",
|
||||||
|
"track": "Track",
|
||||||
|
"stopTracking": "Stop\ntracking",
|
||||||
|
"becameTracked": "Added to tracking list!",
|
||||||
|
"compare": "Compare",
|
||||||
|
"stoppedBeingTracked": "Removed from tracking list!",
|
||||||
|
"tlLeaderboard": "Tetra League leaderboard",
|
||||||
|
"noRecords": "No records",
|
||||||
|
"noRecord": "No record",
|
||||||
|
"notEnoughData": "Not enough data",
|
||||||
|
"noHistorySaved": "No history saved",
|
||||||
|
"obtainDate": "Obtained ${date}",
|
||||||
|
"fetchDate": "Fetched ${date}",
|
||||||
|
"exactGametime": "Exact gametime",
|
||||||
|
"bigRedBanned": "BANNED",
|
||||||
|
"bigRedBadStanding": "BAD STANDING",
|
||||||
|
"copiedToClipboard": "Copied to clipboard!",
|
||||||
|
"playerRoleAccount": " account ",
|
||||||
|
"wasFromBeginning": "that was from very beginning",
|
||||||
|
"created": "created",
|
||||||
|
"botCreatedBy": "by",
|
||||||
|
"notSupporter": "Not a supporter",
|
||||||
|
"assignedManualy": "That badge was assigned manualy by TETR.IO admins",
|
||||||
|
"supporter": "Supporter tier ${tier}",
|
||||||
|
"comparingWith": "Comparing with data from ${date}",
|
||||||
|
"top": "Top",
|
||||||
|
"topRank": "Top Rank",
|
||||||
|
"decaying": "Decaying",
|
||||||
|
"gamesUntilRanked": "${left} games until being ranked",
|
||||||
|
"nerdStats": "Nerd Stats",
|
||||||
|
"statCellNum":{
|
||||||
|
"xpLevel": "XP Level",
|
||||||
|
"hoursPlayed": "Hours\nPlayed",
|
||||||
|
"onlineGames": "Online\nGames",
|
||||||
|
"gamesWon": "Games\nWon",
|
||||||
|
"friends": "Friends",
|
||||||
|
"apm": "Attack\nPer Minute",
|
||||||
|
"vs": "Versus\nScore",
|
||||||
|
"lbp": "Leaderboard\nplacement",
|
||||||
|
"lbpc": "Country LB\nplacement",
|
||||||
|
"gamesPlayed": "Games\nplayed",
|
||||||
|
"gamesWonTL": "Games\nWon",
|
||||||
|
"winrate": "Winrate\nprecentage",
|
||||||
|
"level": "Level",
|
||||||
|
"score": "Score",
|
||||||
|
"spp": "Score\nPer Piece",
|
||||||
|
"pieces": "Pieces\nPlaced",
|
||||||
|
"pps": "Pieces\nPer Second",
|
||||||
|
"finesseFaults": "Finesse\nFaults",
|
||||||
|
"finessePercentage": "Finesse\nPercentage",
|
||||||
|
"keys": "Key\nPresses",
|
||||||
|
"kpp": "KP Per\nPiece",
|
||||||
|
"kps": "KP Per\nSecond"
|
||||||
|
},
|
||||||
|
"playerRole(map)": {
|
||||||
|
"user": "User",
|
||||||
|
"banned": "Banned",
|
||||||
|
"bot": "Bot",
|
||||||
|
"sysop": "System operator",
|
||||||
|
"admin": "Admin",
|
||||||
|
"mod": "Moderator",
|
||||||
|
"halfmod": "Community moderator"
|
||||||
|
},
|
||||||
|
"numOfGameActions":{
|
||||||
|
"pc": "All Clears",
|
||||||
|
"hold": "Holds",
|
||||||
|
"tspinsTotal": "T-spins total",
|
||||||
|
"lineClears": "Line clears"
|
||||||
|
},
|
||||||
|
"popupActions":{
|
||||||
|
"ok": "OK"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
{
|
||||||
|
"locales(map)": {
|
||||||
|
"en": "Английский (English)",
|
||||||
|
"ru": "Русский"
|
||||||
|
},
|
||||||
|
"tetraLeague": "Тетра Лига",
|
||||||
|
"tlRecords": "Матчи ТЛ",
|
||||||
|
"history": "История",
|
||||||
|
"sprint": "40 линий",
|
||||||
|
"blitz": "Блиц",
|
||||||
|
"other": "Другое",
|
||||||
|
"zen": "Дзен",
|
||||||
|
"bio": "Биография",
|
||||||
|
"refresh": "Обновить",
|
||||||
|
"showStoredData": "Показать сохранённые данные",
|
||||||
|
"statsCalc": "Калькулятор статистики",
|
||||||
|
"settings": "Настройки",
|
||||||
|
"track": "Отслеживать",
|
||||||
|
"stopTracking": "Перестать\nотслеживать",
|
||||||
|
"becameTracked": "Добавлен в список отслеживания!",
|
||||||
|
"stoppedBeingTracked": "Удалён из списка отслеживания!",
|
||||||
|
"compare": "Сравнить",
|
||||||
|
"tlLeaderboard": "Таблица лидеров Тетра Лиги",
|
||||||
|
"noRecords": "Нет записей",
|
||||||
|
"noRecord": "Нет рекорда",
|
||||||
|
"notEnoughData": "Недостаточно данных",
|
||||||
|
"noHistorySaved": "Нет сохранённой истории",
|
||||||
|
"obtainDate": "Получено ${date}",
|
||||||
|
"fetchDate": "На момент ${date}",
|
||||||
|
"exactGametime": "Время, проведённое в игре",
|
||||||
|
"bigRedBanned": "ЗАБАНЕН",
|
||||||
|
"bigRedBadStanding": "ПЛОХАЯ РЕПУТАЦИЯ",
|
||||||
|
"copiedToClipboard": "Скопировано в буфер обмена!",
|
||||||
|
"playerRoleAccount": ", аккаунт которого ",
|
||||||
|
"wasFromBeginning": "существовал с самого начала",
|
||||||
|
"created": "создан",
|
||||||
|
"botCreatedBy": "игроком",
|
||||||
|
"notSupporter": "Нет саппортерки",
|
||||||
|
"supporter": "Саппортерка ${tier} уровня",
|
||||||
|
"assignedManualy": "Этот значок был присвоен вручную администрацией TETR.IO",
|
||||||
|
"comparingWith": "Сравнивая с данными от ${date}",
|
||||||
|
"top": "Топ",
|
||||||
|
"topRank": "Топ Ранг",
|
||||||
|
"decaying": "Загнивает",
|
||||||
|
"gamesUntilRanked": "${left} матчей до получения рейтинга",
|
||||||
|
"nerdStats": "Для задротов",
|
||||||
|
"statCellNum": {
|
||||||
|
"xpLevel": "Уровень\nопыта",
|
||||||
|
"hoursPlayed": "Часов\nСыграно",
|
||||||
|
"onlineGames": "Онлайн\nИгр",
|
||||||
|
"gamesWon": "Онлайн\nПобед",
|
||||||
|
"friends": "Друзей",
|
||||||
|
"apm": "Атака в\nМинуту",
|
||||||
|
"vs": "Показатель\nVersus",
|
||||||
|
"lbp": "Положение\nв рейтинге",
|
||||||
|
"lbpc": "Положение\nв рейтинге страны",
|
||||||
|
"gamesPlayed": "Игр\nСыграно",
|
||||||
|
"gamesWonTL": "Побед",
|
||||||
|
"winrate": "Процент\nпобед",
|
||||||
|
"level": "Уровень",
|
||||||
|
"score": "Счёт",
|
||||||
|
"spp": "Очков\nна Фигуру",
|
||||||
|
"pieces": "Фигур\nУстановлено",
|
||||||
|
"pps": "Фигур в\nСекунду",
|
||||||
|
"finesseFaults": "Ошибок\nТехники",
|
||||||
|
"finessePercentage": "% Качества\nТехники",
|
||||||
|
"keys": "Нажатий\nКлавиш",
|
||||||
|
"kpp": "Нажатий\nна Фигуру",
|
||||||
|
"kps": "Нажатий\nв Секунду"
|
||||||
|
},
|
||||||
|
"playerRole(map)": {
|
||||||
|
"user": "Пользователь",
|
||||||
|
"banned": "Заблокированный пользователь",
|
||||||
|
"bot": "Бот",
|
||||||
|
"sysop": "Системный оператор",
|
||||||
|
"admin": "Администратор",
|
||||||
|
"mod": "Модератор",
|
||||||
|
"halfmod": "Модератор сообщества"
|
||||||
|
},
|
||||||
|
"numOfGameActions":{
|
||||||
|
"pc": "Все чисто",
|
||||||
|
"hold": "В запас",
|
||||||
|
"tspinsTotal": "T-spins всего",
|
||||||
|
"lineClears": "Линий очищено"
|
||||||
|
},
|
||||||
|
"popupActions":{
|
||||||
|
"ok": "OK"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue