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*
|
||||
- ~~Ability to compare player with APM-PPS-VS stats~~
|
||||
- ~~Ability to fetch Tetra League leaderboard~~
|
||||
- ~~Average stats for ranks~~ *dev build are here*
|
||||
- Ability to compare player with avgRank
|
||||
- ~~Average stats for ranks~~
|
||||
- ~~Ability to compare player with avgRank~~ *dev build are here*
|
||||
- UI Animations
|
||||
- i18n, EN and RU locales
|
||||
- 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 'package:flutter/material.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/settings_view.dart';
|
||||
import 'package:tetra_stats/views/tracked_players_view.dart';
|
||||
|
@ -11,11 +13,26 @@ void main() {
|
|||
sqfliteFfiInit();
|
||||
databaseFactory = databaseFactoryFfi;
|
||||
}
|
||||
runApp(MaterialApp(
|
||||
home: const MainView(),
|
||||
routes: {"/settings": (context) => const SettingsView(), "/states": (context) => const TrackedPlayersView(), "/calc": (context) => const CalcView()},
|
||||
theme: ThemeData(
|
||||
fontFamily: 'Eurostile Round',
|
||||
colorScheme: const ColorScheme.dark(primary: Colors.cyanAccent, secondary: Colors.white),
|
||||
scaffoldBackgroundColor: Colors.black)));
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
runApp(TranslationProvider(
|
||||
child: MyApp(),
|
||||
));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
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()},
|
||||
theme: ThemeData(
|
||||
fontFamily: 'Eurostile Round',
|
||||
colorScheme: const ColorScheme.dark(primary: Colors.cyanAccent, secondary: Colors.white),
|
||||
scaffoldBackgroundColor: Colors.black
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,18 @@ class CompareState extends State<CompareView> {
|
|||
|
||||
void fetchRedSide(String user) async {
|
||||
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);
|
||||
if (tearDownToNumbers.length == 3) {
|
||||
redSideMode = Mode.stats;
|
||||
|
@ -118,6 +130,18 @@ class CompareState extends State<CompareView> {
|
|||
|
||||
void fetchGreenSide(String user) async {
|
||||
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);
|
||||
if (tearDownToNumbers.length == 3) {
|
||||
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";
|
||||
break;
|
||||
case Mode.averages:
|
||||
titleGreenSide = "average";
|
||||
titleGreenSide = "Average ${theGreenSide[2].rank.toUpperCase()} rank";
|
||||
break;
|
||||
}
|
||||
switch (redSideMode){
|
||||
|
@ -222,7 +246,7 @@ class CompareState extends State<CompareView> {
|
|||
titleRedSide = "${theRedSide[2].apm} APM, ${theRedSide[2].pps} PPS, ${theRedSide[2].vs} VS";
|
||||
break;
|
||||
case Mode.averages:
|
||||
titleRedSide = "average";
|
||||
titleRedSide = "Average ${theRedSide[2].rank.toUpperCase()} rank";
|
||||
break;
|
||||
}
|
||||
return Scaffold(
|
||||
|
@ -376,8 +400,8 @@ class CompareState extends State<CompareView> {
|
|||
),
|
||||
if (theGreenSide[2].gamesPlayed > 9 &&
|
||||
theRedSide[2].gamesPlayed > 9 &&
|
||||
greenSideMode == Mode.player &&
|
||||
redSideMode == Mode.player)
|
||||
greenSideMode != Mode.stats &&
|
||||
redSideMode != Mode.stats)
|
||||
CompareThingy(
|
||||
label: "TR",
|
||||
greenSide: theGreenSide[2].rating,
|
||||
|
@ -385,24 +409,24 @@ class CompareState extends State<CompareView> {
|
|||
fractionDigits: 2,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
if (greenSideMode == Mode.player &&
|
||||
redSideMode == Mode.player)
|
||||
if (greenSideMode != Mode.stats &&
|
||||
redSideMode != Mode.stats)
|
||||
CompareThingy(
|
||||
label: "Games Played",
|
||||
greenSide: theGreenSide[2].gamesPlayed,
|
||||
redSide: theRedSide[2].gamesPlayed,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
if (greenSideMode == Mode.player &&
|
||||
redSideMode == Mode.player)
|
||||
if (greenSideMode != Mode.stats &&
|
||||
redSideMode != Mode.stats)
|
||||
CompareThingy(
|
||||
label: "Games Won",
|
||||
greenSide: theGreenSide[2].gamesWon,
|
||||
redSide: theRedSide[2].gamesWon,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
if (greenSideMode == Mode.player &&
|
||||
redSideMode == Mode.player)
|
||||
if (greenSideMode != Mode.stats &&
|
||||
redSideMode != Mode.stats)
|
||||
CompareThingy(
|
||||
label: "WR %",
|
||||
greenSide:
|
||||
|
@ -413,8 +437,8 @@ class CompareState extends State<CompareView> {
|
|||
),
|
||||
if (theGreenSide[2].gamesPlayed > 9 &&
|
||||
theRedSide[2].gamesPlayed > 9 &&
|
||||
greenSideMode == Mode.player &&
|
||||
redSideMode == Mode.player)
|
||||
greenSideMode != Mode.stats &&
|
||||
redSideMode != Mode.stats)
|
||||
CompareThingy(
|
||||
label: "Glicko",
|
||||
greenSide: theGreenSide[2].glicko!,
|
||||
|
@ -424,8 +448,8 @@ class CompareState extends State<CompareView> {
|
|||
),
|
||||
if (theGreenSide[2].gamesPlayed > 9 &&
|
||||
theRedSide[2].gamesPlayed > 9 &&
|
||||
greenSideMode == Mode.player &&
|
||||
redSideMode == Mode.player)
|
||||
greenSideMode != Mode.stats &&
|
||||
redSideMode != Mode.stats)
|
||||
CompareThingy(
|
||||
label: "RD",
|
||||
greenSide: theGreenSide[2].rd!,
|
||||
|
@ -575,8 +599,8 @@ class CompareState extends State<CompareView> {
|
|||
),
|
||||
if (theGreenSide[2].gamesPlayed > 9 &&
|
||||
theGreenSide[2].gamesPlayed > 9 &&
|
||||
greenSideMode == Mode.player &&
|
||||
redSideMode == Mode.player)
|
||||
greenSideMode != Mode.stats &&
|
||||
redSideMode != Mode.stats)
|
||||
CompareThingy(
|
||||
label: "Acc. of Est.",
|
||||
greenSide: theGreenSide[2].esttracc!,
|
||||
|
@ -781,7 +805,8 @@ class CompareState extends State<CompareView> {
|
|||
fontFamily: "Eurostile Round Extended",
|
||||
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(
|
||||
label: "By Glicko",
|
||||
greenSide: getWinrateByTR(
|
||||
|
@ -803,15 +828,15 @@ class CompareState extends State<CompareView> {
|
|||
label: "By Est. TR",
|
||||
greenSide: getWinrateByTR(
|
||||
theGreenSide[2].estTr!.estglicko,
|
||||
theGreenSide[2].rd!,
|
||||
theGreenSide[2].rd ?? noTrRd,
|
||||
theRedSide[2].estTr!.estglicko,
|
||||
theRedSide[2].rd!) *
|
||||
theRedSide[2].rd ?? noTrRd) *
|
||||
100,
|
||||
redSide: getWinrateByTR(
|
||||
theRedSide[2].estTr!.estglicko,
|
||||
theRedSide[2].rd!,
|
||||
theRedSide[2].rd ?? noTrRd,
|
||||
theGreenSide[2].estTr!.estglicko,
|
||||
theGreenSide[2].rd!) *
|
||||
theGreenSide[2].rd ?? noTrRd) *
|
||||
100,
|
||||
fractionDigits: 2,
|
||||
higherIsBetter: true,
|
||||
|
@ -834,7 +859,7 @@ class PlayerSelector extends StatelessWidget {
|
|||
final Function fetch;
|
||||
final Function change;
|
||||
final Function updateState;
|
||||
const PlayerSelector(
|
||||
PlayerSelector(
|
||||
{super.key,
|
||||
required this.data,
|
||||
required this.mode,
|
||||
|
@ -845,16 +870,30 @@ class PlayerSelector extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final TextEditingController playerController = TextEditingController();
|
||||
String underFieldString = "";
|
||||
if (!listEquals(data, [null, null, null])){
|
||||
switch (mode){
|
||||
case Mode.player:
|
||||
playerController.text = data[0] != null ? data[0].username : "???";
|
||||
playerController.text = data[0] != null ? data[0].username : "";
|
||||
break;
|
||||
case Mode.stats:
|
||||
playerController.text = "${data[2].apm} ${data[2].pps} ${data[2].vs}";
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -867,11 +906,20 @@ class PlayerSelector extends StatelessWidget {
|
|||
controller: playerController,
|
||||
decoration: const InputDecoration(counter: Offstage()),
|
||||
onSubmitted: (String value) {
|
||||
underFieldString = "Fetching...";
|
||||
fetch(value);
|
||||
}),
|
||||
if (data[0] != null && data[1] == null)
|
||||
Text(
|
||||
data[0].toString(),
|
||||
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!),
|
||||
),
|
||||
)
|
||||
else Text(
|
||||
underFieldString,
|
||||
style: const TextStyle(
|
||||
shadows: <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:flutter/services.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/crud_exceptions.dart';
|
||||
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 f2 = NumberFormat.decimalPatternDigits(decimalDigits: 2);
|
||||
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 {
|
||||
final String? player;
|
||||
|
@ -46,14 +47,6 @@ Future<void> copyToClipboard(String text) async {
|
|||
|
||||
class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||
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;
|
||||
late TabController _tabController;
|
||||
late ScrollController _scrollController;
|
||||
|
@ -182,6 +175,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final t = Translations.of(context);
|
||||
return Scaffold(
|
||||
drawer: widget.player == null ? NavDrawer(changePlayer) : null,
|
||||
appBar: AppBar(
|
||||
|
@ -227,21 +221,21 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
),
|
||||
PopupMenuButton(
|
||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
||||
const PopupMenuItem(
|
||||
PopupMenuItem(
|
||||
value: "refresh",
|
||||
child: Text('Refresh'),
|
||||
child: Text(t.refresh),
|
||||
),
|
||||
const PopupMenuItem(
|
||||
PopupMenuItem(
|
||||
value: "/states",
|
||||
child: Text('Show stored data'),
|
||||
child: Text(t.showStoredData),
|
||||
),
|
||||
const PopupMenuItem(
|
||||
PopupMenuItem(
|
||||
value: "/calc",
|
||||
child: Text('Stats Calculator'),
|
||||
child: Text(t.statsCalc),
|
||||
),
|
||||
const PopupMenuItem(
|
||||
PopupMenuItem(
|
||||
value: "/settings",
|
||||
child: Text('Settings'),
|
||||
child: Text(t.settings),
|
||||
),
|
||||
],
|
||||
onSelected: (value) {
|
||||
|
@ -258,22 +252,9 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
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:
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(color: Colors.white));
|
||||
case ConnectionState.active:
|
||||
return const Center(
|
||||
child: Text('active case of FutureBuilder',
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: 42),
|
||||
textAlign: TextAlign.center));
|
||||
return const Center(child: CircularProgressIndicator(color: Colors.white));
|
||||
case ConnectionState.done:
|
||||
//bool bigScreen = MediaQuery.of(context).size.width > 1024;
|
||||
if (snapshot.hasData) {
|
||||
|
@ -303,7 +284,14 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
child: TabBar(
|
||||
controller: _tabController,
|
||||
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(
|
||||
tl: snapshot.data![0].tlSeason1,
|
||||
userID: snapshot.data![0].userId,
|
||||
oldTl: snapshot.data![4],),
|
||||
oldTl: snapshot.data![4]),
|
||||
_TLRecords(userID: snapshot.data![0].userId, data: snapshot.data![3]),
|
||||
_History(states: snapshot.data![2], update: _justUpdate),
|
||||
_RecordThingy(
|
||||
|
@ -406,7 +394,6 @@ class _NavDrawerState extends State<NavDrawer> {
|
|||
builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.none:
|
||||
return const Center(child: Text('none case of StreamBuilder'));
|
||||
case ConnectionState.waiting:
|
||||
case ConnectionState.active:
|
||||
final allPlayers = (snapshot.data != null)
|
||||
|
@ -436,7 +423,7 @@ class _NavDrawerState extends State<NavDrawer> {
|
|||
SliverToBoxAdapter(
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.leaderboard),
|
||||
title: const Text("Tetra League leaderboard"),
|
||||
title: Text(t.tlLeaderboard),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
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(),)
|
||||
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)
|
||||
? [
|
||||
if (record!.stream.contains("40l"))
|
||||
Text("40 Lines",
|
||||
Text(t.sprint,
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: bigScreen ? 42 : 28))
|
||||
else if (record!.stream.contains("blitz"))
|
||||
Text("Blitz",
|
||||
Text(t.blitz,
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: bigScreen ? 42 : 28)),
|
||||
|
@ -629,7 +616,7 @@ class _RecordThingy extends StatelessWidget {
|
|||
playerStatLabel: "Leaderboard Placement",
|
||||
isScreenBig: bigScreen,
|
||||
higherIsBetter: false),
|
||||
Text("Obtained ${dateFormat.format(record!.timestamp!)}",
|
||||
Text(t.obtainDate(date: dateFormat.format(record!.timestamp!)),
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontFamily: "Eurostile Round",
|
||||
|
@ -647,53 +634,53 @@ class _RecordThingy extends StatelessWidget {
|
|||
if (record!.stream.contains("blitz"))
|
||||
StatCellNum(
|
||||
playerStat: record!.endContext!.level,
|
||||
playerStatLabel: "Level",
|
||||
playerStatLabel: t.statCellNum.level,
|
||||
isScreenBig: bigScreen,
|
||||
higherIsBetter: true,),
|
||||
if (record!.stream.contains("blitz"))
|
||||
StatCellNum(
|
||||
playerStat: record!.endContext!.spp,
|
||||
playerStatLabel: "Score\nPer Piece",
|
||||
playerStatLabel: t.statCellNum.spp,
|
||||
fractionDigits: 2,
|
||||
isScreenBig: bigScreen,
|
||||
higherIsBetter: true,),
|
||||
StatCellNum(
|
||||
playerStat: record!.endContext!.piecesPlaced,
|
||||
playerStatLabel: "Pieces\nPlaced",
|
||||
playerStatLabel: t.statCellNum.pieces,
|
||||
isScreenBig: bigScreen,
|
||||
higherIsBetter: true,),
|
||||
StatCellNum(
|
||||
playerStat: record!.endContext!.pps,
|
||||
playerStatLabel: "Pieces\nPer Second",
|
||||
playerStatLabel: t.statCellNum.pps,
|
||||
fractionDigits: 2,
|
||||
isScreenBig: bigScreen,
|
||||
higherIsBetter: true,),
|
||||
StatCellNum(
|
||||
playerStat: record!.endContext!.finesse.faults,
|
||||
playerStatLabel: "Finesse\nFaults",
|
||||
playerStatLabel: t.statCellNum.finesseFaults,
|
||||
isScreenBig: bigScreen,
|
||||
higherIsBetter: false,),
|
||||
StatCellNum(
|
||||
playerStat:
|
||||
record!.endContext!.finessePercentage * 100,
|
||||
playerStatLabel: "Finesse\nPercentage",
|
||||
playerStatLabel: t.statCellNum.finessePercentage,
|
||||
fractionDigits: 2,
|
||||
isScreenBig: bigScreen,
|
||||
higherIsBetter: true,),
|
||||
StatCellNum(
|
||||
playerStat: record!.endContext!.inputs,
|
||||
playerStatLabel: "Key\nPresses",
|
||||
playerStatLabel: t.statCellNum.keys,
|
||||
isScreenBig: bigScreen,
|
||||
higherIsBetter: false,),
|
||||
StatCellNum(
|
||||
playerStat: record!.endContext!.kpp,
|
||||
playerStatLabel: "KP Per\nPiece",
|
||||
playerStatLabel: t.statCellNum.kpp,
|
||||
fractionDigits: 2,
|
||||
isScreenBig: bigScreen,
|
||||
higherIsBetter: false,),
|
||||
StatCellNum(
|
||||
playerStat: record!.endContext!.kps,
|
||||
playerStatLabel: "KP Per\nSecond",
|
||||
playerStatLabel: t.statCellNum.kps,
|
||||
fractionDigits: 2,
|
||||
isScreenBig: bigScreen,
|
||||
higherIsBetter: true,),
|
||||
|
@ -713,8 +700,8 @@ class _RecordThingy extends StatelessWidget {
|
|||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text("All Clears:",
|
||||
style: TextStyle(fontSize: 24)),
|
||||
Text("${t.numOfGameActions.pc}:",
|
||||
style: const TextStyle(fontSize: 24)),
|
||||
Text(
|
||||
record!.endContext!.clears.allClears
|
||||
.toString(),
|
||||
|
@ -726,8 +713,8 @@ class _RecordThingy extends StatelessWidget {
|
|||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text("Holds:",
|
||||
style: TextStyle(fontSize: 24)),
|
||||
Text("${t.numOfGameActions.hold}:",
|
||||
style: const TextStyle(fontSize: 24)),
|
||||
Text(
|
||||
record!.endContext!.holds.toString(),
|
||||
style: const TextStyle(fontSize: 24),
|
||||
|
@ -738,8 +725,8 @@ class _RecordThingy extends StatelessWidget {
|
|||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text("T-spins total:",
|
||||
style: TextStyle(fontSize: 24)),
|
||||
Text("${t.numOfGameActions.tspinsTotal}:",
|
||||
style: const TextStyle(fontSize: 24)),
|
||||
Text(
|
||||
record!.endContext!.tSpins.toString(),
|
||||
style: const TextStyle(fontSize: 24),
|
||||
|
@ -841,8 +828,8 @@ class _RecordThingy extends StatelessWidget {
|
|||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text("Line clears:",
|
||||
style: TextStyle(fontSize: 24)),
|
||||
Text("${t.numOfGameActions.lineClears}:",
|
||||
style: const TextStyle(fontSize: 24)),
|
||||
Text(
|
||||
record!.endContext!.lines.toString(),
|
||||
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) {
|
||||
return Column(
|
||||
children: [
|
||||
Text("Other info",
|
||||
Text(t.other,
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: bigScreen ? 42 : 28)),
|
||||
|
@ -939,17 +926,17 @@ class _OtherThingy extends StatelessWidget {
|
|||
padding: const EdgeInsets.fromLTRB(0, 48, 0, 48),
|
||||
child: Column(
|
||||
children: [
|
||||
Text("Zen",
|
||||
Text(t.zen,
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: bigScreen ? 42 : 28)),
|
||||
Text(
|
||||
"Level ${NumberFormat.decimalPattern().format(zen!.level)}",
|
||||
"${t.statCellNum.level} ${NumberFormat.decimalPattern().format(zen!.level)}",
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: bigScreen ? 42 : 28)),
|
||||
Text(
|
||||
"Score ${NumberFormat.decimalPattern().format(zen!.score)}",
|
||||
"${t.statCellNum.score} ${NumberFormat.decimalPattern().format(zen!.score)}",
|
||||
style: const TextStyle(fontSize: 18)),
|
||||
],
|
||||
),
|
||||
|
@ -959,7 +946,7 @@ class _OtherThingy extends StatelessWidget {
|
|||
padding: const EdgeInsets.fromLTRB(0, 0, 0, 48),
|
||||
child: Column(
|
||||
children: [
|
||||
Text("Bio",
|
||||
Text(t.bio,
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: bigScreen ? 42 : 28)),
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path_provider/path_provider.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/tetrio_crud.dart';
|
||||
|
||||
|
@ -65,6 +66,12 @@ class SettingsState extends State<SettingsView> {
|
|||
|
||||
@override
|
||||
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(
|
||||
appBar: AppBar(
|
||||
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(),
|
||||
ListTile(
|
||||
title: const Text("About app"),
|
||||
|
|
|
@ -3,6 +3,8 @@ import 'package:intl/intl.dart';
|
|||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:fl_chart/fl_chart.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';
|
||||
|
||||
var fDiff = NumberFormat("+#,###.###;-#,###.###");
|
||||
|
@ -17,6 +19,8 @@ class TLThingy extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final t = Translations.of(context);
|
||||
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
bool bigScreen = constraints.maxWidth > 768;
|
||||
return ListView.builder(
|
||||
|
@ -26,8 +30,8 @@ class TLThingy extends StatelessWidget {
|
|||
return Column(
|
||||
children: (tl.gamesPlayed > 0)
|
||||
? [
|
||||
Text("Tetra League", 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)}"),
|
||||
Text(t.tetraLeague, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
||||
if (oldTl != null) Text(t.comparingWith(date: dateFormat.format(oldTl!.timestamp))),
|
||||
if (tl.gamesPlayed >= 10)
|
||||
Wrap(
|
||||
direction: Axis.horizontal,
|
||||
|
@ -51,7 +55,7 @@ class TLThingy extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
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,
|
||||
),
|
||||
],
|
||||
|
@ -73,7 +77,7 @@ class TLThingy extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
if (tl.gamesPlayed < 10)
|
||||
Text("${10 - tl.gamesPlayed} games until being ranked",
|
||||
Text(t.gamesUntilRanked(left: 10 - tl.gamesPlayed),
|
||||
softWrap: true,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
|
@ -90,21 +94,21 @@ class TLThingy extends StatelessWidget {
|
|||
crossAxisAlignment: WrapCrossAlignment.start,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
children: [
|
||||
if (tl.apm != null) StatCellNum(playerStat: tl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Attack\nPer Minute", 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.vs != null) StatCellNum(playerStat: tl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Versus\nScore", 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.standingLocal > 0) StatCellNum(playerStat: tl.standingLocal, isScreenBig: bigScreen, playerStatLabel: "Country LB\nplacement", higherIsBetter: false, oldPlayerStat: oldTl?.standingLocal),
|
||||
StatCellNum(playerStat: tl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Games\nplayed", higherIsBetter: true, oldPlayerStat: oldTl?.gamesPlayed),
|
||||
StatCellNum(playerStat: tl.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nwon", 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),
|
||||
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: t.statCellNum.pps, higherIsBetter: true, oldPlayerStat: oldTl?.pps),
|
||||
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: t.statCellNum.lbp, higherIsBetter: false, oldPlayerStat: oldTl?.standing),
|
||||
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: t.statCellNum.gamesPlayed, higherIsBetter: true, oldPlayerStat: oldTl?.gamesPlayed),
|
||||
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: t.statCellNum.winrate, higherIsBetter: true, oldPlayerStat: oldTl != null ? oldTl!.winrate*100 : null),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (tl.nerdStats != null)
|
||||
Column(
|
||||
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: const EdgeInsets.fromLTRB(0, 40, 0, 0),
|
||||
child: Wrap(
|
||||
|
|
|
@ -1,23 +1,16 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.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:intl/intl.dart';
|
||||
import 'dart:developer' as developer;
|
||||
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 {
|
||||
await Clipboard.setData(ClipboardData(text: text));
|
||||
}
|
||||
|
||||
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
|
||||
|
||||
class UserThingy extends StatelessWidget {
|
||||
final TetrioPlayer player;
|
||||
final bool showStateTimestamp;
|
||||
|
@ -26,6 +19,8 @@ class UserThingy extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final t = Translations.of(context);
|
||||
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
bool bigScreen = constraints.maxWidth > 768;
|
||||
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)),
|
||||
onPressed: () {
|
||||
copyToClipboard(player.userId);
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Copied to clipboard!")));
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.copiedToClipboard)));
|
||||
}),
|
||||
],
|
||||
)),
|
||||
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: [
|
||||
FutureBuilder(
|
||||
future: teto.isPlayerTracking(player.userId),
|
||||
|
@ -121,10 +116,10 @@ class UserThingy extends StatelessWidget {
|
|||
icon: const Icon(Icons.person_remove),
|
||||
onPressed: () {
|
||||
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 {
|
||||
|
@ -135,10 +130,10 @@ class UserThingy extends StatelessWidget {
|
|||
onPressed: () {
|
||||
teto.addPlayerToTrack(player).then((value) => setState());
|
||||
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: [
|
||||
StatCellNum(
|
||||
playerStat: player.level,
|
||||
playerStatLabel: "XP Level",
|
||||
playerStatLabel: t.statCellNum.xpLevel,
|
||||
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)} %")],
|
||||
higherIsBetter: true,
|
||||
|
@ -181,32 +176,32 @@ class UserThingy extends StatelessWidget {
|
|||
if (player.gameTime >= Duration.zero)
|
||||
StatCellNum(
|
||||
playerStat: player.gameTime.inHours,
|
||||
playerStatLabel: "Hours\nPlayed",
|
||||
playerStatLabel: t.statCellNum.hoursPlayed,
|
||||
isScreenBig: bigScreen,
|
||||
alertWidgets: [Text("Exact gametime: ${player.gameTime.toString()}")],
|
||||
alertWidgets: [Text("${t.exactGametime}: ${player.gameTime.toString()}")],
|
||||
higherIsBetter: true,),
|
||||
if (player.gamesPlayed >= 0)
|
||||
StatCellNum(
|
||||
playerStat: player.gamesPlayed,
|
||||
isScreenBig: bigScreen,
|
||||
playerStatLabel: "Online\nGames",
|
||||
playerStatLabel: t.statCellNum.onlineGames,
|
||||
higherIsBetter: true,),
|
||||
if (player.gamesWon >= 0)
|
||||
StatCellNum(
|
||||
playerStat: player.gamesWon,
|
||||
isScreenBig: bigScreen,
|
||||
playerStatLabel: "Games\nWon",
|
||||
playerStatLabel: t.statCellNum.gamesWon,
|
||||
higherIsBetter: true,),
|
||||
if (player.friendCount > 0)
|
||||
StatCellNum(
|
||||
playerStat: player.friendCount,
|
||||
isScreenBig: bigScreen,
|
||||
playerStatLabel: "Friends",
|
||||
playerStatLabel: t.statCellNum.friends,
|
||||
higherIsBetter: true,),
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
"BANNED",
|
||||
t.bigRedBanned,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
|
@ -217,7 +212,7 @@ class UserThingy extends StatelessWidget {
|
|||
),
|
||||
if (player.badstanding != null && player.badstanding!)
|
||||
Text(
|
||||
"BAD STANDING",
|
||||
t.bigRedBadStanding,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
|
@ -231,7 +226,7 @@ class UserThingy extends StatelessWidget {
|
|||
children: [
|
||||
Expanded(
|
||||
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,
|
||||
style: const TextStyle(
|
||||
fontFamily: "Eurostile Round",
|
||||
|
@ -268,8 +263,8 @@ class UserThingy extends StatelessWidget {
|
|||
children: [
|
||||
Image.asset("res/tetrio_badges/${badge.badgeId}.png"),
|
||||
Text(badge.ts != null
|
||||
? "Obtained ${dateFormat.format(badge.ts!)}"
|
||||
: "That badge was assigned manualy by TETR.IO admins"),
|
||||
? t.obtainDate(date: dateFormat.format(badge.ts!))
|
||||
: t.assignedManualy),
|
||||
],
|
||||
)
|
||||
],
|
||||
|
@ -277,7 +272,7 @@ class UserThingy extends StatelessWidget {
|
|||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text('OK'),
|
||||
child: Text(t.popupActions.ok),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
|
|
49
pubspec.lock
49
pubspec.lock
|
@ -97,6 +97,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
csv:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csv
|
||||
sha256: "016b31a51a913744a0a1655c74ff13c9379e1200e246a03d96c81c5d9ed297b5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.2"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -230,6 +238,11 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
flutter_localizations:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -276,10 +289,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||
sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.18.1"
|
||||
version: "0.18.0"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -288,6 +301,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
json2yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json2yaml
|
||||
sha256: da94630fbc56079426fdd167ae58373286f603371075b69bf46d848d63ba3e51
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -501,6 +522,22 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -629,6 +666,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
27
pubspec.yaml
27
pubspec.yaml
|
@ -2,18 +2,6 @@ name: tetra_stats
|
|||
description: Track your and other player stats in TETR.IO
|
||||
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
|
||||
|
||||
environment:
|
||||
|
@ -29,6 +17,8 @@ dependencies:
|
|||
http:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
cupertino_icons: ^1.0.2
|
||||
vector_math: any
|
||||
sqflite: ^2.2.8+2
|
||||
|
@ -39,10 +29,12 @@ dependencies:
|
|||
fl_chart: ^0.62.0
|
||||
package_info_plus: ^4.0.2
|
||||
shared_preferences: ^2.1.1
|
||||
intl: ^0.18.1
|
||||
intl: ^0.18.0
|
||||
syncfusion_flutter_gauges: ^22.1.34
|
||||
file_selector: ^0.9.4
|
||||
file_picker: ^5.3.2
|
||||
slang: ^3.20.0
|
||||
slang_flutter: ^3.20.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -69,6 +61,15 @@ flutter_launcher_icons:
|
|||
generate: true
|
||||
image_path: "res/icons/app.png"
|
||||
|
||||
targets:
|
||||
$default:
|
||||
builders:
|
||||
slang_build_runner:
|
||||
options:
|
||||
input_directory: res/i18n
|
||||
output_directory: lib/i18n
|
||||
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
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