Compare commits

..

No commits in common. "c03ea447824f727757024dd9911229527f079689" and "3d28e5a21429840916131e015fd0d01be34a4b4e" have entirely different histories.

10 changed files with 148 additions and 129 deletions

View File

@ -45,8 +45,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: ashutoshvarma/setup-ninja@master
- uses: subosito/flutter-action@v1 - uses: subosito/flutter-action@v1
- uses: ashutoshvarma/setup-ninja@master
with: with:
channel: 'stable' channel: 'stable'
flutter-version: '3.22.3' flutter-version: '3.22.3'

View File

@ -4,9 +4,9 @@
/// To regenerate, run: `dart run slang` /// To regenerate, run: `dart run slang`
/// ///
/// Locales: 2 /// Locales: 2
/// Strings: 1210 (605 per locale) /// Strings: 1216 (608 per locale)
/// ///
/// Built on 2024-09-04 at 20:41 UTC /// Built on 2024-08-07 at 15:58 UTC
// coverage:ignore-file // coverage:ignore-file
// ignore_for_file: type=lint // ignore_for_file: type=lint
@ -224,6 +224,9 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
String get smooth => 'Smooth'; String get smooth => 'Smooth';
String get postSeason => 'Off-season'; String get postSeason => 'Off-season';
String get seasonStarts => 'Season starts in:'; String get seasonStarts => 'Season starts in:';
String get myMessadgeHeader => 'A messadge from dan63';
String get myMessadgeBody => 'TETR.IO Tetra Channel API has been seriously modified after the last update, therefore, some functions may not work. I will try to catch up and add new stats (and return back the old ones) as soon, as public docs on new Tetra Channel API will be available.';
String preSeasonMessage({required Object n}) => 'Right now you can play unranked FT3 matches with hidden glicko (200 RD 🙂).\nSeason ${n} rules applied';
String get nanow => 'Not avaliable for now...'; String get nanow => 'Not avaliable for now...';
String seasonEnds({required Object countdown}) => 'Season ends in ${countdown}'; String seasonEnds({required Object countdown}) => 'Season ends in ${countdown}';
String get seasonEnded => 'Season has ended'; String get seasonEnded => 'Season has ended';
@ -290,7 +293,7 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
String stateViewTitle({required Object nickname, required Object date}) => '${nickname} account on ${date}'; String stateViewTitle({required Object nickname, required Object date}) => '${nickname} account on ${date}';
String statesViewTitle({required Object number, required Object nickname}) => '${number} states of ${nickname} account'; String statesViewTitle({required Object number, required Object nickname}) => '${number} states of ${nickname} account';
String matchesViewTitle({required Object nickname}) => '${nickname} TL matches'; String matchesViewTitle({required Object nickname}) => '${nickname} TL matches';
String statesViewEntry({required Object level, required Object glicko, required Object rd, required Object games}) => '${level} TR, ${glicko}±${rd} Glicko, ${games} игр сыграно'; String statesViewEntry({required Object level, required Object gameTime, required Object friends, required Object rd}) => 'Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD';
String stateRemoved({required Object date}) => '${date} state was removed from database!'; String stateRemoved({required Object date}) => '${date} state was removed from database!';
String matchRemoved({required Object date}) => '${date} match was removed from database!'; String matchRemoved({required Object date}) => '${date} match was removed from database!';
String get viewAllMatches => 'View all matches'; String get viewAllMatches => 'View all matches';
@ -935,6 +938,9 @@ class _StringsRu implements Translations {
@override String get smooth => 'Гладкий'; @override String get smooth => 'Гладкий';
@override String get postSeason => 'Внесезонье'; @override String get postSeason => 'Внесезонье';
@override String get seasonStarts => 'Сезон начнётся через:'; @override String get seasonStarts => 'Сезон начнётся через:';
@override String get myMessadgeHeader => 'Сообщение от dan63';
@override String get myMessadgeBody => 'TETR.IO Tetra Channel API был серьёзно изменён после последнего обновления, поэтому некоторый функционал может не работать. Я постараюсь добавить новую статистику (и вернуть старую) как только будут опубликована новая документация по данному API.';
@override String preSeasonMessage({required Object n}) => 'Прямо сейчас вы можете сыграть безранговый матч до трёх побед со скрытым Glicko (200 RD 🙂).\nПрименяются правила ${n} сезона';
@override String get nanow => 'Пока недоступно...'; @override String get nanow => 'Пока недоступно...';
@override String seasonEnds({required Object countdown}) => 'Сезон закончится через ${countdown}'; @override String seasonEnds({required Object countdown}) => 'Сезон закончится через ${countdown}';
@override String get seasonEnded => 'Сезон закончился'; @override String get seasonEnded => 'Сезон закончился';
@ -1001,7 +1007,7 @@ class _StringsRu implements Translations {
@override String stateViewTitle({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}'; @override String stateViewTitle({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}';
@override String statesViewTitle({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}'; @override String statesViewTitle({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}';
@override String matchesViewTitle({required Object nickname}) => 'Матчи аккаунта ${nickname}'; @override String matchesViewTitle({required Object nickname}) => 'Матчи аккаунта ${nickname}';
@override String statesViewEntry({required Object level, required Object glicko, required Object rd, required Object games}) => '${level} TR, ${glicko}±${rd} Glicko, ${games} игр сыграно'; @override String statesViewEntry({required Object level, required Object gameTime, required Object friends, required Object rd}) => '${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD';
@override String stateRemoved({required Object date}) => 'Состояние от ${date} было удалено из локальной базы данных!'; @override String stateRemoved({required Object date}) => 'Состояние от ${date} было удалено из локальной базы данных!';
@override String matchRemoved({required Object date}) => 'Матч от ${date} был удален из локальной базы данных!'; @override String matchRemoved({required Object date}) => 'Матч от ${date} был удален из локальной базы данных!';
@override String get viewAllMatches => 'Все матчи'; @override String get viewAllMatches => 'Все матчи';
@ -1638,6 +1644,9 @@ extension on Translations {
case 'smooth': return 'Smooth'; case 'smooth': return 'Smooth';
case 'postSeason': return 'Off-season'; case 'postSeason': return 'Off-season';
case 'seasonStarts': return 'Season starts in:'; case 'seasonStarts': return 'Season starts in:';
case 'myMessadgeHeader': return 'A messadge from dan63';
case 'myMessadgeBody': return 'TETR.IO Tetra Channel API has been seriously modified after the last update, therefore, some functions may not work. I will try to catch up and add new stats (and return back the old ones) as soon, as public docs on new Tetra Channel API will be available.';
case 'preSeasonMessage': return ({required Object n}) => 'Right now you can play unranked FT3 matches with hidden glicko (200 RD 🙂).\nSeason ${n} rules applied';
case 'nanow': return 'Not avaliable for now...'; case 'nanow': return 'Not avaliable for now...';
case 'seasonEnds': return ({required Object countdown}) => 'Season ends in ${countdown}'; case 'seasonEnds': return ({required Object countdown}) => 'Season ends in ${countdown}';
case 'seasonEnded': return 'Season has ended'; case 'seasonEnded': return 'Season has ended';
@ -1704,7 +1713,7 @@ extension on Translations {
case 'stateViewTitle': return ({required Object nickname, required Object date}) => '${nickname} account on ${date}'; case 'stateViewTitle': return ({required Object nickname, required Object date}) => '${nickname} account on ${date}';
case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} states of ${nickname} account'; case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} states of ${nickname} account';
case 'matchesViewTitle': return ({required Object nickname}) => '${nickname} TL matches'; case 'matchesViewTitle': return ({required Object nickname}) => '${nickname} TL matches';
case 'statesViewEntry': return ({required Object level, required Object glicko, required Object rd, required Object games}) => '${level} TR, ${glicko}±${rd} Glicko, ${games} игр сыграно'; case 'statesViewEntry': return ({required Object level, required Object gameTime, required Object friends, required Object rd}) => 'Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD';
case 'stateRemoved': return ({required Object date}) => '${date} state was removed from database!'; case 'stateRemoved': return ({required Object date}) => '${date} state was removed from database!';
case 'matchRemoved': return ({required Object date}) => '${date} match was removed from database!'; case 'matchRemoved': return ({required Object date}) => '${date} match was removed from database!';
case 'viewAllMatches': return 'View all matches'; case 'viewAllMatches': return 'View all matches';
@ -2265,6 +2274,9 @@ extension on _StringsRu {
case 'smooth': return 'Гладкий'; case 'smooth': return 'Гладкий';
case 'postSeason': return 'Внесезонье'; case 'postSeason': return 'Внесезонье';
case 'seasonStarts': return 'Сезон начнётся через:'; case 'seasonStarts': return 'Сезон начнётся через:';
case 'myMessadgeHeader': return 'Сообщение от dan63';
case 'myMessadgeBody': return 'TETR.IO Tetra Channel API был серьёзно изменён после последнего обновления, поэтому некоторый функционал может не работать. Я постараюсь добавить новую статистику (и вернуть старую) как только будут опубликована новая документация по данному API.';
case 'preSeasonMessage': return ({required Object n}) => 'Прямо сейчас вы можете сыграть безранговый матч до трёх побед со скрытым Glicko (200 RD 🙂).\nПрименяются правила ${n} сезона';
case 'nanow': return 'Пока недоступно...'; case 'nanow': return 'Пока недоступно...';
case 'seasonEnds': return ({required Object countdown}) => 'Сезон закончится через ${countdown}'; case 'seasonEnds': return ({required Object countdown}) => 'Сезон закончится через ${countdown}';
case 'seasonEnded': return 'Сезон закончился'; case 'seasonEnded': return 'Сезон закончился';
@ -2331,7 +2343,7 @@ extension on _StringsRu {
case 'stateViewTitle': return ({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}'; case 'stateViewTitle': return ({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}';
case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}'; case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}';
case 'matchesViewTitle': return ({required Object nickname}) => 'Матчи аккаунта ${nickname}'; case 'matchesViewTitle': return ({required Object nickname}) => 'Матчи аккаунта ${nickname}';
case 'statesViewEntry': return ({required Object level, required Object glicko, required Object rd, required Object games}) => '${level} TR, ${glicko}±${rd} Glicko, ${games} игр сыграно'; case 'statesViewEntry': return ({required Object level, required Object gameTime, required Object friends, required Object rd}) => '${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD';
case 'stateRemoved': return ({required Object date}) => 'Состояние от ${date} было удалено из локальной базы данных!'; case 'stateRemoved': return ({required Object date}) => 'Состояние от ${date} было удалено из локальной базы данных!';
case 'matchRemoved': return ({required Object date}) => 'Матч от ${date} был удален из локальной базы данных!'; case 'matchRemoved': return ({required Object date}) => 'Матч от ${date} был удален из локальной базы данных!';
case 'viewAllMatches': return 'Все матчи'; case 'viewAllMatches': return 'Все матчи';

View File

@ -213,7 +213,6 @@ class TetrioService extends DB {
_players.removeWhere((key, value) => key == id); _players.removeWhere((key, value) => key == id);
_tetrioStreamController.add(_players); _tetrioStreamController.add(_players);
} }
await db.delete(tetrioLeagueTable, where: "id LIKE ?", whereArgs: ["$id%"]);
} }
/// Gets nickname from database or requests it from API if missing. /// Gets nickname from database or requests it from API if missing.
@ -1118,14 +1117,46 @@ class TetrioService extends DB {
} }
} }
/// Remove state, which has [dbID] from the local database /// Remove state (which is [tetrioPlayer]) from the local database
/// ([dbid] is a concatenation of player id and UINX milliseconds in hex) // Future<void> deleteState(TetrioPlayer tetrioPlayer) async {
Future<void> deleteState(String dbID) async { // await ensureDbIsOpen();
await ensureDbIsOpen(); // final db = getDatabaseOrThrow();
final db = getDatabaseOrThrow(); // //List<TetrioPlayer> states = await getPlayer(tetrioPlayer.userId);
int result = await db.delete(tetrioLeagueTable, where: "id = ?", whereArgs: [dbID]); // // removing state from map that contain every state of each user
if (result == 0) throw Exception("Failed to remove a row $dbID - it's probably not exist"); // states.removeWhere((element) => element.state == tetrioPlayer.state);
}
// // Making map of the states (without deleted one)
// final Map<String, dynamic> statesJson = {};
// // for (var e in states) {
// // statesJson.addEntries({(e.state.millisecondsSinceEpoch ~/ 1000).toString(): e.toJson()}.entries);
// // }
// // Rewriting database entry with new json
// await db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)},
// where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]);
// _tetrioStreamController.add(_players);
// }
/// Returns list of all states of player with given [id] from database. Can return empty list if player
/// was not found.
// Future<List<TetrioPlayer>> getPlayer(String id) async {
// await ensureDbIsOpen();
// final db = getDatabaseOrThrow();
// List<TetrioPlayer> states = [];
// final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
// if (results.isEmpty) {
// return states; // it empty
// } else {
// dynamic rawStates = results.first['jsonStates'] as String;
// rawStates = json.decode(rawStates);
// // recreating objects of states
// rawStates.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k) * 1000), id, results.first[nickCol] as String)));
// // updating the stream
// _players.removeWhere((key, value) => key == id);
// _players.addEntries({states.last.userId: states.last.username}.entries);
// _tetrioStreamController.add(_players);
// return states;
// }
// }
/// Retrieves general stats of [user] (nickname or id) from Tetra Channel api. Returns [TetrioPlayer] object of this user. /// Retrieves general stats of [user] (nickname or id) from Tetra Channel api. Returns [TetrioPlayer] object of this user.
/// If [isItDiscordID] is true, function expects [user] to be a discord user id. Throws an exception if fails to retrieve. /// If [isItDiscordID] is true, function expects [user] to be a discord user id. Throws an exception if fails to retrieve.
@ -1225,14 +1256,17 @@ class TetrioService extends DB {
} }
} }
/// Retrieves whole [tetrioUsersTable] and returns Map {id: nickname} of everyone in database /// Retrieves whole [tetrioUsersTable] and returns Map with [TetrioPlayer] objects of everyone in database
Future<Map<String, String>> getAllPlayers() async { Future<Map<String, List<TetrioPlayer>>> getAllPlayers() async {
await ensureDbIsOpen(); await ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
final players = await db.query(tetrioUsersTable); final players = await db.query(tetrioUsersTable);
Map<String, String> data = {}; Map<String, List<TetrioPlayer>> data = {};
for (var entry in players){ for (var entry in players){
data[entry[idCol] as String] = entry[nickCol] as String; var test = json.decode(entry['jsonStates'] as String);
List<TetrioPlayer> states = [];
test.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k) * 1000), entry[idCol] as String, entry[nickCol] as String)));
data.addEntries({states.last.userId: states}.entries);
} }
return data; return data;
} }

View File

@ -4,15 +4,14 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/widgets/text_timestamp.dart'; //import 'package:tetra_stats/widgets/tl_thingy.dart';
import 'package:tetra_stats/widgets/tl_thingy.dart';
import 'package:tetra_stats/widgets/user_thingy.dart'; import 'package:tetra_stats/widgets/user_thingy.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms(); final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
class StateView extends StatefulWidget { class StateView extends StatefulWidget {
final TetraLeague state; final TetrioPlayer state;
const StateView({super.key, required this.state}); const StateView({super.key, required this.state});
@override @override
@ -29,7 +28,7 @@ class StateState extends State<StateView> {
_scrollController = ScrollController(); _scrollController = ScrollController();
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){ if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){
windowManager.getTitle().then((value) => oldWindowTitle = value); windowManager.getTitle().then((value) => oldWindowTitle = value);
windowManager.setTitle("State from ${timestamp(widget.state.timestamp)}"); windowManager.setTitle("Tetra Stats: ${t.stateViewTitle(nickname: widget.state.username.toUpperCase(), date: dateFormat.format(widget.state.state))}");
} }
super.initState(); super.initState();
} }
@ -50,12 +49,15 @@ class StateState extends State<StateView> {
final t = Translations.of(context); final t = Translations.of(context);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text("State from ${timestamp(widget.state.timestamp)}"), title: Text(t.stateViewTitle(nickname: widget.state.username.toUpperCase(), date: dateFormat.format(widget.state.state))),
), ),
backgroundColor: Colors.black, backgroundColor: Colors.black,
body: SafeArea( body: SafeArea(
child: TLThingy(tl: widget.state, userID: widget.state.id, states: []) child: NestedScrollView(
) controller: _scrollController,
); headerSliverBuilder: (context, value) {
return [SliverToBoxAdapter(child: UserThingy(player: widget.state, showStateTimestamp: true, setState: _justUpdate))];
},
body: Container())));
} }
} }

View File

@ -1,19 +1,18 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/main.dart' show teto; import 'package:tetra_stats/main.dart' show teto;
import 'package:tetra_stats/utils/numers_formats.dart';
import 'package:tetra_stats/views/mathes_view.dart'; import 'package:tetra_stats/views/mathes_view.dart';
import 'package:tetra_stats/views/state_view.dart'; import 'package:tetra_stats/views/state_view.dart';
import 'package:tetra_stats/widgets/text_timestamp.dart'; import 'package:tetra_stats/widgets/text_timestamp.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
class StatesView extends StatefulWidget { class StatesView extends StatefulWidget {
final String nickname; final List<TetraLeague> states;
final String id; const StatesView({super.key, required this.states});
const StatesView({required this.nickname, required this.id, super.key});
@override @override
State<StatefulWidget> createState() => StatesState(); State<StatefulWidget> createState() => StatesState();
@ -26,7 +25,7 @@ class StatesState extends State<StatesView> {
void initState() { void initState() {
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){ if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){
windowManager.getTitle().then((value) => oldWindowTitle = value); windowManager.getTitle().then((value) => oldWindowTitle = value);
//windowManager.setTitle("Tetra Stats: ${t.statesViewTitle(number: widget.states.length, nickname: widget.states.last.id.toUpperCase())}"); windowManager.setTitle("Tetra Stats: ${t.statesViewTitle(number: widget.states.length, nickname: widget.states.last.id.toUpperCase())}");
} }
super.initState(); super.initState();
} }
@ -42,14 +41,14 @@ class StatesState extends State<StatesView> {
final t = Translations.of(context); final t = Translations.of(context);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(t.statesViewTitle(number: "", nickname: widget.nickname)), title: Text(t.statesViewTitle(number: widget.states.length, nickname: widget.states.first.id)),
actions: [ actions: [
IconButton( IconButton(
onPressed: (){ onPressed: (){
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => MatchesView(userID: widget.id, username: widget.nickname), builder: (context) => MatchesView(userID: widget.states.first.id, username: widget.states.first.id),
), ),
); );
}, icon: const Icon(Icons.list), tooltip: t.viewAllMatches) }, icon: const Icon(Icons.list), tooltip: t.viewAllMatches)
@ -57,63 +56,30 @@ class StatesState extends State<StatesView> {
), ),
backgroundColor: Colors.black, backgroundColor: Colors.black,
body: SafeArea( body: SafeArea(
child: FutureBuilder<List<TetraLeague>>(future: teto.getStates(widget.id), builder: (context, snapshot) { child: ListView.builder(
switch (snapshot.connectionState) { itemCount: widget.states.length,
case ConnectionState.none:
case ConnectionState.waiting:
case ConnectionState.active:
return const Center(child: CircularProgressIndicator(color: Colors.white));
case ConnectionState.done:
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.length,
prototypeItem: ListTile(
title: Text(""),
subtitle: Text("", style: TextStyle(color: Colors.grey)),
trailing: IconButton(icon: const Icon(Icons.delete_forever), onPressed: (){}),
),
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return ListTile(
title: Text(timestamp(snapshot.data![index].timestamp)), title: Text(timestamp(widget.states[index].timestamp)),
subtitle: Text( //subtitle: Text(t.statesViewEntry(level: widget.states[index].level.toStringAsFixed(2), gameTime: widget.states[index].gameTime, friends: widget.states[index].friendCount, rd: 0)),
t.statesViewEntry(level: f2.format(snapshot.data![index].tr), games: intf.format(snapshot.data![index].gamesPlayed), glicko: snapshot.data![index].glicko != null ? f2.format(snapshot.data![index].glicko) : "---", rd: snapshot.data![index].rd != null ? f2.format(snapshot.data![index].rd) : "--"),
style: TextStyle(color: Colors.grey),
),
trailing: IconButton( trailing: IconButton(
icon: const Icon(Icons.delete_forever), icon: const Icon(Icons.delete_forever),
onPressed: () { onPressed: () {
teto.deleteState(snapshot.data![index].id+snapshot.data![index].timestamp.millisecondsSinceEpoch.toRadixString(16)).then((value) => setState(() { //DateTime nn = widget.states[index].state;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.stateRemoved(date: timestamp(snapshot.data![index].timestamp))))); // teto.deleteState(widget.states[index]).then((value) => setState(() {
})); // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.stateRemoved(date: timestamp(nn)))));
// }));
}, },
), ),
onTap: () { onTap: () {
Navigator.push( // Navigator.push(
context, // context,
MaterialPageRoute( // MaterialPageRoute(
builder: (context) => StateView(state: snapshot.data![index]), // builder: (context) => StateView(state: widget.states[index]),
), // ),
); // );
}, },
); );
}); })));
} else if (snapshot.hasError) {
return Center(child:
Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(snapshot.error.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 42, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(snapshot.stackTrace.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 18), textAlign: TextAlign.center),
),
],
)
);
}
break;
}
return const Center(child: Text('default case of FutureBuilder', style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42), textAlign: TextAlign.center));
}
)));}
} }
}

View File

@ -78,7 +78,7 @@ class TrackedPlayersState extends State<TrackedPlayersView> {
case ConnectionState.active: case ConnectionState.active:
return const Center(child: CircularProgressIndicator(color: Colors.white)); return const Center(child: CircularProgressIndicator(color: Colors.white));
case ConnectionState.done: case ConnectionState.done:
final allPlayers = (snapshot.data != null) ? snapshot.data as Map<String, String> : <String, String>{}; final allPlayers = (snapshot.data != null) ? snapshot.data as Map<String, List<TetrioPlayer>> : <String, List<TetrioPlayer>>{};
List<String> keys = allPlayers.keys.toList(); List<String> keys = allPlayers.keys.toList();
return NestedScrollView( return NestedScrollView(
headerSliverBuilder: (context, value) { headerSliverBuilder: (context, value) {
@ -107,24 +107,24 @@ class TrackedPlayersState extends State<TrackedPlayersView> {
body: ListView.builder( body: ListView.builder(
itemCount: allPlayers.length, itemCount: allPlayers.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
print(index);
return ListTile( return ListTile(
title: Text(allPlayers[keys[index]]??"No nickname (huh?)"), title: Text(t.trackedPlayersEntry(nickname: allPlayers[keys[index]]!.last.username, numberOfStates: allPlayers[keys[index]]!.length)),
subtitle: Text(keys[index], style: TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey)), subtitle: Text(t.trackedPlayersDescription(firstStateDate: timestamp(allPlayers[keys[index]]!.first.state), lastStateDate: timestamp(allPlayers[keys[index]]!.last.state))),
trailing: IconButton( trailing: IconButton(
icon: const Icon(Icons.delete_forever), icon: const Icon(Icons.delete_forever),
onPressed: () { onPressed: () {
String nn = allPlayers[keys[index]]!.last.username;
setState(() {teto.deletePlayer(keys[index]);}); setState(() {teto.deletePlayer(keys[index]);});
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.trackedPlayersStatesDeleted(nickname: allPlayers[keys[index]]??"No nickname (huh?)")))); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.trackedPlayersStatesDeleted(nickname: nn))));
}, },
), ),
onTap: () { onTap: () {
Navigator.push( // Navigator.push(
context, // context,
MaterialPageRoute( // MaterialPageRoute(
builder: (context) => StatesView(nickname: allPlayers[keys[index]]!, id: keys[index]), // builder: (context) => StatesView(states: allPlayers[keys[index]]!),
), // ),
); // );
}, },
); );
})); }));

View File

@ -124,9 +124,8 @@ class _ZenithThingyState extends State<ZenithThingy> {
StatCellNum(playerStat: record!.aggregateStats.apm, playerStatLabel: t.statCellNum.apm, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: true), StatCellNum(playerStat: record!.aggregateStats.apm, playerStatLabel: t.statCellNum.apm, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: true),
StatCellNum(playerStat: record!.aggregateStats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false), StatCellNum(playerStat: record!.aggregateStats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
StatCellNum(playerStat: record!.aggregateStats.vs, playerStatLabel: t.statCellNum.vs, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: true), StatCellNum(playerStat: record!.aggregateStats.vs, playerStatLabel: t.statCellNum.vs, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: true),
StatCellNum(playerStat: record!.stats.kills, playerStatLabel: "KO's", isScreenBig: bigScreen, higherIsBetter: true), StatCellNum(playerStat: record!.stats.kills, playerStatLabel: "Kills", isScreenBig: bigScreen, higherIsBetter: true),
StatCellNum(playerStat: record!.stats.cps, playerStatLabel: "Climb speed\n(Peak: ${f2.format(record!.stats.zenith!.peakrank)})", fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true), StatCellNum(playerStat: record!.stats.cps, playerStatLabel: "CPS\n(Peak: ${f2.format(record!.stats.zenith!.peakrank)})", fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true)
StatCellNum(playerStat: record!.stats.topBtB, playerStatLabel: "Top B2B\nchain", isScreenBig: bigScreen, higherIsBetter: true)
], ],
), ),
FinesseThingy(record?.stats.finesse, record?.stats.finessePercentage), FinesseThingy(record?.stats.finesse, record?.stats.finessePercentage),

View File

@ -2,7 +2,7 @@ name: tetra_stats
description: Track your and other player stats in TETR.IO description: Track your and other player stats in TETR.IO
publish_to: 'none' publish_to: 'none'
version: 1.6.9+35 version: 1.6.8+34
environment: environment:
sdk: '>=3.0.0' sdk: '>=3.0.0'

View File

@ -89,6 +89,9 @@
"smooth": "Smooth", "smooth": "Smooth",
"postSeason": "Off-season", "postSeason": "Off-season",
"seasonStarts": "Season starts in:", "seasonStarts": "Season starts in:",
"myMessadgeHeader": "A messadge from dan63",
"myMessadgeBody": "TETR.IO Tetra Channel API has been seriously modified after the last update, therefore, some functions may not work. I will try to catch up and add new stats (and return back the old ones) as soon, as public docs on new Tetra Channel API will be available.",
"preSeasonMessage": "Right now you can play unranked FT3 matches with hidden glicko (200 RD 🙂).\nSeason ${n} rules applied",
"nanow": "Not avaliable for now...", "nanow": "Not avaliable for now...",
"seasonEnds": "Season ends in ${countdown}", "seasonEnds": "Season ends in ${countdown}",
"seasonEnded": "Season has ended", "seasonEnded": "Season has ended",
@ -155,7 +158,7 @@
"stateViewTitle": "${nickname} account on ${date}", "stateViewTitle": "${nickname} account on ${date}",
"statesViewTitle": "${number} states of ${nickname} account", "statesViewTitle": "${number} states of ${nickname} account",
"matchesViewTitle": "${nickname} TL matches", "matchesViewTitle": "${nickname} TL matches",
"statesViewEntry": "${level} TR, ${glicko}±${rd} Glicko, ${games} игр сыграно", "statesViewEntry": "Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD",
"stateRemoved": "${date} state was removed from database!", "stateRemoved": "${date} state was removed from database!",
"matchRemoved": "${date} match was removed from database!", "matchRemoved": "${date} match was removed from database!",
"viewAllMatches": "View all matches", "viewAllMatches": "View all matches",

View File

@ -89,6 +89,9 @@
"smooth": "Гладкий", "smooth": "Гладкий",
"postSeason": "Внесезонье", "postSeason": "Внесезонье",
"seasonStarts": "Сезон начнётся через:", "seasonStarts": "Сезон начнётся через:",
"myMessadgeHeader": "Сообщение от dan63",
"myMessadgeBody": "TETR.IO Tetra Channel API был серьёзно изменён после последнего обновления, поэтому некоторый функционал может не работать. Я постараюсь добавить новую статистику (и вернуть старую) как только будут опубликована новая документация по данному API.",
"preSeasonMessage": "Прямо сейчас вы можете сыграть безранговый матч до трёх побед со скрытым Glicko (200 RD 🙂).\nПрименяются правила ${n} сезона",
"nanow": "Пока недоступно...", "nanow": "Пока недоступно...",
"seasonEnds": "Сезон закончится через ${countdown}", "seasonEnds": "Сезон закончится через ${countdown}",
"seasonEnded": "Сезон закончился", "seasonEnded": "Сезон закончился",
@ -155,7 +158,7 @@
"stateViewTitle": "Аккаунт ${nickname} ${date}", "stateViewTitle": "Аккаунт ${nickname} ${date}",
"statesViewTitle": "${number} состояний аккаунта ${nickname}", "statesViewTitle": "${number} состояний аккаунта ${nickname}",
"matchesViewTitle": "Матчи аккаунта ${nickname}", "matchesViewTitle": "Матчи аккаунта ${nickname}",
"statesViewEntry": "${level} TR, ${glicko}±${rd} Glicko, ${games} игр сыграно", "statesViewEntry": "${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD",
"stateRemoved": "Состояние от ${date} было удалено из локальной базы данных!", "stateRemoved": "Состояние от ${date} было удалено из локальной базы данных!",
"matchRemoved": "Матч от ${date} был удален из локальной базы данных!", "matchRemoved": "Матч от ${date} был удален из локальной базы данных!",
"viewAllMatches": "Все матчи", "viewAllMatches": "Все матчи",