Redoing local DB

- Retrieving history is slow rn because i save one entry at the time
- No check if entry is already here
- S1 history is not avaliable for now
- Maybe i should store it like i did that during S1 but idk
This commit is contained in:
dan63047 2024-09-02 00:44:19 +03:00
parent d710674973
commit 38ec643a01
16 changed files with 196 additions and 232 deletions

View File

@ -3,10 +3,10 @@
import 'dart:math'; import 'dart:math';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tetra_stats/data_objects/tetra_stats.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:vector_math/vector_math.dart'; import 'package:vector_math/vector_math.dart';
const int currentSeason = 2;
const double noTrRd = 60.9; const double noTrRd = 60.9;
const double apmWeight = 1; const double apmWeight = 1;
const double ppsWeight = 45; const double ppsWeight = 45;
@ -228,7 +228,6 @@ class TetrioPlayer {
bool? badstanding; bool? badstanding;
String? botmaster; String? botmaster;
Connections? connections; Connections? connections;
TetraLeague? tlSeason1;
TetrioZen? zen; TetrioZen? zen;
Distinguishment? distinguishment; Distinguishment? distinguishment;
DateTime? cachedUntil; DateTime? cachedUntil;
@ -254,7 +253,6 @@ class TetrioPlayer {
this.badstanding, this.badstanding,
this.botmaster, this.botmaster,
required this.connections, required this.connections,
required this.tlSeason1,
this.zen, this.zen,
this.distinguishment, this.distinguishment,
this.cachedUntil this.cachedUntil
@ -281,7 +279,6 @@ class TetrioPlayer {
country = json['country']; country = json['country'];
supporterTier = json['supporter_tier'] ?? 0; supporterTier = json['supporter_tier'] ?? 0;
verified = json['verified'] ?? false; verified = json['verified'] ?? false;
tlSeason1 = json['league'] != null ? TetraLeague.fromJson(json['league'], stateTime) : null;
avatarRevision = json['avatar_revision']; avatarRevision = json['avatar_revision'];
bannerRevision = json['banner_revision']; bannerRevision = json['banner_revision'];
bio = json['bio']; bio = json['bio'];
@ -307,7 +304,6 @@ class TetrioPlayer {
if (country != null) data['country'] = country; if (country != null) data['country'] = country;
if (supporterTier > 0) data['supporter_tier'] = supporterTier; if (supporterTier > 0) data['supporter_tier'] = supporterTier;
if (verified) data['verified'] = verified; if (verified) data['verified'] = verified;
data['league'] = tlSeason1?.toJson();
if (distinguishment != null) data['distinguishment'] = distinguishment?.toJson(); if (distinguishment != null) data['distinguishment'] = distinguishment?.toJson();
if (avatarRevision != null) data['avatar_revision'] = avatarRevision; if (avatarRevision != null) data['avatar_revision'] = avatarRevision;
if (bannerRevision != null) data['banner_revision'] = bannerRevision; if (bannerRevision != null) data['banner_revision'] = bannerRevision;
@ -337,83 +333,15 @@ class TetrioPlayer {
if (badstanding != other.badstanding) return false; if (badstanding != other.badstanding) return false;
if (botmaster != other.botmaster) return false; if (botmaster != other.botmaster) return false;
if (connections != other.connections) return false; if (connections != other.connections) return false;
if (tlSeason1 != other.tlSeason1) return false;
if (distinguishment != other.distinguishment) return false; if (distinguishment != other.distinguishment) return false;
return true; return true;
} }
bool checkForRetrivedHistory(covariant TetrioPlayer other) {
return tlSeason1!.lessStrictCheck(other.tlSeason1!);
}
@override @override
String toString() { String toString() {
return "$username ($state)"; return "$username ($state)";
} }
num? getStatByEnum(Stats stat){
switch (stat) {
case Stats.tr:
return tlSeason1?.tr;
case Stats.glicko:
return tlSeason1?.glicko;
case Stats.gxe:
return tlSeason1?.gxe;
case Stats.s1tr:
return tlSeason1?.s1tr;
case Stats.rd:
return tlSeason1?.rd;
case Stats.gp:
return tlSeason1?.gamesPlayed;
case Stats.gw:
return tlSeason1?.gamesWon;
case Stats.wr:
return tlSeason1?.winrate;
case Stats.apm:
return tlSeason1?.apm;
case Stats.pps:
return tlSeason1?.pps;
case Stats.vs:
return tlSeason1?.vs;
case Stats.app:
return tlSeason1?.nerdStats?.app;
case Stats.dss:
return tlSeason1?.nerdStats?.dss;
case Stats.dsp:
return tlSeason1?.nerdStats?.dsp;
case Stats.appdsp:
return tlSeason1?.nerdStats?.appdsp;
case Stats.vsapm:
return tlSeason1?.nerdStats?.vsapm;
case Stats.cheese:
return tlSeason1?.nerdStats?.cheese;
case Stats.gbe:
return tlSeason1?.nerdStats?.gbe;
case Stats.nyaapp:
return tlSeason1?.nerdStats?.nyaapp;
case Stats.area:
return tlSeason1?.nerdStats?.area;
case Stats.eTR:
return tlSeason1?.estTr?.esttr;
case Stats.acceTR:
return tlSeason1?.esttracc;
case Stats.acceTRabs:
return tlSeason1?.esttracc?.abs();
case Stats.opener:
return tlSeason1?.playstyle?.opener;
case Stats.plonk:
return tlSeason1?.playstyle?.plonk;
case Stats.infDS:
return tlSeason1?.playstyle?.infds;
case Stats.stride:
return tlSeason1?.playstyle?.stride;
case Stats.stridemMinusPlonk:
return tlSeason1?.playstyle != null ? tlSeason1!.playstyle!.stride - tlSeason1!.playstyle!.plonk : null;
case Stats.openerMinusInfDS:
return tlSeason1?.playstyle != null ? tlSeason1!.playstyle!.opener - tlSeason1!.playstyle!.infds : null;
}
}
@override @override
int get hashCode => state.hashCode; int get hashCode => state.hashCode;
@ -444,7 +372,7 @@ class Summaries{
if (json['zenithex']['record'] != null) zenithEx = RecordSingle.fromJson(json['zenithex']['record'], json['zenithex']['rank'], json['zenithex']['rank_local']); if (json['zenithex']['record'] != null) zenithEx = RecordSingle.fromJson(json['zenithex']['record'], json['zenithex']['rank'], json['zenithex']['rank_local']);
if (json['zenithex']['best']['record'] != null) zenithCareerBest = RecordSingle.fromJson(json['zenithex']['best']['record'], json['zenith']['best']['rank'], -1); if (json['zenithex']['best']['record'] != null) zenithCareerBest = RecordSingle.fromJson(json['zenithex']['best']['record'], json['zenith']['best']['rank'], -1);
achievements = [for (var achievement in json['achievements']) Achievement.fromJson(achievement)]; achievements = [for (var achievement in json['achievements']) Achievement.fromJson(achievement)];
league = TetraLeague.fromJson(json['league'], DateTime.now()); league = TetraLeague.fromJson(json['league'], DateTime.now(), currentSeason, i);
zen = TetrioZen.fromJson(json['zen']); zen = TetrioZen.fromJson(json['zen']);
} }
} }
@ -1373,6 +1301,7 @@ class EndContextMulti {
} }
class TetraLeague { class TetraLeague {
late String id;
late DateTime timestamp; late DateTime timestamp;
late int gamesPlayed; late int gamesPlayed;
late int gamesWon; late int gamesWon;
@ -1397,10 +1326,11 @@ class TetraLeague {
NerdStats? nerdStats; NerdStats? nerdStats;
EstTr? estTr; EstTr? estTr;
Playstyle? playstyle; Playstyle? playstyle;
List? records; late int season;
TetraLeague( TetraLeague(
{required this.timestamp, {required this.id,
required this.timestamp,
required this.gamesPlayed, required this.gamesPlayed,
required this.gamesWon, required this.gamesWon,
required this.bestRank, required this.bestRank,
@ -1421,7 +1351,7 @@ class TetraLeague {
this.apm, this.apm,
this.pps, this.pps,
this.vs, this.vs,
this.records}){ required this.season}){
nerdStats = (apm != null && pps != null && vs != null) ? NerdStats(apm!, pps!, vs!) : null; nerdStats = (apm != null && pps != null && vs != null) ? NerdStats(apm!, pps!, vs!) : null;
estTr = (nerdStats != null) ? EstTr(apm!, pps!, vs!, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe) : null; estTr = (nerdStats != null) ? EstTr(apm!, pps!, vs!, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe) : null;
playstyle =(nerdStats != null) ? Playstyle(apm!, pps!, nerdStats!.app, nerdStats!.vsapm, nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, estTr!.statrank) : null; playstyle =(nerdStats != null) ? Playstyle(apm!, pps!, nerdStats!.app, nerdStats!.vsapm, nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, estTr!.statrank) : null;
@ -1430,8 +1360,10 @@ class TetraLeague {
double get winrate => gamesWon / gamesPlayed; double get winrate => gamesWon / gamesPlayed;
double get s1tr => gxe * 250; double get s1tr => gxe * 250;
TetraLeague.fromJson(Map<String, dynamic> json, ts) { TetraLeague.fromJson(Map<String, dynamic> json, ts, int s, String i) {
timestamp = ts; timestamp = ts;
season = s;
id = i;
gamesPlayed = json['gamesplayed'] ?? 0; gamesPlayed = json['gamesplayed'] ?? 0;
gamesWon = json['gameswon'] ?? 0; gamesWon = json['gameswon'] ?? 0;
tr = json['tr'] != null ? json['tr'].toDouble() : json['rating'] != null ? json['rating'].toDouble() : -1; tr = json['tr'] != null ? json['tr'].toDouble() : json['rating'] != null ? json['rating'].toDouble() : -1;
@ -1470,25 +1402,29 @@ class TetraLeague {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{}; final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['timestamp'] = timestamp.millisecondsSinceEpoch;
if (gamesPlayed > 0) data['gamesplayed'] = gamesPlayed; if (gamesPlayed > 0) data['gamesplayed'] = gamesPlayed;
if (gamesWon > 0) data['gameswon'] = gamesWon; if (gamesWon > 0) data['gameswon'] = gamesWon;
if (tr >= 0) data['tr'] = tr; if (tr >= 0) data['tr'] = tr;
if (glicko != null) data['glicko'] = glicko; if (glicko != null) data['glicko'] = glicko;
if (gxe != -1) data['gxe'] = gxe;
if (rd != null && rd != noTrRd) data['rd'] = rd; if (rd != null && rd != noTrRd) data['rd'] = rd;
if (rank != 'z') data['rank'] = rank; if (rank != 'z') data['rank'] = rank;
if (bestRank != 'z') data['bestrank'] = bestRank; if (bestRank != 'z') data['bestrank'] = bestRank;
if (apm != null) data['apm'] = apm; if (apm != null) data['apm'] = apm;
if (pps != null) data['pps'] = pps; if (pps != null) data['pps'] = pps;
if (vs != null) data['vs'] = vs; if (vs != null) data['vs'] = vs;
if (decaying) data['decaying'] = decaying; if (decaying) data['decaying'] = decaying ? 1 : 0;
if (standing >= 0) data['standing'] = standing; if (standing >= 0) data['standing'] = standing;
if (!rankCutoffs.containsValue(percentile)) data['percentile'] = percentile; data['percentile'] = percentile;
if (standingLocal >= 0) data['standing_local'] = standingLocal; if (standingLocal >= 0) data['standing_local'] = standingLocal;
if (prevRank != null) data['prev_rank'] = prevRank; if (prevRank != null) data['prev_rank'] = prevRank;
if (prevAt >= 0) data['prev_at'] = prevAt; if (prevAt >= 0) data['prev_at'] = prevAt;
if (nextRank != null) data['next_rank'] = nextRank; if (nextRank != null) data['next_rank'] = nextRank;
if (nextAt >= 0) data['next_at'] = nextAt; if (nextAt >= 0) data['next_at'] = nextAt;
if (percentileRank != rank) data['percentile_rank'] = percentileRank; data['percentile_rank'] = percentileRank;
data['season'] = season;
return data; return data;
} }
} }
@ -2193,7 +2129,7 @@ class TetrioPlayersLeaderboard {
avgInfDS /= filtredLeaderboard.length; avgInfDS /= filtredLeaderboard.length;
avgGamesPlayed = (totalGamesPlayed / filtredLeaderboard.length).floor(); avgGamesPlayed = (totalGamesPlayed / filtredLeaderboard.length).floor();
avgGamesWon = (totalGamesWon / filtredLeaderboard.length).floor(); avgGamesWon = (totalGamesWon / filtredLeaderboard.length).floor();
return [TetraLeague(timestamp: DateTime.now(), apm: avgAPM, pps: avgPPS, vs: avgVS, gxe: avgGlixare, glicko: avgGlicko, rd: avgRD, gamesPlayed: avgGamesPlayed, gamesWon: avgGamesWon, bestRank: rank, decaying: false, tr: avgTR, rank: rank == "" ? "z" : rank, percentileRank: rank, percentile: rankCutoffs[rank]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1), return [TetraLeague(id: "", timestamp: DateTime.now(), apm: avgAPM, pps: avgPPS, vs: avgVS, gxe: avgGlixare, glicko: avgGlicko, rd: avgRD, gamesPlayed: avgGamesPlayed, gamesWon: avgGamesWon, bestRank: rank, decaying: false, tr: avgTR, rank: rank == "" ? "z" : rank, percentileRank: rank, percentile: rankCutoffs[rank]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1, season: currentSeason),
{ {
"everyone": rank == "", "everyone": rank == "",
"totalGamesPlayed": totalGamesPlayed, "totalGamesPlayed": totalGamesPlayed,
@ -2375,7 +2311,7 @@ class TetrioPlayersLeaderboard {
"entries": filtredLeaderboard "entries": filtredLeaderboard
}]; }];
}else{ }else{
return [TetraLeague(timestamp: DateTime.now(), apm: 0, pps: 0, vs: 0, glicko: 0, rd: noTrRd, gamesPlayed: 0, gamesWon: 0, bestRank: rank, decaying: false, tr: 0, rank: rank, percentileRank: rank, gxe: -1, percentile: rankCutoffs[rank]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1), return [TetraLeague(id: "", timestamp: DateTime.now(), apm: 0, pps: 0, vs: 0, glicko: 0, rd: noTrRd, gamesPlayed: 0, gamesWon: 0, bestRank: rank, decaying: false, tr: 0, rank: rank, percentileRank: rank, gxe: -1, percentile: rankCutoffs[rank]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1, season: currentSeason),
{"players": filtredLeaderboard.length, "lowestTR": 0, "toEnterTR": 0, "toEnterGlicko": 0}]; {"players": filtredLeaderboard.length, "lowestTR": 0, "toEnterTR": 0, "toEnterGlicko": 0}];
} }
} }

View File

@ -39,14 +39,14 @@ ThemeData theme = ThemeData(
shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(12.0), right: Radius.circular(12.0)))), shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(12.0), right: Radius.circular(12.0)))),
elevation: WidgetStatePropertyAll(8.0) elevation: WidgetStatePropertyAll(8.0)
), ),
chipTheme: ChipThemeData( chipTheme: const ChipThemeData(
side: BorderSide(color: Colors.transparent), side: BorderSide(color: Colors.transparent),
), ),
segmentedButtonTheme: SegmentedButtonThemeData( segmentedButtonTheme: SegmentedButtonThemeData(
style: ButtonStyle( style: ButtonStyle(
side: WidgetStatePropertyAll(BorderSide(color: Colors.transparent)), side: const WidgetStatePropertyAll(BorderSide(color: Colors.transparent)),
surfaceTintColor: WidgetStatePropertyAll(Colors.cyanAccent), surfaceTintColor: const WidgetStatePropertyAll(Colors.cyanAccent),
iconColor: WidgetStatePropertyAll(Colors.cyanAccent), iconColor: const WidgetStatePropertyAll(Colors.cyanAccent),
shadowColor: WidgetStatePropertyAll(Colors.cyanAccent.shade200), shadowColor: WidgetStatePropertyAll(Colors.cyanAccent.shade200),
) )
), ),

View File

@ -33,6 +33,7 @@ class DB {
await db.execute(createTetrioUsersToTrack); await db.execute(createTetrioUsersToTrack);
await db.execute(createTetrioTLRecordsTable); await db.execute(createTetrioTLRecordsTable);
await db.execute(createTetrioTLReplayStats); await db.execute(createTetrioTLReplayStats);
await db.execute(createTetrioLeagueTable);
} on MissingPlatformDirectoryException { } on MissingPlatformDirectoryException {
throw UnableToGetDocuments(); throw UnableToGetDocuments();
} }

View File

@ -5,6 +5,7 @@ import 'dart:convert';
import 'dart:developer' as developer; import 'dart:developer' as developer;
import 'dart:io'; import 'dart:io';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sql.dart';
import 'package:tetra_stats/data_objects/tetra_stats.dart'; import 'package:tetra_stats/data_objects/tetra_stats.dart';
import 'package:tetra_stats/data_objects/tetrio_multiplayer_replay.dart'; import 'package:tetra_stats/data_objects/tetrio_multiplayer_replay.dart';
import 'package:tetra_stats/main.dart' show packageInfo; import 'package:tetra_stats/main.dart' show packageInfo;
@ -21,6 +22,7 @@ const String tetrioUsersTable = "tetrioUsers";
const String tetrioUsersToTrackTable = "tetrioUsersToTrack"; const String tetrioUsersToTrackTable = "tetrioUsersToTrack";
const String tetraLeagueMatchesTable = "tetrioAlphaLeagueMathces"; const String tetraLeagueMatchesTable = "tetrioAlphaLeagueMathces";
const String tetrioTLReplayStatsTable = "tetrioTLReplayStats"; const String tetrioTLReplayStatsTable = "tetrioTLReplayStats";
const String tetrioLeagueTable = "tetrioLeague";
const String idCol = "id"; const String idCol = "id";
const String replayID = "replayId"; const String replayID = "replayId";
const String nickCol = "nickname"; const String nickCol = "nickname";
@ -67,6 +69,33 @@ const String createTetrioTLReplayStats = '''
PRIMARY KEY("id") PRIMARY KEY("id")
) )
'''; ''';
const String createTetrioLeagueTable = '''
CREATE TABLE IF NOT EXISTS "tetrioLeague" (
"id" TEXT NOT NULL,
"timestamp" INTEGER NOT NULL,
"gamesplayed" INTEGER NOT NULL DEFAULT 0,
"gameswon" INTEGER NOT NULL DEFAULT 0,
"tr" REAL,
"glicko" REAL,
"rd" REAL,
"gxe" REAL,
"rank" TEXT NOT NULL DEFAULT 'z',
"bestrank" TEXT NOT NULL DEFAULT 'z',
"apm" REAL,
"pps" REAL,
"vs" REAL,
"decaying" INTEGER NOT NULL DEFAULT 0,
"standing" INTEGER NOT NULL DEFAULT -1,
"standing_local" INTEGER NOT NULL DEFAULT -1,
"percentile" REAL NOT NULL,
"prev_rank" TEXT,
"prev_at" INTEGER NOT NULL DEFAULT -1,
"next_rank" TEXT,
"next_at" INTEGER NOT NULL DEFAULT -1,
"percentile_rank" TEXT NOT NULL DEFAULT 'z',
"season" INTEGER NOT NULL DEFAULT 1
)
''';
class CacheController { class CacheController {
late Map<String, dynamic> _cache; late Map<String, dynamic> _cache;
@ -546,7 +575,7 @@ class TetrioService extends DB {
/// Retrieves Tetra League history from p1nkl0bst3r api for a player with given [id]. Returns a list of states /// Retrieves Tetra League history from p1nkl0bst3r api for a player with given [id]. Returns a list of states
/// (state = instance of [TetrioPlayer] at some point of time). Can throw an exception if fails to retrieve data. /// (state = instance of [TetrioPlayer] at some point of time). Can throw an exception if fails to retrieve data.
Future<List<TetrioPlayer>> fetchAndsaveTLHistory(String id) async { Future<List<TetraLeague>> fetchAndsaveTLHistory(String id) async {
Uri url; Uri url;
if (kIsWeb) { if (kIsWeb) {
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLHistory", "user": id}); url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLHistory", "user": id});
@ -558,27 +587,14 @@ class TetrioService extends DB {
switch (response.statusCode) { switch (response.statusCode) {
case 200: case 200:
await ensureDbIsOpen();
final db = getDatabaseOrThrow();
// that one api returns csv instead of json // that one api returns csv instead of json
List<List<dynamic>> csv = const CsvToListConverter().convert(response.body)..removeAt(0); List<List<dynamic>> csv = const CsvToListConverter().convert(response.body)..removeAt(0);
List<TetrioPlayer> history = []; List<TetraLeague> history = [];
// doesn't return nickname, need to retrieve it separately
String nick = await getNicknameByID(id);
for (List<dynamic> entry in csv){ // each entry is one state for (List<dynamic> entry in csv){ // each entry is one state
TetrioPlayer state = TetrioPlayer( TetraLeague state = TetraLeague(
userId: id, id: id,
username: nick,
role: "p1nkl0bst3r",
state: DateTime.parse(entry[9]),
badges: [],
friendCount: -1,
gamesPlayed: -1,
gamesWon: -1,
gameTime: const Duration(seconds: -1),
xp: -1,
supporterTier: 0,
verified: false,
connections: null,
tlSeason1: TetraLeague(
timestamp: DateTime.parse(entry[9]), timestamp: DateTime.parse(entry[9]),
apm: entry[6] != '' ? entry[6] : null, apm: entry[6] != '' ? entry[6] : null,
pps: entry[7] != '' ? entry[7] : null, pps: entry[7] != '' ? entry[7] : null,
@ -597,24 +613,12 @@ class TetrioService extends DB {
standing: -1, standing: -1,
standingLocal: -1, standingLocal: -1,
nextAt: -1, nextAt: -1,
prevAt: -1 prevAt: -1,
), season: 1
); );
history.add(state); history.add(state);
await db.insert(tetrioLeagueTable, state.toJson(), conflictAlgorithm: ConflictAlgorithm.replace);
} }
// trying to dump it to local DB
await ensureDbIsOpen();
final db = getDatabaseOrThrow();
List<TetrioPlayer> states = await getPlayer(id);
if (states.isEmpty) await createPlayer(history.first);
states.insertAll(0, history.reversed);
final Map<String, dynamic> statesJson = {};
for (var e in states) { // making one big json out of this list
statesJson.addEntries({(e.state.millisecondsSinceEpoch ~/ 1000).toString(): e.toJson()}.entries);
}
// and putting it to local DB
await db.update(tetrioUsersTable, {idCol: id, nickCol: nick, statesCol: jsonEncode(statesJson)}, where: '$idCol = ?', whereArgs: [id]);
return history; return history;
case 404: case 404:
developer.log("fetchTLHistory: Probably, history doesn't exist", name: "services/tetrio_crud", error: response.statusCode); developer.log("fetchTLHistory: Probably, history doesn't exist", name: "services/tetrio_crud", error: response.statusCode);
@ -1057,7 +1061,10 @@ class TetrioService extends DB {
if (results.isNotEmpty) { if (results.isNotEmpty) {
throw TetrioPlayerAlreadyExist(); throw TetrioPlayerAlreadyExist();
} }
await db.insert(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username}, conflictAlgorithm: ConflictAlgorithm.replace);
db.insert(tetrioUsersToTrackTable, {idCol: tetrioPlayer.userId}); db.insert(tetrioUsersToTrackTable, {idCol: tetrioPlayer.userId});
_players[tetrioPlayer.userId] = tetrioPlayer.username;
_tetrioStreamController.add(_players);
} }
/// Returns bool, which tells whether is given [id] is in [tetrioUsersToTrackTable]. /// Returns bool, which tells whether is given [id] is in [tetrioUsersToTrackTable].
@ -1081,6 +1088,7 @@ class TetrioService extends DB {
await ensureDbIsOpen(); await ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
final deletedPlayer = await db.delete(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]); final deletedPlayer = await db.delete(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
await db.delete(tetrioUsersTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
if (deletedPlayer != 1) { if (deletedPlayer != 1) {
throw CouldNotDeletePlayer(); throw CouldNotDeletePlayer();
} else { } else {
@ -1089,72 +1097,67 @@ class TetrioService extends DB {
} }
} }
/// Saves state (which is [tetrioPlayer]) to the local database. Future<List<TetraLeague>> getStates(String userID, int season) async {
Future<void> storeState(TetrioPlayer tetrioPlayer) async {
// if tetrio player doesn't have entry in database - just calling different function
List<TetrioPlayer> states = await getPlayer(tetrioPlayer.userId);
if (states.isEmpty) {
await createPlayer(tetrioPlayer);
return;
}
// we not going to add state, that is same, as the previous
if (!states.last.isSameState(tetrioPlayer)) states.add(tetrioPlayer);
// Making map of the states
final Map<String, dynamic> statesJson = {};
for (var e in states) {
// Saving in format: {"unix_seconds": json_of_state}
statesJson.addEntries({(e.state.millisecondsSinceEpoch ~/ 1000).toString(): e.toJson()}.entries);
}
// Rewrite our database
await ensureDbIsOpen(); await ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
await db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)}, List<Map> query = await db.query(tetrioLeagueTable, where: '"id" = ? AND "season" = ?', whereArgs: [userID, season]);
where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]); List<TetraLeague> result = [];
for (var entry in query){
result.add(TetraLeague.fromJson(entry as Map<String, dynamic>, entry["timestamp"], entry["season"], entry["id"]));
}
return result;
}
/// Saves state (which is [TetraLeague]) to the local database.
Future<void> storeState(TetraLeague league) async {
await ensureDbIsOpen();
final db = getDatabaseOrThrow();
List<Map> test = await db.query(tetrioLeagueTable, where: '"id" = ? AND "gamesplayed" = ? AND "rd" = ?', whereArgs: [league.id, league.gamesPlayed, league.rd]);
if (test.isEmpty) {
await db.insert(tetrioLeagueTable, league.toJson());
}
} }
/// Remove state (which is [tetrioPlayer]) from the local database /// Remove state (which is [tetrioPlayer]) from the local database
Future<void> deleteState(TetrioPlayer tetrioPlayer) async { // Future<void> deleteState(TetrioPlayer tetrioPlayer) async {
await ensureDbIsOpen(); // await ensureDbIsOpen();
final db = getDatabaseOrThrow(); // final db = getDatabaseOrThrow();
List<TetrioPlayer> states = await getPlayer(tetrioPlayer.userId); // //List<TetrioPlayer> states = await getPlayer(tetrioPlayer.userId);
// removing state from map that contain every state of each user // // removing state from map that contain every state of each user
states.removeWhere((element) => element.state == tetrioPlayer.state); // states.removeWhere((element) => element.state == tetrioPlayer.state);
// Making map of the states (without deleted one) // // Making map of the states (without deleted one)
final Map<String, dynamic> statesJson = {}; // final Map<String, dynamic> statesJson = {};
for (var e in states) { // // for (var e in states) {
statesJson.addEntries({(e.state.millisecondsSinceEpoch ~/ 1000).toString(): e.toJson()}.entries); // // statesJson.addEntries({(e.state.millisecondsSinceEpoch ~/ 1000).toString(): e.toJson()}.entries);
} // // }
// Rewriting database entry with new json // // Rewriting database entry with new json
await db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)}, // await db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)},
where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]); // where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]);
_tetrioStreamController.add(_players); // _tetrioStreamController.add(_players);
} // }
/// Returns list of all states of player with given [id] from database. Can return empty list if player /// Returns list of all states of player with given [id] from database. Can return empty list if player
/// was not found. /// was not found.
Future<List<TetrioPlayer>> getPlayer(String id) async { // Future<List<TetrioPlayer>> getPlayer(String id) async {
await ensureDbIsOpen(); // await ensureDbIsOpen();
final db = getDatabaseOrThrow(); // final db = getDatabaseOrThrow();
List<TetrioPlayer> states = []; // List<TetrioPlayer> states = [];
final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]); // final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
if (results.isEmpty) { // if (results.isEmpty) {
return states; // it empty // return states; // it empty
} else { // } else {
dynamic rawStates = results.first['jsonStates'] as String; // dynamic rawStates = results.first['jsonStates'] as String;
rawStates = json.decode(rawStates); // rawStates = json.decode(rawStates);
// recreating objects of states // // 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))); // rawStates.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k) * 1000), id, results.first[nickCol] as String)));
// updating the stream // // updating the stream
_players.removeWhere((key, value) => key == id); // _players.removeWhere((key, value) => key == id);
_players.addEntries({states.last.userId: states.last.username}.entries); // _players.addEntries({states.last.userId: states.last.username}.entries);
_tetrioStreamController.add(_players); // _tetrioStreamController.add(_players);
return states; // 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.

View File

@ -85,6 +85,7 @@ class CompareState extends State<CompareView> {
theRedSide = [null, theRedSide = [null,
null, null,
Summaries(user, TetraLeague( Summaries(user, TetraLeague(
id: "",
timestamp: DateTime.now(), timestamp: DateTime.now(),
apm: apm, apm: apm,
pps: pps, pps: pps,
@ -102,7 +103,7 @@ class CompareState extends State<CompareView> {
standing: -1, standing: -1,
standingLocal: -1, standingLocal: -1,
nextAt: -1, nextAt: -1,
prevAt: -1), TetrioZen(level: 0, score: 0))]; prevAt: -1, season: currentSeason), TetrioZen(level: 0, score: 0))];
return setState(() {}); return setState(() {});
} }
var player = await teto.fetchPlayer(user); var player = await teto.fetchPlayer(user);
@ -132,9 +133,11 @@ class CompareState extends State<CompareView> {
_justUpdate(); _justUpdate();
} }
void changeRedSide(TetrioPlayer user) { void changeRedSide(TetraLeague user) {
setState(() {theRedSide[0] = user; setState(() {
theRedSide[2].league = user.tlSeason1;}); //theRedSide[0] = user;
theRedSide[2].league = user;
});
} }
void fetchGreenSide(String user) async { void fetchGreenSide(String user) async {
@ -161,6 +164,7 @@ class CompareState extends State<CompareView> {
theGreenSide = [null, theGreenSide = [null,
null, null,
Summaries(user, TetraLeague( Summaries(user, TetraLeague(
id: "",
timestamp: DateTime.now(), timestamp: DateTime.now(),
apm: apm, apm: apm,
pps: pps, pps: pps,
@ -178,7 +182,7 @@ class CompareState extends State<CompareView> {
standing: -1, standing: -1,
standingLocal: -1, standingLocal: -1,
nextAt: -1, nextAt: -1,
prevAt: -1), TetrioZen(level: 0, score: 0))]; prevAt: -1, season: currentSeason), TetrioZen(level: 0, score: 0))];
return setState(() {}); return setState(() {});
} }
var player = await teto.fetchPlayer(user); var player = await teto.fetchPlayer(user);
@ -208,9 +212,11 @@ class CompareState extends State<CompareView> {
_justUpdate(); _justUpdate();
} }
void changeGreenSide(TetrioPlayer user) { void changeGreenSide(TetraLeague user) {
setState(() {theGreenSide[0] = user; setState(() {
theGreenSide[2].league = user.tlSeason1;}); //theGreenSide[0] = user;
theGreenSide[2].league = user;
});
} }
double getWinrateByTR(double yourGlicko, double yourRD, double notyourGlicko,double notyourRD) { double getWinrateByTR(double yourGlicko, double yourRD, double notyourGlicko,double notyourRD) {
@ -955,7 +961,7 @@ class CompareState extends State<CompareView> {
const Divider(), const Divider(),
Padding( Padding(
padding: const EdgeInsets.only(bottom: 16), padding: const EdgeInsets.only(bottom: 16),
child: Text("${t.quickPlay} ${t.expert} ${t.nerdStats}", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)), child: Text("${t.quickPlay} ${t.expert} ${t.nerdStats}", style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)),
), ),
CompareThingy( CompareThingy(
label: "APP", label: "APP",

View File

@ -216,15 +216,15 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
if (everyone != null && summaries.league.gamesPlayed > 9) rankAverages = everyone?.averages[summaries.league.percentileRank]?[0]; if (everyone != null && summaries.league.gamesPlayed > 9) rankAverages = everyone?.averages[summaries.league.percentileRank]?[0];
// Making list of Tetra League matches // Making list of Tetra League matches
//bool isTracking = await teto.isPlayerTracking(me.userId); bool isTracking = await teto.isPlayerTracking(me.userId);
List<TetrioPlayer> states = []; List<TetrioPlayer> states = [];
TetraLeague? compareWith; TetraLeague? compareWith;
Set<TetraLeague> uniqueTL = {}; Set<TetraLeague> uniqueTL = {};
List<TetraLeagueAlphaRecord> storedRecords = await teto.getTLMatchesbyPlayerID(me.userId); // get old matches List<TetraLeagueAlphaRecord> storedRecords = await teto.getTLMatchesbyPlayerID(me.userId); // get old matches
// if (isTracking){ // if tracked - save data to local DB if (isTracking){ // if tracked - save data to local DB
// await teto.storeState(me); await teto.storeState(summaries.league);
// //await teto.saveTLMatchesFromStream(tlStream); //await teto.saveTLMatchesFromStream(tlStream);
// } }
TetraLeagueAlphaStream? oldMatches; TetraLeagueAlphaStream? oldMatches;
// building list of TL matches // building list of TL matches
if(fetchTLmatches) { if(fetchTLmatches) {
@ -271,11 +271,11 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
} }
} }
states.addAll(await teto.getPlayer(me.userId)); //states.addAll(await teto.getPlayer(me.userId));
for (var element in states) { // For graphs I need only unique entries // for (var element in states) { // For graphs I need only unique entries
if (element.tlSeason1 != null && uniqueTL.isNotEmpty && uniqueTL.last != element.tlSeason1) uniqueTL.add(element.tlSeason1!); // if (element.tlSeason1 != null && uniqueTL.isNotEmpty && uniqueTL.last != element.tlSeason1) uniqueTL.add(element.tlSeason1!);
if (uniqueTL.isEmpty) uniqueTL.add(summaries.league); // if (uniqueTL.isEmpty) uniqueTL.add(summaries.league);
} // }
// Also i need previous Tetra League State for comparison if avaliable // Also i need previous Tetra League State for comparison if avaliable
if (uniqueTL.length >= 2){ if (uniqueTL.length >= 2){
compareWith = uniqueTL.toList().elementAtOrNull(uniqueTL.length - 2); compareWith = uniqueTL.toList().elementAtOrNull(uniqueTL.length - 2);

View File

@ -6,6 +6,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:syncfusion_flutter_charts/charts.dart'; import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:syncfusion_flutter_gauges/gauges.dart'; import 'package:syncfusion_flutter_gauges/gauges.dart';
import 'package:tetra_stats/data_objects/tetra_stats.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/services/crud_exceptions.dart'; import 'package:tetra_stats/services/crud_exceptions.dart';
import 'package:tetra_stats/utils/colors_functions.dart'; import 'package:tetra_stats/utils/colors_functions.dart';
@ -286,11 +287,11 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
} }
} }
states.addAll(await teto.getPlayer(widget.searchFor)); //states.addAll(await teto.getPlayer(widget.searchFor));
for (var element in states) { // for (var element in states) {
if (element.tlSeason1 != null && uniqueTL.isNotEmpty && uniqueTL.last != element.tlSeason1) uniqueTL.add(element.tlSeason1!); // if (element.tlSeason1 != null && uniqueTL.isNotEmpty && uniqueTL.last != element.tlSeason1) uniqueTL.add(element.tlSeason1!);
if (uniqueTL.isEmpty) uniqueTL.add(element.tlSeason1!); // if (uniqueTL.isEmpty) uniqueTL.add(element.tlSeason1!);
} // }
if (uniqueTL.length >= 2){ if (uniqueTL.length >= 2){
chartsData = <DropdownMenuItem<List<_HistoryChartSpot>>>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid chartsData = <DropdownMenuItem<List<_HistoryChartSpot>>>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid
@ -511,9 +512,10 @@ class FetchResults{
bool success; bool success;
TetrioPlayer? player; TetrioPlayer? player;
Summaries? summaries; Summaries? summaries;
Cutoffs? cutoffs;
Exception? exception; Exception? exception;
FetchResults(this.success, this.player, this.summaries, this.exception); FetchResults(this.success, this.player, this.summaries, this.cutoffs, this.exception);
} }
class RecordSummary extends StatelessWidget{ class RecordSummary extends StatelessWidget{
@ -613,10 +615,17 @@ class _DestinationHomeState extends State<DestinationHome> {
player = await teto.fetchPlayer(widget.searchFor); // Otherwise it's probably a user id or username player = await teto.fetchPlayer(widget.searchFor); // Otherwise it's probably a user id or username
} }
}on TetrioPlayerNotExist{ }on TetrioPlayerNotExist{
return FetchResults(false, null, null, TetrioPlayerNotExist()); return FetchResults(false, null, null, null, TetrioPlayerNotExist());
} }
Summaries summaries = await teto.fetchSummaries(player.userId); late Summaries summaries;
return FetchResults(true, player, summaries, null); late Cutoffs cutoffs;
List<dynamic> requests = await Future.wait([
teto.fetchSummaries(player.userId),
teto.fetchCutoffsBeanserver(),
]);
summaries = requests[0];
cutoffs = requests[1];
return FetchResults(true, player, summaries, cutoffs, null);
} }
Widget getOverviewCard(Summaries summaries){ Widget getOverviewCard(Summaries summaries){
@ -645,7 +654,7 @@ class _DestinationHomeState extends State<DestinationHome> {
children: [ children: [
const Text("Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, height: 0.9)), const Text("Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, height: 0.9)),
const Divider(color: Color.fromARGB(50, 158, 158, 158)), const Divider(color: Color.fromARGB(50, 158, 158, 158)),
TLRatingThingy(userID: "", tlData: summaries.league), TLRatingThingy(userID: "", tlData: summaries.league, showPositions: true),
const Divider(color: Color.fromARGB(50, 158, 158, 158)), const Divider(color: Color.fromARGB(50, 158, 158, 158)),
Text("${summaries.league.apm != null ? f2.format(summaries.league.apm) : "-.--"} APM • ${summaries.league.pps != null ? f2.format(summaries.league.pps) : "-.--"} PPS • ${summaries.league.vs != null ? f2.format(summaries.league.vs) : "-.--"} VS • ${summaries.league.nerdStats != null ? f2.format(summaries.league.nerdStats!.app) : "-.--"} APP • ${summaries.league.nerdStats != null ? f2.format(summaries.league.nerdStats!.vsapm) : "-.--"} VS/APM", style: const TextStyle(color: Colors.grey)) Text("${summaries.league.apm != null ? f2.format(summaries.league.apm) : "-.--"} APM • ${summaries.league.pps != null ? f2.format(summaries.league.pps) : "-.--"} PPS • ${summaries.league.vs != null ? f2.format(summaries.league.vs) : "-.--"} VS • ${summaries.league.nerdStats != null ? f2.format(summaries.league.nerdStats!.app) : "-.--"} APP • ${summaries.league.nerdStats != null ? f2.format(summaries.league.nerdStats!.vsapm) : "-.--"} VS/APM", style: const TextStyle(color: Colors.grey))
], ],
@ -827,7 +836,7 @@ class _DestinationHomeState extends State<DestinationHome> {
); );
} }
Widget getTetraLeagueCard(TetraLeague data){ Widget getTetraLeagueCard(TetraLeague data, Cutoffs? cutoffs){
return Column( return Column(
children: [ children: [
Card( Card(
@ -845,7 +854,7 @@ class _DestinationHomeState extends State<DestinationHome> {
), ),
), ),
), ),
TetraLeagueThingy(league: data), TetraLeagueThingy(league: data, cutoffs: cutoffs),
if (data.nerdStats != null) Card( if (data.nerdStats != null) Card(
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -1514,7 +1523,7 @@ class _DestinationHomeState extends State<DestinationHome> {
child: switch (rightCard){ child: switch (rightCard){
Cards.overview => getOverviewCard(snapshot.data!.summaries!), Cards.overview => getOverviewCard(snapshot.data!.summaries!),
Cards.tetraLeague => switch (cardMod){ Cards.tetraLeague => switch (cardMod){
CardMod.info => getTetraLeagueCard(snapshot.data!.summaries!.league), CardMod.info => getTetraLeagueCard(snapshot.data!.summaries!.league, snapshot.data!.cutoffs),
CardMod.records => getRecentTLrecords(widget.constraints), CardMod.records => getRecentTLrecords(widget.constraints),
_ => const Center(child: Text("huh?")) _ => const Center(child: Text("huh?"))
}, },
@ -2340,8 +2349,9 @@ class _SearchDrawerState extends State<SearchDrawer> {
class TetraLeagueThingy extends StatelessWidget{ class TetraLeagueThingy extends StatelessWidget{
final TetraLeague league; final TetraLeague league;
final Cutoffs? cutoffs;
const TetraLeagueThingy({super.key, required this.league}); const TetraLeagueThingy({super.key, required this.league, this.cutoffs});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -2349,7 +2359,15 @@ class TetraLeagueThingy extends StatelessWidget{
child: Column( child: Column(
children: [ children: [
TLRatingThingy(userID: "w", tlData: league), TLRatingThingy(userID: "w", tlData: league),
TLProgress(tlData: league,), TLProgress(
tlData: league,
previousRankTRcutoff: cutoffs != null ? cutoffs!.tr[league.rank != "z" ? league.rank : league.percentileRank] : null,
nextRankTRcutoff: cutoffs != null ? (league.rank != "z" ? league.rank == "x+" : league.percentileRank == "x+") ? 25000 : cutoffs!.tr[ranks.elementAtOrNull(ranks.indexOf(league.rank != "z" ? league.rank : league.percentileRank)+1)] : null,
nextRankTRcutoffTarget: league.rank != "z" ? rankTargets[league.rank] : null,
previousRankTRcutoffTarget: (league.rank != "z" && league.rank != "x+") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(league.rank)+1)] : null,
previousGlickoCutoff: cutoffs != null ? cutoffs!.glicko[league.rank != "z" ? league.rank : league.percentileRank] : null,
nextRankGlickoCutoff: cutoffs != null ? (league.rank != "z" ? league.rank == "x+" : league.percentileRank == "x+") ? 25000 : cutoffs!.glicko[ranks.elementAtOrNull(ranks.indexOf(league.rank != "z" ? league.rank : league.percentileRank)+1)] : null,
),
Row( Row(
// spacing: 25.0, // spacing: 25.0,
// alignment: WrapAlignment.spaceAround, // alignment: WrapAlignment.spaceAround,
@ -2809,9 +2827,10 @@ class TLRatingThingy extends StatelessWidget{
final TetraLeague tlData; final TetraLeague tlData;
final TetraLeague? oldTl; final TetraLeague? oldTl;
final double? topTR; final double? topTR;
final bool? showPositions;
final DateTime? lastMatchPlayed; final DateTime? lastMatchPlayed;
const TLRatingThingy({super.key, required this.userID, required this.tlData, this.oldTl, this.topTR, this.lastMatchPlayed}); const TLRatingThingy({super.key, required this.userID, required this.tlData, this.oldTl, this.topTR, this.lastMatchPlayed, this.showPositions});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -2893,7 +2912,7 @@ class TLRatingThingy extends StatelessWidget{
), ),
], ],
), ),
RichText( if (showPositions == true) RichText(
textAlign: TextAlign.start, textAlign: TextAlign.start,
text: TextSpan( text: TextSpan(
text: "", text: "",

View File

@ -523,7 +523,7 @@ class _ListEntry extends StatelessWidget {
children: [ children: [
Text(f.format(value), Text(f.format(value),
style: const TextStyle(fontSize: 22, height: 0.9)), style: const TextStyle(fontSize: 22, height: 0.9)),
if (id.isNotEmpty) Text(t.forPlayer(username: username), style: TextStyle(color: Colors.grey, fontWeight: FontWeight.w100),) if (id.isNotEmpty) Text(t.forPlayer(username: username), style: const TextStyle(color: Colors.grey, fontWeight: FontWeight.w100),)
], ],
), ),
onTap: id.isNotEmpty onTap: id.isNotEmpty

View File

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/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';
@ -58,6 +58,6 @@ class StateState extends State<StateView> {
headerSliverBuilder: (context, value) { headerSliverBuilder: (context, value) {
return [SliverToBoxAdapter(child: UserThingy(player: widget.state, showStateTimestamp: true, setState: _justUpdate))]; return [SliverToBoxAdapter(child: UserThingy(player: widget.state, showStateTimestamp: true, setState: _justUpdate))];
}, },
body: TLThingy(tl: widget.state.tlSeason1!, userID: widget.state.userId, states: const [])))); body: Container())));
} }
} }

View File

@ -61,14 +61,14 @@ class StatesState extends State<StatesView> {
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return ListTile(
title: Text(timestamp(widget.states[index].state)), title: Text(timestamp(widget.states[index].state)),
subtitle: Text(t.statesViewEntry(level: widget.states[index].level.toStringAsFixed(2), gameTime: widget.states[index].gameTime, friends: widget.states[index].friendCount, rd: NumberFormat.compact().format(widget.states[index].tlSeason1?.rd??0))), subtitle: Text(t.statesViewEntry(level: widget.states[index].level.toStringAsFixed(2), gameTime: widget.states[index].gameTime, friends: widget.states[index].friendCount, rd: 0)),
trailing: IconButton( trailing: IconButton(
icon: const Icon(Icons.delete_forever), icon: const Icon(Icons.delete_forever),
onPressed: () { onPressed: () {
DateTime nn = widget.states[index].state; DateTime nn = widget.states[index].state;
teto.deleteState(widget.states[index]).then((value) => setState(() { // teto.deleteState(widget.states[index]).then((value) => setState(() {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.stateRemoved(date: timestamp(nn))))); // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.stateRemoved(date: timestamp(nn)))));
})); // }));
}, },
), ),
onTap: () { onTap: () {

View File

@ -210,7 +210,7 @@ class TLLeaderboardState extends State<TLLeaderboardView> {
) )
); );
} }
return Text("end of FutureBuilder"); return const Text("end of FutureBuilder");
} }
})), })),
); );

View File

@ -51,7 +51,7 @@ class TLProgress extends StatelessWidget{
] ]
) )
), ),
Spacer(), const Spacer(),
RichText( RichText(
textAlign: TextAlign.right, textAlign: TextAlign.right,
text: TextSpan( text: TextSpan(

View File

@ -59,7 +59,7 @@ class TLRatingThingy extends StatelessWidget{
if (formatedTR.elementAtOrNull(1) != null) TextSpan(text: decimalSeparator + formatedTR[1]), if (formatedTR.elementAtOrNull(1) != null) TextSpan(text: decimalSeparator + formatedTR[1]),
TextSpan(text: " TR", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)) TextSpan(text: " TR", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28))
], ],
} : [TextSpan(text: "---\n", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28, color: Colors.grey)), TextSpan(text: t.gamesUntilRanked(left: 10-tlData.gamesPlayed), style: TextStyle(color: Colors.grey, fontSize: 14)),] } : [TextSpan(text: "---\n", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28, color: Colors.grey)), TextSpan(text: t.gamesUntilRanked(left: 10-tlData.gamesPlayed), style: const TextStyle(color: Colors.grey, fontSize: 14)),]
) )
), ),
if (oldTl != null) Text( if (oldTl != null) Text(

View File

@ -22,7 +22,7 @@ var intFDiff = NumberFormat("+#,###.000;-#,###.000");
class TLThingy extends StatefulWidget { class TLThingy extends StatefulWidget {
final TetraLeague tl; final TetraLeague tl;
final String userID; final String userID;
final List<TetrioPlayer> states; final List<TetraLeague> states;
final bool showTitle; final bool showTitle;
final bool bot; final bool bot;
final bool guest; final bool guest;
@ -47,13 +47,13 @@ class _TLThingyState extends State<TLThingy> with TickerProviderStateMixin {
late TetraLeague? oldTl; late TetraLeague? oldTl;
late TetraLeague currentTl; late TetraLeague currentTl;
late RangeValues _currentRangeValues; late RangeValues _currentRangeValues;
late List<TetrioPlayer> sortedStates; late List<TetraLeague> sortedStates;
@override @override
void initState() { void initState() {
_currentRangeValues = const RangeValues(0, 1); _currentRangeValues = const RangeValues(0, 1);
sortedStates = widget.states.reversed.toList(); sortedStates = widget.states.reversed.toList();
oldTl = sortedStates.elementAtOrNull(1)?.tlSeason1; oldTl = sortedStates.elementAtOrNull(1);
currentTl = widget.tl; currentTl = widget.tl;
super.initState(); super.initState();
} }
@ -95,12 +95,12 @@ class _TLThingyState extends State<TLThingy> with TickerProviderStateMixin {
if (values.start.round() == 0){ if (values.start.round() == 0){
currentTl = widget.tl; currentTl = widget.tl;
}else{ }else{
currentTl = sortedStates[values.start.round()-1].tlSeason1!; currentTl = sortedStates[values.start.round()-1]!;
} }
if (values.end.round() == 0){ if (values.end.round() == 0){
oldTl = widget.tl; oldTl = widget.tl;
}else{ }else{
oldTl = sortedStates[values.end.round()-1].tlSeason1; oldTl = sortedStates[values.end.round()-1];
} }
}); });
}, },

View File

@ -182,7 +182,6 @@ class UserThingy extends StatelessWidget {
],), ],),
onPressed: () { onPressed: () {
teto.addPlayerToTrack(player).then((value) => setState()); teto.addPlayerToTrack(player).then((value) => setState());
teto.storeState(player);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.becameTracked))); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.becameTracked)));
}, },
), ),
@ -213,7 +212,7 @@ class UserThingy extends StatelessWidget {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => CompareView(greenSide: [player, null, player.tlSeason1], redSide: const [null, null, null], greenMode: Mode.player, redMode: Mode.player), builder: (context) => CompareView(greenSide: [player, null, null], redSide: const [null, null, null], greenMode: Mode.player, redMode: Mode.player),
), ),
); );
}, },

View File

@ -148,7 +148,7 @@ class _ZenithThingyState extends State<ZenithThingy> {
const Positioned(left: 25, top: 20, child: Text("otal time", style: TextStyle(fontFamily: "Eurostile Round Extended"))), const Positioned(left: 25, top: 20, child: Text("otal time", style: TextStyle(fontFamily: "Eurostile Round Extended"))),
Padding( Padding(
padding: const EdgeInsets.only(left: 10.0), padding: const EdgeInsets.only(left: 10.0),
child: Text("${getMoreNormalTime(record!.stats.finalTime)}", style: TextStyle( child: Text(getMoreNormalTime(record!.stats.finalTime), style: const TextStyle(
shadows: textShadow, shadows: textShadow,
fontFamily: "Eurostile Round Extended", fontFamily: "Eurostile Round Extended",
fontSize: 36, fontSize: 36,