swamp water
This commit is contained in:
parent
8f5b7c018d
commit
b623693148
|
@ -60,7 +60,8 @@ const Map<String, double> rankTargets = {
|
||||||
"d+": 606,
|
"d+": 606,
|
||||||
"d": 0,
|
"d": 0,
|
||||||
};
|
};
|
||||||
DateTime seasonEnd = DateTime.utc(2024, 07, 26, 15);
|
DateTime seasonStart = DateTime.utc(2024, 08, 16, 18);
|
||||||
|
//DateTime seasonEnd = DateTime.utc(2024, 07, 26, 15);
|
||||||
enum Stats {
|
enum Stats {
|
||||||
tr,
|
tr,
|
||||||
glicko,
|
glicko,
|
||||||
|
@ -261,9 +262,7 @@ class TetrioPlayer {
|
||||||
bool? badstanding;
|
bool? badstanding;
|
||||||
String? botmaster;
|
String? botmaster;
|
||||||
Connections? connections;
|
Connections? connections;
|
||||||
late TetraLeagueAlpha tlSeason1;
|
TetraLeagueAlpha? tlSeason1;
|
||||||
List<RecordSingle?> sprint = [];
|
|
||||||
List<RecordSingle?> blitz = [];
|
|
||||||
TetrioZen? zen;
|
TetrioZen? zen;
|
||||||
Distinguishment? distinguishment;
|
Distinguishment? distinguishment;
|
||||||
DateTime? cachedUntil;
|
DateTime? cachedUntil;
|
||||||
|
@ -290,8 +289,6 @@ class TetrioPlayer {
|
||||||
this.botmaster,
|
this.botmaster,
|
||||||
required this.connections,
|
required this.connections,
|
||||||
required this.tlSeason1,
|
required this.tlSeason1,
|
||||||
required this.sprint,
|
|
||||||
required this.blitz,
|
|
||||||
this.zen,
|
this.zen,
|
||||||
this.distinguishment,
|
this.distinguishment,
|
||||||
this.cachedUntil
|
this.cachedUntil
|
||||||
|
@ -318,7 +315,7 @@ 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 = TetraLeagueAlpha.fromJson(json['league'], stateTime);
|
tlSeason1 = json['league'] != null ? TetraLeagueAlpha.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'];
|
||||||
|
@ -344,7 +341,7 @@ 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();
|
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;
|
||||||
|
@ -380,12 +377,12 @@ class TetrioPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkForRetrivedHistory(covariant TetrioPlayer other) {
|
bool checkForRetrivedHistory(covariant TetrioPlayer other) {
|
||||||
return tlSeason1.lessStrictCheck(other.tlSeason1);
|
return tlSeason1!.lessStrictCheck(other.tlSeason1!);
|
||||||
}
|
}
|
||||||
|
|
||||||
TetrioPlayerFromLeaderboard convertToPlayerFromLeaderboard() => TetrioPlayerFromLeaderboard(
|
TetrioPlayerFromLeaderboard convertToPlayerFromLeaderboard() => TetrioPlayerFromLeaderboard(
|
||||||
userId, username, role, xp, country, supporterTier > 0, verified, state, gamesPlayed, gamesWon,
|
userId, username, role, xp, country, verified, state, gamesPlayed, gamesWon,
|
||||||
tlSeason1.rating, tlSeason1.glicko??0, tlSeason1.rd??noTrRd, tlSeason1.rank, tlSeason1.bestRank, tlSeason1.apm??0, tlSeason1.pps??0, tlSeason1.vs??0, tlSeason1.decaying);
|
tlSeason1!.rating, tlSeason1!.glicko??0, tlSeason1!.rd??noTrRd, tlSeason1!.rank, tlSeason1!.bestRank, tlSeason1!.apm??0, tlSeason1!.pps??0, tlSeason1!.vs??0, tlSeason1!.decaying);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
@ -395,59 +392,59 @@ class TetrioPlayer {
|
||||||
num? getStatByEnum(Stats stat){
|
num? getStatByEnum(Stats stat){
|
||||||
switch (stat) {
|
switch (stat) {
|
||||||
case Stats.tr:
|
case Stats.tr:
|
||||||
return tlSeason1.rating;
|
return tlSeason1?.rating;
|
||||||
case Stats.glicko:
|
case Stats.glicko:
|
||||||
return tlSeason1.glicko;
|
return tlSeason1?.glicko;
|
||||||
case Stats.rd:
|
case Stats.rd:
|
||||||
return tlSeason1.rd;
|
return tlSeason1?.rd;
|
||||||
case Stats.gp:
|
case Stats.gp:
|
||||||
return tlSeason1.gamesPlayed;
|
return tlSeason1?.gamesPlayed;
|
||||||
case Stats.gw:
|
case Stats.gw:
|
||||||
return tlSeason1.gamesWon;
|
return tlSeason1?.gamesWon;
|
||||||
case Stats.wr:
|
case Stats.wr:
|
||||||
return tlSeason1.winrate;
|
return tlSeason1?.winrate;
|
||||||
case Stats.apm:
|
case Stats.apm:
|
||||||
return tlSeason1.apm;
|
return tlSeason1?.apm;
|
||||||
case Stats.pps:
|
case Stats.pps:
|
||||||
return tlSeason1.pps;
|
return tlSeason1?.pps;
|
||||||
case Stats.vs:
|
case Stats.vs:
|
||||||
return tlSeason1.vs;
|
return tlSeason1?.vs;
|
||||||
case Stats.app:
|
case Stats.app:
|
||||||
return tlSeason1.nerdStats?.app;
|
return tlSeason1?.nerdStats?.app;
|
||||||
case Stats.dss:
|
case Stats.dss:
|
||||||
return tlSeason1.nerdStats?.dss;
|
return tlSeason1?.nerdStats?.dss;
|
||||||
case Stats.dsp:
|
case Stats.dsp:
|
||||||
return tlSeason1.nerdStats?.dsp;
|
return tlSeason1?.nerdStats?.dsp;
|
||||||
case Stats.appdsp:
|
case Stats.appdsp:
|
||||||
return tlSeason1.nerdStats?.appdsp;
|
return tlSeason1?.nerdStats?.appdsp;
|
||||||
case Stats.vsapm:
|
case Stats.vsapm:
|
||||||
return tlSeason1.nerdStats?.vsapm;
|
return tlSeason1?.nerdStats?.vsapm;
|
||||||
case Stats.cheese:
|
case Stats.cheese:
|
||||||
return tlSeason1.nerdStats?.cheese;
|
return tlSeason1?.nerdStats?.cheese;
|
||||||
case Stats.gbe:
|
case Stats.gbe:
|
||||||
return tlSeason1.nerdStats?.gbe;
|
return tlSeason1?.nerdStats?.gbe;
|
||||||
case Stats.nyaapp:
|
case Stats.nyaapp:
|
||||||
return tlSeason1.nerdStats?.nyaapp;
|
return tlSeason1?.nerdStats?.nyaapp;
|
||||||
case Stats.area:
|
case Stats.area:
|
||||||
return tlSeason1.nerdStats?.area;
|
return tlSeason1?.nerdStats?.area;
|
||||||
case Stats.eTR:
|
case Stats.eTR:
|
||||||
return tlSeason1.estTr?.esttr;
|
return tlSeason1?.estTr?.esttr;
|
||||||
case Stats.acceTR:
|
case Stats.acceTR:
|
||||||
return tlSeason1.esttracc;
|
return tlSeason1?.esttracc;
|
||||||
case Stats.acceTRabs:
|
case Stats.acceTRabs:
|
||||||
return tlSeason1.esttracc?.abs();
|
return tlSeason1?.esttracc?.abs();
|
||||||
case Stats.opener:
|
case Stats.opener:
|
||||||
return tlSeason1.playstyle?.opener;
|
return tlSeason1?.playstyle?.opener;
|
||||||
case Stats.plonk:
|
case Stats.plonk:
|
||||||
return tlSeason1.playstyle?.plonk;
|
return tlSeason1?.playstyle?.plonk;
|
||||||
case Stats.infDS:
|
case Stats.infDS:
|
||||||
return tlSeason1.playstyle?.infds;
|
return tlSeason1?.playstyle?.infds;
|
||||||
case Stats.stride:
|
case Stats.stride:
|
||||||
return tlSeason1.playstyle?.stride;
|
return tlSeason1?.playstyle?.stride;
|
||||||
case Stats.stridemMinusPlonk:
|
case Stats.stridemMinusPlonk:
|
||||||
return tlSeason1.playstyle != null ? tlSeason1.playstyle!.stride - tlSeason1.playstyle!.plonk : null;
|
return tlSeason1?.playstyle != null ? tlSeason1!.playstyle!.stride - tlSeason1!.playstyle!.plonk : null;
|
||||||
case Stats.openerMinusInfDS:
|
case Stats.openerMinusInfDS:
|
||||||
return tlSeason1.playstyle != null ? tlSeason1.playstyle!.opener - tlSeason1.playstyle!.infds : null;
|
return tlSeason1?.playstyle != null ? tlSeason1!.playstyle!.opener - tlSeason1!.playstyle!.infds : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,6 +455,24 @@ class TetrioPlayer {
|
||||||
bool operator ==(covariant TetrioPlayer other) => isSameState(other) && state.isAtSameMomentAs(other.state);
|
bool operator ==(covariant TetrioPlayer other) => isSameState(other) && state.isAtSameMomentAs(other.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Summaries{
|
||||||
|
late String id;
|
||||||
|
late RecordSingle sprint;
|
||||||
|
late RecordSingle blitz;
|
||||||
|
late TetraLeagueAlpha league;
|
||||||
|
late TetrioZen zen;
|
||||||
|
|
||||||
|
Summaries(this.id, this.league, this.zen);
|
||||||
|
|
||||||
|
Summaries.fromJson(Map<String, dynamic> json, String i){
|
||||||
|
id = i;
|
||||||
|
sprint = RecordSingle.fromJson(json['40l']['record'], json['40l']['rank']);
|
||||||
|
blitz = RecordSingle.fromJson(json['blitz']['record'], json['blitz']['rank']);
|
||||||
|
league = TetraLeagueAlpha.fromJson(json['league'], DateTime.now());
|
||||||
|
zen = TetrioZen.fromJson(json['zen']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Badge {
|
class Badge {
|
||||||
late String badgeId;
|
late String badgeId;
|
||||||
late String label;
|
late String label;
|
||||||
|
@ -652,8 +667,7 @@ class Finesse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EndContextSingle {
|
class ResultsStats {
|
||||||
late String gameType;
|
|
||||||
late int topBtB;
|
late int topBtB;
|
||||||
late int topCombo;
|
late int topCombo;
|
||||||
late int holds;
|
late int holds;
|
||||||
|
@ -662,7 +676,7 @@ class EndContextSingle {
|
||||||
late int piecesPlaced;
|
late int piecesPlaced;
|
||||||
late int lines;
|
late int lines;
|
||||||
late int score;
|
late int score;
|
||||||
late double seed;
|
late int seed;
|
||||||
late Duration finalTime;
|
late Duration finalTime;
|
||||||
late int tSpins;
|
late int tSpins;
|
||||||
late Clears clears;
|
late Clears clears;
|
||||||
|
@ -674,8 +688,8 @@ class EndContextSingle {
|
||||||
double get kps => inputs / (finalTime.inMicroseconds / 1000000);
|
double get kps => inputs / (finalTime.inMicroseconds / 1000000);
|
||||||
double get finessePercentage => finesse != null ? finesse!.perfectPieces / piecesPlaced : 0;
|
double get finessePercentage => finesse != null ? finesse!.perfectPieces / piecesPlaced : 0;
|
||||||
|
|
||||||
EndContextSingle(
|
ResultsStats(
|
||||||
{required this.gameType,
|
{
|
||||||
required this.topBtB,
|
required this.topBtB,
|
||||||
required this.topCombo,
|
required this.topCombo,
|
||||||
required this.holds,
|
required this.holds,
|
||||||
|
@ -690,12 +704,12 @@ class EndContextSingle {
|
||||||
required this.clears,
|
required this.clears,
|
||||||
required this.finesse});
|
required this.finesse});
|
||||||
|
|
||||||
EndContextSingle.fromJson(Map<String, dynamic> json) {
|
ResultsStats.fromJson(Map<String, dynamic> json) {
|
||||||
seed = json['seed'].toDouble();
|
seed = json['seed'];
|
||||||
lines = json['lines'];
|
lines = json['lines'];
|
||||||
inputs = json['inputs'];
|
inputs = json['inputs'];
|
||||||
holds = json['holds'] ?? 0;
|
holds = json['holds'] ?? 0;
|
||||||
finalTime = doubleMillisecondsToDuration(json['finalTime'].toDouble());
|
finalTime = doubleMillisecondsToDuration(json['finaltime'].toDouble());
|
||||||
score = json['score'];
|
score = json['score'];
|
||||||
level = json['level'];
|
level = json['level'];
|
||||||
topCombo = json['topcombo'];
|
topCombo = json['topcombo'];
|
||||||
|
@ -704,7 +718,6 @@ class EndContextSingle {
|
||||||
piecesPlaced = json['piecesplaced'];
|
piecesPlaced = json['piecesplaced'];
|
||||||
clears = Clears.fromJson(json['clears']);
|
clears = Clears.fromJson(json['clears']);
|
||||||
finesse = json.containsKey("finesse") ? Finesse.fromJson(json['finesse']) : null;
|
finesse = json.containsKey("finesse") ? Finesse.fromJson(json['finesse']) : null;
|
||||||
gameType = json['gametype'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
@ -722,7 +735,6 @@ class EndContextSingle {
|
||||||
data['clears'] = clears.toJson();
|
data['clears'] = clears.toJson();
|
||||||
if (finesse != null) data['finesse'] = finesse!.toJson();
|
if (finesse != null) data['finesse'] = finesse!.toJson();
|
||||||
data['finalTime'] = finalTime;
|
data['finalTime'] = finalTime;
|
||||||
data['gametype'] = gameType;
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -873,6 +885,119 @@ class TetraLeagueAlphaStream{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TetraLeagueBetaStream{
|
||||||
|
late String id;
|
||||||
|
List<BetaRecord> records = [];
|
||||||
|
|
||||||
|
TetraLeagueBetaStream({required this.id, required this.records});
|
||||||
|
|
||||||
|
TetraLeagueBetaStream.fromJson(List<dynamic> json, String userID) {
|
||||||
|
id = userID;
|
||||||
|
for (var entry in json) records.add(BetaRecord.fromJson(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
addFromAlphaStream(TetraLeagueAlphaStream oldStream){
|
||||||
|
for (var entry in oldStream.records) {
|
||||||
|
records.add(
|
||||||
|
BetaRecord(
|
||||||
|
id: entry.ownId,
|
||||||
|
replayID: entry.replayId,
|
||||||
|
ts: entry.timestamp,
|
||||||
|
enemyID: entry.endContext[1].userId,
|
||||||
|
enemyUsername: entry.endContext[1].username,
|
||||||
|
gamemode: "oldleague",
|
||||||
|
results: BetaLeagueResults(
|
||||||
|
leaderboard: [
|
||||||
|
BetaLeagueLeaderboardEntry(
|
||||||
|
id: entry.endContext[0].userId,
|
||||||
|
username: entry.endContext[0].username,
|
||||||
|
naturalorder: entry.endContext[0].naturalOrder,
|
||||||
|
wins: entry.endContext[0].points,
|
||||||
|
stats: BetaLeagueStats(
|
||||||
|
apm: entry.endContext[0].secondary,
|
||||||
|
pps: entry.endContext[0].tertiary,
|
||||||
|
vs: entry.endContext[0].extra,
|
||||||
|
garbageSent: -1,
|
||||||
|
garbageReceived: -1,
|
||||||
|
kills: entry.endContext[0].points,
|
||||||
|
altitude: 0.0,
|
||||||
|
rank: -1,
|
||||||
|
nerdStats: entry.endContext[0].nerdStats,
|
||||||
|
playstyle: entry.endContext[0].playstyle,
|
||||||
|
estTr: entry.endContext[0].estTr,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
BetaLeagueLeaderboardEntry(
|
||||||
|
id: entry.endContext[1].userId,
|
||||||
|
username: entry.endContext[1].username,
|
||||||
|
naturalorder: entry.endContext[1].naturalOrder,
|
||||||
|
wins: entry.endContext[1].points,
|
||||||
|
stats: BetaLeagueStats(
|
||||||
|
apm: entry.endContext[1].secondary,
|
||||||
|
pps: entry.endContext[1].tertiary,
|
||||||
|
vs: entry.endContext[1].extra,
|
||||||
|
garbageSent: -1,
|
||||||
|
garbageReceived: -1,
|
||||||
|
kills: entry.endContext[1].points,
|
||||||
|
altitude: 0.0,
|
||||||
|
rank: -1,
|
||||||
|
nerdStats: entry.endContext[1].nerdStats,
|
||||||
|
playstyle: entry.endContext[1].playstyle,
|
||||||
|
estTr: entry.endContext[1].estTr,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
rounds: [
|
||||||
|
for (int i=0; i<entry.endContext[0].secondaryTracking.length; i++)
|
||||||
|
[BetaLeagueRound(
|
||||||
|
id: entry.endContext[0].userId,
|
||||||
|
username: entry.endContext[0].username,
|
||||||
|
naturalorder: entry.endContext[0].naturalOrder,
|
||||||
|
active: false,
|
||||||
|
alive: false,
|
||||||
|
lifetime: Duration(milliseconds: -1),
|
||||||
|
stats: BetaLeagueStats(
|
||||||
|
apm: entry.endContext[0].secondaryTracking[i],
|
||||||
|
pps: entry.endContext[0].tertiaryTracking[i],
|
||||||
|
vs: entry.endContext[0].extraTracking[i],
|
||||||
|
garbageSent: -1,
|
||||||
|
garbageReceived: -1,
|
||||||
|
kills: 0,
|
||||||
|
altitude: 0.0,
|
||||||
|
rank: -1,
|
||||||
|
nerdStats: entry.endContext[0].nerdStatsTracking[i],
|
||||||
|
playstyle: entry.endContext[0].playstyleTracking[i],
|
||||||
|
estTr: entry.endContext[0].estTrTracking[i],
|
||||||
|
)
|
||||||
|
),BetaLeagueRound(
|
||||||
|
id: entry.endContext[1].userId,
|
||||||
|
username: entry.endContext[1].username,
|
||||||
|
naturalorder: entry.endContext[1].naturalOrder,
|
||||||
|
active: false,
|
||||||
|
alive: false,
|
||||||
|
lifetime: Duration(milliseconds: -1),
|
||||||
|
stats: BetaLeagueStats(
|
||||||
|
apm: entry.endContext[1].secondaryTracking[i],
|
||||||
|
pps: entry.endContext[1].tertiaryTracking[i],
|
||||||
|
vs: entry.endContext[1].extraTracking[i],
|
||||||
|
garbageSent: -1,
|
||||||
|
garbageReceived: -1,
|
||||||
|
kills: 0,
|
||||||
|
altitude: 0.0,
|
||||||
|
rank: -1,
|
||||||
|
nerdStats: entry.endContext[1].nerdStatsTracking[i],
|
||||||
|
playstyle: entry.endContext[1].playstyleTracking[i],
|
||||||
|
estTr: entry.endContext[1].estTrTracking[i],
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SingleplayerStream{
|
class SingleplayerStream{
|
||||||
late String userId;
|
late String userId;
|
||||||
late String type;
|
late String type;
|
||||||
|
@ -888,6 +1013,118 @@ class SingleplayerStream{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BetaRecord{
|
||||||
|
late String id;
|
||||||
|
late String replayID;
|
||||||
|
late String gamemode;
|
||||||
|
late DateTime ts;
|
||||||
|
late String enemyUsername;
|
||||||
|
late String enemyID;
|
||||||
|
late BetaLeagueResults results;
|
||||||
|
|
||||||
|
BetaRecord({required this.id, required this.replayID, required this.gamemode, required this.ts, required this.enemyUsername, required this.enemyID, required this.results});
|
||||||
|
|
||||||
|
BetaRecord.fromJson(Map<String, dynamic> json){
|
||||||
|
id = json['_id'];
|
||||||
|
replayID = json['replayid'];
|
||||||
|
gamemode = json['gamemode'];
|
||||||
|
ts = DateTime.parse(json['ts']);
|
||||||
|
enemyUsername = json['otherusers'][0]['username'];
|
||||||
|
enemyID = json['otherusers'][0]['id'];
|
||||||
|
results = BetaLeagueResults.fromJson(json['results']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BetaLeagueResults{
|
||||||
|
List<BetaLeagueLeaderboardEntry> leaderboard = [];
|
||||||
|
List<List<BetaLeagueRound>> rounds = [];
|
||||||
|
|
||||||
|
BetaLeagueResults({required this.leaderboard, required this.rounds});
|
||||||
|
|
||||||
|
BetaLeagueResults.fromJson(Map<String, dynamic> json){
|
||||||
|
for (var lbEntry in json['leaderboard']) leaderboard.add(BetaLeagueLeaderboardEntry.fromJson(lbEntry));
|
||||||
|
for (var roundEntry in json['rounds']){
|
||||||
|
List<BetaLeagueRound> round = [];
|
||||||
|
for (var r in roundEntry) round.add(BetaLeagueRound.fromJson(r));
|
||||||
|
rounds.add(round);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BetaLeagueLeaderboardEntry{
|
||||||
|
late String id;
|
||||||
|
late String username;
|
||||||
|
late int naturalorder;
|
||||||
|
late int wins;
|
||||||
|
late BetaLeagueStats stats;
|
||||||
|
|
||||||
|
BetaLeagueLeaderboardEntry({required this.id, required this.username, required this.naturalorder, required this.wins, required this.stats});
|
||||||
|
|
||||||
|
BetaLeagueLeaderboardEntry.fromJson(Map<String, dynamic> json){
|
||||||
|
id = json['id'];
|
||||||
|
username = json['username'];
|
||||||
|
naturalorder = json['naturalorder'];
|
||||||
|
wins = json['wins'];
|
||||||
|
stats = BetaLeagueStats.fromJson(json['stats']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BetaLeagueStats{
|
||||||
|
late double apm;
|
||||||
|
late double pps;
|
||||||
|
late double vs;
|
||||||
|
late int garbageSent;
|
||||||
|
late int garbageReceived;
|
||||||
|
late int kills;
|
||||||
|
late double altitude;
|
||||||
|
late int rank;
|
||||||
|
int? targetingFactor;
|
||||||
|
int? targetingRace;
|
||||||
|
late NerdStats nerdStats;
|
||||||
|
late EstTr estTr;
|
||||||
|
late Playstyle playstyle;
|
||||||
|
|
||||||
|
BetaLeagueStats({required this.apm, required this.pps, required this.vs, required this.garbageSent, required this.garbageReceived, required this.kills, required this.altitude, required this.rank, required this.nerdStats, required this.estTr, required this.playstyle});
|
||||||
|
|
||||||
|
BetaLeagueStats.fromJson(Map<String, dynamic> json){
|
||||||
|
apm = json['apm'].toDouble();
|
||||||
|
pps = json['pps'].toDouble();
|
||||||
|
vs = json['vsscore'].toDouble();
|
||||||
|
garbageSent = json['garbagesent'];
|
||||||
|
garbageReceived = json['garbagereceived'];
|
||||||
|
kills = json['kills'];
|
||||||
|
altitude = json['altitude'].toDouble();
|
||||||
|
rank = json['rank'];
|
||||||
|
targetingFactor = json['targetingfactor'];
|
||||||
|
targetingRace = json['targetinggrace'];
|
||||||
|
nerdStats = NerdStats(apm, pps, vs);
|
||||||
|
estTr = EstTr(apm, pps, vs, nerdStats.app, nerdStats.dss, nerdStats.dsp, nerdStats.gbe);
|
||||||
|
playstyle = Playstyle(apm, pps, nerdStats.app, nerdStats.vsapm, nerdStats.dsp, nerdStats.gbe, estTr.srarea, estTr.statrank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BetaLeagueRound{
|
||||||
|
late String id;
|
||||||
|
late String username;
|
||||||
|
late bool active;
|
||||||
|
late int naturalorder;
|
||||||
|
late bool alive;
|
||||||
|
late Duration lifetime;
|
||||||
|
late BetaLeagueStats stats;
|
||||||
|
|
||||||
|
BetaLeagueRound({required this.id, required this.username, required this.active, required this.naturalorder, required this.alive, required this.lifetime, required this.stats});
|
||||||
|
|
||||||
|
BetaLeagueRound.fromJson(Map<String, dynamic> json){
|
||||||
|
id = json['id'];
|
||||||
|
username = json['username'];
|
||||||
|
active = json['active'];
|
||||||
|
naturalorder = json['naturalorder'];
|
||||||
|
alive = json['alive'];
|
||||||
|
lifetime = Duration(milliseconds: json['lifetime']);
|
||||||
|
stats = BetaLeagueStats.fromJson(json['stats']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TetraLeagueAlphaRecord{
|
class TetraLeagueAlphaRecord{
|
||||||
late String replayId;
|
late String replayId;
|
||||||
late String ownId;
|
late String ownId;
|
||||||
|
@ -1132,26 +1369,28 @@ class RecordSingle {
|
||||||
late String userId;
|
late String userId;
|
||||||
late String replayId;
|
late String replayId;
|
||||||
late String ownId;
|
late String ownId;
|
||||||
|
late String gamemode;
|
||||||
late DateTime timestamp;
|
late DateTime timestamp;
|
||||||
late EndContextSingle endContext;
|
late ResultsStats stats;
|
||||||
int? rank;
|
int? rank;
|
||||||
|
|
||||||
RecordSingle({required this.userId, required this.replayId, required this.ownId, required this.timestamp, required this.endContext, this.rank});
|
RecordSingle({required this.userId, required this.replayId, required this.ownId, required this.timestamp, required this.stats, this.rank});
|
||||||
|
|
||||||
RecordSingle.fromJson(Map<String, dynamic> json, int? ran) {
|
RecordSingle.fromJson(Map<String, dynamic> json, int? ran) {
|
||||||
//developer.log("RecordSingle.fromJson: $json", name: "data_objects/tetrio");
|
//developer.log("RecordSingle.fromJson: $json", name: "data_objects/tetrio");
|
||||||
ownId = json['_id'];
|
ownId = json['_id'];
|
||||||
endContext = EndContextSingle.fromJson(json['endcontext']);
|
gamemode = json['gamemode'];
|
||||||
|
stats = ResultsStats.fromJson(json['results']['stats']);
|
||||||
replayId = json['replayid'];
|
replayId = json['replayid'];
|
||||||
timestamp = DateTime.parse(json['ts']);
|
timestamp = DateTime.parse(json['ts']);
|
||||||
userId = json['user']['_id'];
|
userId = json['user']['id'];
|
||||||
rank = ran;
|
rank = ran;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final Map<String, dynamic> data = <String, dynamic>{};
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
data['_id'] = ownId;
|
data['_id'] = ownId;
|
||||||
data['endcontext'] = endContext.toJson();
|
data['results']['stats'] = stats.toJson();
|
||||||
data['ismulti'] = false;
|
data['ismulti'] = false;
|
||||||
data['replayid'] = replayId;
|
data['replayid'] = replayId;
|
||||||
data['ts'] = timestamp;
|
data['ts'] = timestamp;
|
||||||
|
@ -1470,8 +1709,8 @@ class TetrioPlayersLeaderboard {
|
||||||
avgPPS += entry.pps;
|
avgPPS += entry.pps;
|
||||||
avgVS += entry.vs;
|
avgVS += entry.vs;
|
||||||
avgTR += entry.rating;
|
avgTR += entry.rating;
|
||||||
avgGlicko += entry.glicko;
|
if (entry.glicko != null) avgGlicko += entry.glicko!;
|
||||||
avgRD += entry.rd;
|
if (entry.rd != null) avgRD += entry.rd!;
|
||||||
avgAPP += entry.nerdStats.app;
|
avgAPP += entry.nerdStats.app;
|
||||||
avgVSAPM += entry.nerdStats.vsapm;
|
avgVSAPM += entry.nerdStats.vsapm;
|
||||||
avgDSS += entry.nerdStats.dss;
|
avgDSS += entry.nerdStats.dss;
|
||||||
|
@ -1494,13 +1733,13 @@ class TetrioPlayersLeaderboard {
|
||||||
lowestTRid = entry.userId;
|
lowestTRid = entry.userId;
|
||||||
lowestTRnick = entry.username;
|
lowestTRnick = entry.username;
|
||||||
}
|
}
|
||||||
if (entry.glicko < lowestGlicko){
|
if (entry.glicko != null && entry.glicko! < lowestGlicko){
|
||||||
lowestGlicko = entry.glicko;
|
lowestGlicko = entry.glicko!;
|
||||||
lowestGlickoID = entry.userId;
|
lowestGlickoID = entry.userId;
|
||||||
lowestGlickoNick = entry.username;
|
lowestGlickoNick = entry.username;
|
||||||
}
|
}
|
||||||
if (entry.rd < lowestRD){
|
if (entry.rd != null && entry.rd! < lowestRD){
|
||||||
lowestRD = entry.rd;
|
lowestRD = entry.rd!;
|
||||||
lowestRdID = entry.userId;
|
lowestRdID = entry.userId;
|
||||||
lowestRdNick = entry.username;
|
lowestRdNick = entry.username;
|
||||||
}
|
}
|
||||||
|
@ -1614,13 +1853,13 @@ class TetrioPlayersLeaderboard {
|
||||||
highestTRid = entry.userId;
|
highestTRid = entry.userId;
|
||||||
highestTRnick = entry.username;
|
highestTRnick = entry.username;
|
||||||
}
|
}
|
||||||
if (entry.glicko > highestGlicko){
|
if (entry.glicko != null && entry.glicko! > highestGlicko){
|
||||||
highestGlicko = entry.glicko;
|
highestGlicko = entry.glicko!;
|
||||||
highestGlickoID = entry.userId;
|
highestGlickoID = entry.userId;
|
||||||
highestGlickoNick = entry.username;
|
highestGlickoNick = entry.username;
|
||||||
}
|
}
|
||||||
if (entry.rd > highestRD){
|
if (entry.rd != null && entry.rd! > highestRD){
|
||||||
highestRD = entry.rd;
|
highestRD = entry.rd!;
|
||||||
highestRdID = entry.userId;
|
highestRdID = entry.userId;
|
||||||
highestRdNick = entry.username;
|
highestRdNick = entry.username;
|
||||||
}
|
}
|
||||||
|
@ -1929,7 +2168,7 @@ class TetrioPlayersLeaderboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerLeaderboardPosition? getLeaderboardPosition(TetrioPlayer user) {
|
PlayerLeaderboardPosition? getLeaderboardPosition(TetrioPlayer user) {
|
||||||
if (user.tlSeason1.gamesPlayed == 0) return null;
|
if (user.tlSeason1?.gamesPlayed == 0) return null;
|
||||||
bool fakePositions = false;
|
bool fakePositions = false;
|
||||||
late List<TetrioPlayerFromLeaderboard> copyOfLeaderboard;
|
late List<TetrioPlayerFromLeaderboard> copyOfLeaderboard;
|
||||||
if (leaderboard.indexWhere((element) => element.userId == user.userId) == -1){
|
if (leaderboard.indexWhere((element) => element.userId == user.userId) == -1){
|
||||||
|
@ -2029,16 +2268,15 @@ class TetrioPlayerFromLeaderboard {
|
||||||
late String role;
|
late String role;
|
||||||
late double xp;
|
late double xp;
|
||||||
String? country;
|
String? country;
|
||||||
late bool supporter;
|
|
||||||
late bool verified;
|
late bool verified;
|
||||||
late DateTime timestamp;
|
late DateTime timestamp;
|
||||||
late int gamesPlayed;
|
late int gamesPlayed;
|
||||||
late int gamesWon;
|
late int gamesWon;
|
||||||
late double rating;
|
late double rating;
|
||||||
late double glicko;
|
late double? glicko;
|
||||||
late double rd;
|
late double? rd;
|
||||||
late String rank;
|
late String rank;
|
||||||
late String bestRank;
|
late String? bestRank;
|
||||||
late double apm;
|
late double apm;
|
||||||
late double pps;
|
late double pps;
|
||||||
late double vs;
|
late double vs;
|
||||||
|
@ -2053,7 +2291,6 @@ class TetrioPlayerFromLeaderboard {
|
||||||
this.role,
|
this.role,
|
||||||
this.xp,
|
this.xp,
|
||||||
this.country,
|
this.country,
|
||||||
this.supporter,
|
|
||||||
this.verified,
|
this.verified,
|
||||||
this.timestamp,
|
this.timestamp,
|
||||||
this.gamesPlayed,
|
this.gamesPlayed,
|
||||||
|
@ -2081,14 +2318,13 @@ class TetrioPlayerFromLeaderboard {
|
||||||
role = json['role'];
|
role = json['role'];
|
||||||
xp = json['xp'].toDouble();
|
xp = json['xp'].toDouble();
|
||||||
country = json['country'];
|
country = json['country'];
|
||||||
supporter = json['supporter'];
|
|
||||||
verified = json['verified'];
|
verified = json['verified'];
|
||||||
timestamp = ts;
|
timestamp = ts;
|
||||||
gamesPlayed = json['league']['gamesplayed'];
|
gamesPlayed = json['league']['gamesplayed'] as int;
|
||||||
gamesWon = json['league']['gameswon'];
|
gamesWon = json['league']['gameswon'] as int;
|
||||||
rating = json['league']['rating'].toDouble();
|
rating = json['league']['rating'] != null ? json['league']['rating'].toDouble() : 0;
|
||||||
glicko = json['league']['glicko'].toDouble();
|
glicko = json['league']['glicko'] != null ? json['league']['glicko'].toDouble() : null;
|
||||||
rd = json['league']['rd'].toDouble();
|
rd = json['league']['rd'] != null ? json['league']['rd'].toDouble() : null;
|
||||||
rank = json['league']['rank'];
|
rank = json['league']['rank'];
|
||||||
bestRank = json['league']['bestrank'];
|
bestRank = json['league']['bestrank'];
|
||||||
apm = json['league']['apm'] != null ? json['league']['apm'].toDouble() : 0.00;
|
apm = json['league']['apm'] != null ? json['league']['apm'].toDouble() : 0.00;
|
||||||
|
@ -2105,9 +2341,9 @@ class TetrioPlayerFromLeaderboard {
|
||||||
case Stats.tr:
|
case Stats.tr:
|
||||||
return rating;
|
return rating;
|
||||||
case Stats.glicko:
|
case Stats.glicko:
|
||||||
return glicko;
|
return glicko??-1;
|
||||||
case Stats.rd:
|
case Stats.rd:
|
||||||
return rd;
|
return rd??-1;
|
||||||
case Stats.gp:
|
case Stats.gp:
|
||||||
return gamesPlayed;
|
return gamesPlayed;
|
||||||
case Stats.gw:
|
case Stats.gw:
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
/// To regenerate, run: `dart run slang`
|
/// To regenerate, run: `dart run slang`
|
||||||
///
|
///
|
||||||
/// Locales: 2
|
/// Locales: 2
|
||||||
/// Strings: 1186 (593 per locale)
|
/// Strings: 1198 (599 per locale)
|
||||||
///
|
///
|
||||||
/// Built on 2024-07-20 at 13:24 UTC
|
/// Built on 2024-07-27 at 18:54 UTC
|
||||||
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
|
@ -222,6 +222,12 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
|
||||||
String get verdictBetter => 'better';
|
String get verdictBetter => 'better';
|
||||||
String get verdictWorse => 'worse';
|
String get verdictWorse => 'worse';
|
||||||
String get smooth => 'Smooth';
|
String get smooth => 'Smooth';
|
||||||
|
String get postSeason => 'Off-season';
|
||||||
|
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 against absolutely random player.\nSeason ${n} rules applied';
|
||||||
|
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';
|
||||||
String gamesUntilRanked({required Object left}) => '${left} games until being ranked';
|
String gamesUntilRanked({required Object left}) => '${left} games until being ranked';
|
||||||
|
@ -919,6 +925,12 @@ class _StringsRu implements Translations {
|
||||||
@override String get verdictBetter => 'Лучше';
|
@override String get verdictBetter => 'Лучше';
|
||||||
@override String get verdictWorse => 'Хуже';
|
@override String get verdictWorse => 'Хуже';
|
||||||
@override String get smooth => 'Гладкий';
|
@override String get smooth => 'Гладкий';
|
||||||
|
@override String get postSeason => 'Внесезонье';
|
||||||
|
@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}) => 'Прямо сейчас вы можете сыграть безранговый матч до трёх побед против абсолютно рандомного по скиллу игрока.\nПрименяются правила ${n} сезона';
|
||||||
|
@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 => 'Сезон закончился';
|
||||||
@override String gamesUntilRanked({required Object left}) => '${left} матчей до получения рейтинга';
|
@override String gamesUntilRanked({required Object left}) => '${left} матчей до получения рейтинга';
|
||||||
|
@ -1608,6 +1620,12 @@ extension on Translations {
|
||||||
case 'verdictBetter': return 'better';
|
case 'verdictBetter': return 'better';
|
||||||
case 'verdictWorse': return 'worse';
|
case 'verdictWorse': return 'worse';
|
||||||
case 'smooth': return 'Smooth';
|
case 'smooth': return 'Smooth';
|
||||||
|
case 'postSeason': return 'Off-season';
|
||||||
|
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 against absolutely random player.\nSeason ${n} rules applied';
|
||||||
|
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';
|
||||||
case 'gamesUntilRanked': return ({required Object left}) => '${left} games until being ranked';
|
case 'gamesUntilRanked': return ({required Object left}) => '${left} games until being ranked';
|
||||||
|
@ -2221,6 +2239,12 @@ extension on _StringsRu {
|
||||||
case 'verdictBetter': return 'Лучше';
|
case 'verdictBetter': return 'Лучше';
|
||||||
case 'verdictWorse': return 'Хуже';
|
case 'verdictWorse': return 'Хуже';
|
||||||
case 'smooth': return 'Гладкий';
|
case 'smooth': return 'Гладкий';
|
||||||
|
case 'postSeason': return 'Внесезонье';
|
||||||
|
case 'seasonStarts': return 'Сезон начнётся через:';
|
||||||
|
case 'myMessadgeHeader': return 'Сообщение от dan63';
|
||||||
|
case 'myMessadgeBody': return 'TETR.IO Tetra Channel API был серьёзно изменён после последнего обновления, поэтому некоторый функционал может не работать. Я постараюсь добавить новую статистику (и вернуть старую) как только будут опубликована новая документация по данному API.';
|
||||||
|
case 'preSeasonMessage': return ({required Object n}) => 'Прямо сейчас вы можете сыграть безранговый матч до трёх побед против абсолютно рандомного по скиллу игрока.\nПрименяются правила ${n} сезона';
|
||||||
|
case 'nanow': return 'Пока недоступно...';
|
||||||
case 'seasonEnds': return ({required Object countdown}) => 'Сезон закончится через ${countdown}';
|
case 'seasonEnds': return ({required Object countdown}) => 'Сезон закончится через ${countdown}';
|
||||||
case 'seasonEnded': return 'Сезон закончился';
|
case 'seasonEnded': return 'Сезон закончился';
|
||||||
case 'gamesUntilRanked': return ({required Object left}) => '${left} матчей до получения рейтинга';
|
case 'gamesUntilRanked': return ({required Object left}) => '${left} матчей до получения рейтинга';
|
||||||
|
|
|
@ -25,39 +25,15 @@ import 'package:go_router/go_router.dart';
|
||||||
late final PackageInfo packageInfo;
|
late final PackageInfo packageInfo;
|
||||||
late SharedPreferences prefs;
|
late SharedPreferences prefs;
|
||||||
late TetrioService teto;
|
late TetrioService teto;
|
||||||
ThemeData theme = ThemeData(fontFamily: 'Eurostile Round', colorScheme: const ColorScheme.dark(primary: Colors.cyanAccent, secondary: Colors.white), scaffoldBackgroundColor: Colors.black);
|
ThemeData theme = ThemeData(
|
||||||
|
fontFamily: 'Eurostile Round',
|
||||||
// Future<dynamic> computeIsolate(Future Function() function) async {
|
colorScheme: const ColorScheme.dark(
|
||||||
// final receivePort = ReceivePort();
|
primary: Colors.cyanAccent,
|
||||||
// var rootToken = RootIsolateToken.instance!;
|
surface: Color.fromARGB(255, 10, 10, 10),
|
||||||
// await Isolate.spawn<_IsolateData>(
|
secondary: Colors.white
|
||||||
// _isolateEntry,
|
),
|
||||||
// _IsolateData(
|
scaffoldBackgroundColor: Colors.black
|
||||||
// token: rootToken,
|
);
|
||||||
// function: function,
|
|
||||||
// answerPort: receivePort.sendPort,
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// return await receivePort.first;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void _isolateEntry(_IsolateData isolateData) async {
|
|
||||||
// BackgroundIsolateBinaryMessenger.ensureInitialized(isolateData.token);
|
|
||||||
// final answer = await isolateData.function();
|
|
||||||
// isolateData.answerPort.send(answer);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// class _IsolateData {
|
|
||||||
// final RootIsolateToken token;
|
|
||||||
// final Function function;
|
|
||||||
// final SendPort answerPort;
|
|
||||||
|
|
||||||
// _IsolateData({
|
|
||||||
// required this.token,
|
|
||||||
// required this.function,
|
|
||||||
// required this.answerPort,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
final router = GoRouter(
|
final router = GoRouter(
|
||||||
initialLocation: "/",
|
initialLocation: "/",
|
||||||
|
|
|
@ -557,8 +557,6 @@ class TetrioService extends DB {
|
||||||
nextAt: -1,
|
nextAt: -1,
|
||||||
prevAt: -1
|
prevAt: -1
|
||||||
),
|
),
|
||||||
sprint: [],
|
|
||||||
blitz: []
|
|
||||||
);
|
);
|
||||||
history.add(state);
|
history.add(state);
|
||||||
}
|
}
|
||||||
|
@ -601,7 +599,7 @@ class TetrioService extends DB {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Docs later
|
/// Docs later
|
||||||
Future<List<TetraLeagueAlphaRecord>> fetchAndSaveOldTLmatches(String userID) async {
|
Future<TetraLeagueAlphaStream> fetchAndSaveOldTLmatches(String userID) async {
|
||||||
Uri url;
|
Uri url;
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLMatches", "user": userID});
|
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLMatches", "user": userID});
|
||||||
|
@ -616,7 +614,7 @@ class TetrioService extends DB {
|
||||||
case 200:
|
case 200:
|
||||||
TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(jsonDecode(response.body)['data']['records'], userID);
|
TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(jsonDecode(response.body)['data']['records'], userID);
|
||||||
saveTLMatchesFromStream(stream);
|
saveTLMatchesFromStream(stream);
|
||||||
return stream.records;
|
return stream;
|
||||||
case 404:
|
case 404:
|
||||||
developer.log("fetchAndSaveOldTLmatches: Probably, history doesn't exist", name: "services/tetrio_crud", error: response.statusCode);
|
developer.log("fetchAndSaveOldTLmatches: Probably, history doesn't exist", name: "services/tetrio_crud", error: response.statusCode);
|
||||||
throw TetrioHistoryNotExist();
|
throw TetrioHistoryNotExist();
|
||||||
|
@ -650,7 +648,7 @@ class TetrioService extends DB {
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLLeaderboard"});
|
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLLeaderboard"});
|
||||||
} else {
|
} else {
|
||||||
url = Uri.https('ch.tetr.io', 'api/users/lists/league/all');
|
url = Uri.https('ch.tetr.io', 'api/users/by/league');
|
||||||
}
|
}
|
||||||
try{
|
try{
|
||||||
final response = await client.get(url);
|
final response = await client.get(url);
|
||||||
|
@ -660,7 +658,7 @@ class TetrioService extends DB {
|
||||||
_lbPositions.clear();
|
_lbPositions.clear();
|
||||||
var rawJson = jsonDecode(response.body);
|
var rawJson = jsonDecode(response.body);
|
||||||
if (rawJson['success']) { // if api confirmed that everything ok
|
if (rawJson['success']) { // if api confirmed that everything ok
|
||||||
TetrioPlayersLeaderboard leaderboard = TetrioPlayersLeaderboard.fromJson(rawJson['data']['users'], "league", DateTime.fromMillisecondsSinceEpoch(rawJson['cache']['cached_at']));
|
TetrioPlayersLeaderboard leaderboard = TetrioPlayersLeaderboard.fromJson(rawJson['data']['entries'], "league", DateTime.fromMillisecondsSinceEpoch(rawJson['cache']['cached_at']));
|
||||||
developer.log("fetchTLLeaderboard: Leaderboard retrieved and cached", name: "services/tetrio_crud");
|
developer.log("fetchTLLeaderboard: Leaderboard retrieved and cached", name: "services/tetrio_crud");
|
||||||
//_leaderboardsCache[rawJson['cache']['cached_until'].toString()] = leaderboard;
|
//_leaderboardsCache[rawJson['cache']['cached_until'].toString()] = leaderboard;
|
||||||
_cache.store(leaderboard, rawJson['cache']['cached_until']);
|
_cache.store(leaderboard, rawJson['cache']['cached_until']);
|
||||||
|
@ -758,15 +756,15 @@ class TetrioService extends DB {
|
||||||
|
|
||||||
/// Retrieves avaliable Tetra League matches from Tetra Channel api. Returns stream object (fake stream).
|
/// Retrieves avaliable Tetra League matches from Tetra Channel api. Returns stream object (fake stream).
|
||||||
/// Throws an exception if fails to retrieve.
|
/// Throws an exception if fails to retrieve.
|
||||||
Future<TetraLeagueAlphaStream> fetchTLStream(String userID) async {
|
Future<TetraLeagueBetaStream> fetchTLStream(String userID) async {
|
||||||
TetraLeagueAlphaStream? cached = _cache.get(userID, TetraLeagueAlphaStream);
|
TetraLeagueBetaStream? cached = _cache.get(userID, TetraLeagueAlphaStream);
|
||||||
if (cached != null) return cached;
|
if (cached != null) return cached;
|
||||||
|
|
||||||
Uri url;
|
Uri url;
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "tetrioUserTL", "user": userID.toLowerCase().trim()});
|
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "tetrioUserTL", "user": userID.toLowerCase().trim()});
|
||||||
} else {
|
} else {
|
||||||
url = Uri.https('ch.tetr.io', 'api/streams/league_userrecent_${userID.toLowerCase().trim()}');
|
url = Uri.https('ch.tetr.io', 'api/users/${userID.toLowerCase().trim()}/records/league/recent');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final response = await client.get(url);
|
final response = await client.get(url);
|
||||||
|
@ -774,7 +772,7 @@ class TetrioService extends DB {
|
||||||
switch (response.statusCode) {
|
switch (response.statusCode) {
|
||||||
case 200:
|
case 200:
|
||||||
if (jsonDecode(response.body)['success']) {
|
if (jsonDecode(response.body)['success']) {
|
||||||
TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(jsonDecode(response.body)['data']['records'], userID);
|
TetraLeagueBetaStream stream = TetraLeagueBetaStream.fromJson(jsonDecode(response.body)['data']['entries'], userID);
|
||||||
_cache.store(stream, jsonDecode(response.body)['cache']['cached_until']);
|
_cache.store(stream, jsonDecode(response.body)['cache']['cached_until']);
|
||||||
developer.log("fetchTLStream: $userID stream retrieved and cached", name: "services/tetrio_crud");
|
developer.log("fetchTLStream: $userID stream retrieved and cached", name: "services/tetrio_crud");
|
||||||
return stream;
|
return stream;
|
||||||
|
@ -941,6 +939,50 @@ class TetrioService extends DB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Summaries> fetchSummaries(String id) async {
|
||||||
|
Summaries? cached = _cache.get(id, Summaries);
|
||||||
|
if (cached != null) return cached;
|
||||||
|
|
||||||
|
Uri url;
|
||||||
|
if (kIsWeb) {
|
||||||
|
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "Summaries", "id": id});
|
||||||
|
} else {
|
||||||
|
url = Uri.https('ch.tetr.io', 'api/users/$id/summaries');
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
final response = await client.get(url);
|
||||||
|
|
||||||
|
switch (response.statusCode) {
|
||||||
|
case 200:
|
||||||
|
if (jsonDecode(response.body)['success']) {
|
||||||
|
developer.log("fetchSummaries: $id summaries retrieved and cached", name: "services/tetrio_crud");
|
||||||
|
return Summaries.fromJson(jsonDecode(response.body)['data'], id);
|
||||||
|
} else {
|
||||||
|
developer.log("fetchSummaries: User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||||
|
throw TetrioPlayerNotExist();
|
||||||
|
}
|
||||||
|
case 403:
|
||||||
|
throw TetrioForbidden();
|
||||||
|
case 429:
|
||||||
|
throw TetrioTooManyRequests();
|
||||||
|
case 418:
|
||||||
|
throw TetrioOskwareBridgeProblem();
|
||||||
|
case 500:
|
||||||
|
case 502:
|
||||||
|
case 503:
|
||||||
|
case 504:
|
||||||
|
throw TetrioInternalProblem();
|
||||||
|
default:
|
||||||
|
developer.log("fetchRecords Failed to fetch records", name: "services/tetrio_crud", error: response.statusCode);
|
||||||
|
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||||
|
}
|
||||||
|
} on http.ClientException catch (e, s) {
|
||||||
|
developer.log("$e, $s");
|
||||||
|
throw http.ClientException(e.message, e.uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates an entry in local DB for [tetrioPlayer]. Throws an exception if that player already here.
|
/// Creates an entry in local DB for [tetrioPlayer]. Throws an exception if that player already here.
|
||||||
Future<void> createPlayer(TetrioPlayer tetrioPlayer) async {
|
Future<void> createPlayer(TetrioPlayer tetrioPlayer) async {
|
||||||
await ensureDbIsOpen();
|
await ensureDbIsOpen();
|
||||||
|
@ -1131,7 +1173,7 @@ class TetrioService extends DB {
|
||||||
var json = jsonDecode(response.body);
|
var json = jsonDecode(response.body);
|
||||||
if (json['success']) {
|
if (json['success']) {
|
||||||
// parse and count stats
|
// parse and count stats
|
||||||
TetrioPlayer player = TetrioPlayer.fromJson(json['data']['user'], DateTime.fromMillisecondsSinceEpoch(json['cache']['cached_at'], isUtc: true), json['data']['user']['_id'], json['data']['user']['username'], DateTime.fromMillisecondsSinceEpoch(json['cache']['cached_until'], isUtc: true));
|
TetrioPlayer player = TetrioPlayer.fromJson(json['data'], DateTime.fromMillisecondsSinceEpoch(json['cache']['cached_at'], isUtc: true), json['data']['_id'], json['data']['username'], DateTime.fromMillisecondsSinceEpoch(json['cache']['cached_until'], isUtc: true));
|
||||||
_cache.store(player, json['cache']['cached_until']);
|
_cache.store(player, json['cache']['cached_until']);
|
||||||
developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud");
|
developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud");
|
||||||
return player;
|
return player;
|
||||||
|
@ -1175,14 +1217,14 @@ class TetrioService extends DB {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchTracked() async {
|
// Future<void> fetchTracked() async {
|
||||||
for (String userID in (await getAllPlayerToTrack())) {
|
// for (String userID in (await getAllPlayerToTrack())) {
|
||||||
TetrioPlayer player = await fetchPlayer(userID);
|
// TetrioPlayer player = await fetchPlayer(userID);
|
||||||
storeState(player);
|
// storeState(player);
|
||||||
sleep(Durations.extralong4);
|
// sleep(Durations.extralong4);
|
||||||
TetraLeagueAlphaStream matches = await fetchTLStream(userID);
|
// TetraLeagueBetaStream matches = await fetchTLStream(userID);
|
||||||
saveTLMatchesFromStream(matches);
|
// saveTLMatchesFromStream(matches);
|
||||||
sleep(Durations.extralong4);
|
// sleep(Durations.extralong4);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,8 @@ import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||||
|
|
||||||
final NumberFormat secs = NumberFormat("00.###", LocaleSettings.currentLocale.languageCode);
|
final NumberFormat secs = NumberFormat("00.###", LocaleSettings.currentLocale.languageCode);
|
||||||
final NumberFormat nonsecs = NumberFormat("00.###", LocaleSettings.currentLocale.languageCode);
|
final NumberFormat nonsecs = NumberFormat("00", LocaleSettings.currentLocale.languageCode);
|
||||||
|
final NumberFormat nonsecs3 = NumberFormat("000", LocaleSettings.currentLocale.languageCode);
|
||||||
final NumberFormat _timeInSec = NumberFormat("#,###.###s.", LocaleSettings.currentLocale.languageCode);
|
final NumberFormat _timeInSec = NumberFormat("#,###.###s.", LocaleSettings.currentLocale.languageCode);
|
||||||
|
|
||||||
/// Returns string, that represents time difference between [dateTime] and now
|
/// Returns string, that represents time difference between [dateTime] and now
|
||||||
|
@ -77,5 +78,11 @@ String readableTimeDifference(Duration a, Duration b){
|
||||||
}
|
}
|
||||||
|
|
||||||
String countdown(Duration difference){
|
String countdown(Duration difference){
|
||||||
return "${difference.inDays}:${nonsecs.format(difference.inHours%24)}:${nonsecs.format(difference.inMinutes%60)}:${secs.format(difference.inSeconds%60)}";
|
return "${difference.inDays}d ${nonsecs.format(difference.inHours%24)}h ${nonsecs.format(difference.inMinutes%60)}m ${secs.format(difference.inSeconds%60)}s";
|
||||||
|
}
|
||||||
|
|
||||||
|
String playtime(Duration difference){
|
||||||
|
if (difference.inHours > 0) return "${intf.format(difference.inHours)}h ${nonsecs.format(difference.inMinutes%60)}m";
|
||||||
|
else if (difference.inMinutes > 0) return "${difference.inMinutes}m ${nonsecs.format(difference.inSeconds%60)}s";
|
||||||
|
else return "${secs.format(difference.inMilliseconds/1000)}s";
|
||||||
}
|
}
|
|
@ -155,7 +155,8 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
|
|
||||||
// Requesting Tetra League (alpha), records, news and top TR of player
|
// Requesting Tetra League (alpha), records, news and top TR of player
|
||||||
late List<dynamic> requests;
|
late List<dynamic> requests;
|
||||||
late TetraLeagueAlphaStream tlStream;
|
late Summaries summaries;
|
||||||
|
late TetraLeagueBetaStream tlStream;
|
||||||
late UserRecords records;
|
late UserRecords records;
|
||||||
late News news;
|
late News news;
|
||||||
late SingleplayerStream recent;
|
late SingleplayerStream recent;
|
||||||
|
@ -164,24 +165,26 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
late TetrioPlayerFromLeaderboard? topOne;
|
late TetrioPlayerFromLeaderboard? topOne;
|
||||||
late TopTr? topTR;
|
late TopTr? topTR;
|
||||||
requests = await Future.wait([ // all at once (7 requests to oskware lmao)
|
requests = await Future.wait([ // all at once (7 requests to oskware lmao)
|
||||||
|
teto.fetchSummaries(_searchFor),
|
||||||
teto.fetchTLStream(_searchFor),
|
teto.fetchTLStream(_searchFor),
|
||||||
teto.fetchRecords(_searchFor),
|
//teto.fetchRecords(_searchFor),
|
||||||
teto.fetchNews(_searchFor),
|
teto.fetchNews(_searchFor),
|
||||||
teto.fetchSingleplayerStream(_searchFor, "any_userrecent"),
|
// teto.fetchSingleplayerStream(_searchFor, "any_userrecent"),
|
||||||
teto.fetchSingleplayerStream(_searchFor, "40l_userbest"),
|
// teto.fetchSingleplayerStream(_searchFor, "40l_userbest"),
|
||||||
teto.fetchSingleplayerStream(_searchFor, "blitz_userbest"),
|
// teto.fetchSingleplayerStream(_searchFor, "blitz_userbest"),
|
||||||
prefs.getBool("showPositions") != true ? teto.fetchCutoffs() : Future.delayed(Duration.zero, ()=><Map<String, double>>[]),
|
// prefs.getBool("showPositions") != true ? teto.fetchCutoffs() : Future.delayed(Duration.zero, ()=><Map<String, double>>[]),
|
||||||
(me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? teto.fetchTopOneFromTheLeaderboard() : Future.delayed(Duration.zero, ()=>null),
|
//(me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? teto.fetchTopOneFromTheLeaderboard() : Future.delayed(Duration.zero, ()=>null),
|
||||||
(me.tlSeason1.gamesPlayed > 9) ? teto.fetchTopTR(_searchFor) : Future.delayed(Duration.zero, () => null) // can retrieve this only if player has TR
|
//(me.tlSeason1.gamesPlayed > 9) ? teto.fetchTopTR(_searchFor) : Future.delayed(Duration.zero, () => null) // can retrieve this only if player has TR
|
||||||
]);
|
]);
|
||||||
tlStream = requests[0] as TetraLeagueAlphaStream;
|
summaries = requests[0] as Summaries;
|
||||||
records = requests[1] as UserRecords;
|
tlStream = requests[1] as TetraLeagueBetaStream;
|
||||||
|
// records = requests[1] as UserRecords;
|
||||||
news = requests[2] as News;
|
news = requests[2] as News;
|
||||||
recent = requests[3] as SingleplayerStream;
|
// recent = requests[3] as SingleplayerStream;
|
||||||
sprint = requests[4] as SingleplayerStream;
|
// sprint = requests[4] as SingleplayerStream;
|
||||||
blitz = requests[5] as SingleplayerStream;
|
// blitz = requests[5] as SingleplayerStream;
|
||||||
topOne = requests[7] as TetrioPlayerFromLeaderboard?;
|
// topOne = requests[7] as TetrioPlayerFromLeaderboard?;
|
||||||
topTR = requests[8] as TopTr?; // No TR - no Top TR
|
// topTR = requests[8] as TopTr?; // No TR - no Top TR
|
||||||
|
|
||||||
meAmongEveryone = teto.getCachedLeaderboardPositions(me.userId);
|
meAmongEveryone = teto.getCachedLeaderboardPositions(me.userId);
|
||||||
if (prefs.getBool("showPositions") == true){
|
if (prefs.getBool("showPositions") == true){
|
||||||
|
@ -193,37 +196,34 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!);
|
if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Map<String, double>? cutoffs = prefs.getBool("showPositions") == true ? everyone!.cutoffs : (requests[6] as Cutoffs?)?.tr;
|
//Map<String, double>? cutoffs = prefs.getBool("showPositions") == true ? everyone!.cutoffs : (requests[6] as Cutoffs?)?.tr;
|
||||||
Map<String, double>? cutoffsGlicko = prefs.getBool("showPositions") == true ? everyone!.cutoffsGlicko : (requests[6] as Cutoffs?)?.glicko;
|
//Map<String, double>? cutoffsGlicko = prefs.getBool("showPositions") == true ? everyone!.cutoffsGlicko : (requests[6] as Cutoffs?)?.glicko;
|
||||||
|
|
||||||
if (me.tlSeason1.gamesPlayed > 9) {
|
// if (me.tlSeason1.gamesPlayed > 9) {
|
||||||
thatRankCutoff = cutoffs?[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank];
|
// thatRankCutoff = cutoffs?[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank];
|
||||||
thatRankGlickoCutoff = cutoffsGlicko?[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank];
|
// thatRankGlickoCutoff = cutoffsGlicko?[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank];
|
||||||
nextRankCutoff = (me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? topOne?.rating??25000 : cutoffs?[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)];
|
// nextRankCutoff = (me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? topOne?.rating??25000 : cutoffs?[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)];
|
||||||
nextRankGlickoCutoff = (me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? topOne?.glicko??double.infinity : cutoffsGlicko?[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)];
|
// nextRankGlickoCutoff = (me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? topOne?.glicko??double.infinity : cutoffsGlicko?[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)];
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (everyone != null && me.tlSeason1.gamesPlayed > 9) rankAverages = everyone?.averages[me.tlSeason1.percentileRank]?[0];
|
// if (everyone != null && me.tlSeason1.gamesPlayed > 9) rankAverages = everyone?.averages[me.tlSeason1.percentileRank]?[0];
|
||||||
|
|
||||||
// Making list of Tetra League matches
|
// Making list of Tetra League matches
|
||||||
List<TetraLeagueAlphaRecord> tlMatches = [];
|
|
||||||
bool isTracking = await teto.isPlayerTracking(me.userId);
|
bool isTracking = await teto.isPlayerTracking(me.userId);
|
||||||
List<TetrioPlayer> states = [];
|
List<TetrioPlayer> states = [];
|
||||||
TetraLeagueAlpha? compareWith;
|
TetraLeagueAlpha? compareWith;
|
||||||
Set<TetraLeagueAlpha> uniqueTL = {};
|
Set<TetraLeagueAlpha> uniqueTL = {};
|
||||||
tlMatches = tlStream.records;
|
|
||||||
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(me);
|
||||||
await teto.saveTLMatchesFromStream(tlStream);
|
//await teto.saveTLMatchesFromStream(tlStream);
|
||||||
}
|
}
|
||||||
|
TetraLeagueAlphaStream? oldMatches;
|
||||||
// building list of TL matches
|
// building list of TL matches
|
||||||
if(fetchTLmatches) {
|
if(fetchTLmatches) {
|
||||||
try{
|
try{
|
||||||
List<TetraLeagueAlphaRecord> oldMatches = await teto.fetchAndSaveOldTLmatches(_searchFor);
|
oldMatches = await teto.fetchAndSaveOldTLmatches(_searchFor);
|
||||||
storedRecords.addAll(oldMatches);
|
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.fetchAndSaveOldTLmatchesResult(number: oldMatches.records.length))));
|
||||||
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.fetchAndSaveOldTLmatchesResult(number: oldMatches.length))));
|
|
||||||
}on TetrioHistoryNotExist{
|
}on TetrioHistoryNotExist{
|
||||||
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.errors.p1nkl0bst3rTLmatches)));
|
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.errors.p1nkl0bst3rTLmatches)));
|
||||||
}on P1nkl0bst3rForbidden {
|
}on P1nkl0bst3rForbidden {
|
||||||
|
@ -237,16 +237,16 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (storedRecords.isNotEmpty) _TLHistoryWasFetched = true;
|
if (storedRecords.isNotEmpty) _TLHistoryWasFetched = true;
|
||||||
for (var match in storedRecords) {
|
|
||||||
// add stored match to list only if it missing from retrived ones
|
// add stored match to list only if it missing from retrived ones
|
||||||
if (!tlMatches.contains(match)) tlMatches.add(match);
|
if (oldMatches != null) tlStream.addFromAlphaStream(oldMatches);
|
||||||
}
|
|
||||||
tlMatches.sort((a, b) { // Newest matches gonna be shown at the top of the list
|
// tlMatches.sort((a, b) { // Newest matches gonna be shown at the top of the list
|
||||||
if(a.timestamp.isBefore(b.timestamp)) return 1;
|
// if(a.ts.isBefore(b.ts)) return 1;
|
||||||
if(a.timestamp.isAtSameMomentAs(b.timestamp)) return 0;
|
// if(a.ts.isAtSameMomentAs(b.ts)) return 0;
|
||||||
if(a.timestamp.isAfter(b.timestamp)) return -1;
|
// if(a.ts.isAfter(b.ts)) return -1;
|
||||||
return 0;
|
// return 0;
|
||||||
});
|
// });
|
||||||
|
|
||||||
// Handling history
|
// Handling history
|
||||||
if(fetchHistory){
|
if(fetchHistory){
|
||||||
|
@ -266,8 +266,8 @@ 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 (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!);
|
||||||
}
|
}
|
||||||
// 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){
|
||||||
|
@ -305,8 +305,8 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
changePlayer(me.userId);
|
changePlayer(me.userId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return [me, summaries, news, tlStream];
|
||||||
return [me, records, states, tlMatches, compareWith, isTracking, news, topTR, recent, sprint, blitz, tlMatches.elementAtOrNull(0)?.timestamp];
|
//return [me, records, states, tlMatches, compareWith, isTracking, news, topTR, recent, sprint, blitz, tlMatches.elementAtOrNull(0)?.timestamp];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Triggers widgets rebuild
|
/// Triggers widgets rebuild
|
||||||
|
@ -455,31 +455,31 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
width: MediaQuery.of(context).size.width-450,
|
width: MediaQuery.of(context).size.width-450,
|
||||||
constraints: const BoxConstraints(maxWidth: 1024),
|
constraints: const BoxConstraints(maxWidth: 1024),
|
||||||
child: TLThingy(
|
child: TLThingy(
|
||||||
tl: snapshot.data![0].tlSeason1,
|
tl: snapshot.data![1].league,
|
||||||
userID: snapshot.data![0].userId,
|
userID: snapshot.data![0].userId,
|
||||||
states: snapshot.data![2],
|
states: const [], //snapshot.data![2],
|
||||||
topTR: snapshot.data![7]?.tr,
|
//topTR: snapshot.data![7]?.tr,
|
||||||
lastMatchPlayed: snapshot.data![11],
|
//lastMatchPlayed: snapshot.data![11],
|
||||||
bot: snapshot.data![0].role == "bot",
|
bot: snapshot.data![0].role == "bot",
|
||||||
guest: snapshot.data![0].role == "anon",
|
guest: snapshot.data![0].role == "anon",
|
||||||
thatRankCutoff: thatRankCutoff,
|
//thatRankCutoff: thatRankCutoff,
|
||||||
thatRankCutoffGlicko: thatRankGlickoCutoff,
|
//thatRankCutoffGlicko: thatRankGlickoCutoff,
|
||||||
thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null,
|
//thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null,
|
||||||
nextRankCutoff: nextRankCutoff,
|
//nextRankCutoff: nextRankCutoff,
|
||||||
nextRankCutoffGlicko: nextRankGlickoCutoff,
|
//nextRankCutoffGlicko: nextRankGlickoCutoff,
|
||||||
nextRankTarget: (snapshot.data![0].tlSeason1.rank != "z" && snapshot.data![0].tlSeason1.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1)] : null,
|
//nextRankTarget: (snapshot.data![0].tlSeason1.rank != "z" && snapshot.data![0].tlSeason1.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1)] : null,
|
||||||
averages: rankAverages,
|
//averages: rankAverages,
|
||||||
lbPositions: meAmongEveryone
|
//lbPositions: meAmongEveryone
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 450,
|
width: 450,
|
||||||
child: _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, oldMathcesHere: _TLHistoryWasFetched, separateScrollController: true,)
|
child: _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3].records, wasActiveInTL: true, oldMathcesHere: _TLHistoryWasFetched, separateScrollController: true)
|
||||||
),
|
),
|
||||||
],),
|
],),
|
||||||
_History(chartsData: chartsData, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0),
|
_History(chartsData: chartsData, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![1].league.gamesPlayed > 0),
|
||||||
_TwoRecordsThingy(sprint: snapshot.data![1].sprint, blitz: snapshot.data![1].blitz, rank: snapshot.data![0].tlSeason1.percentileRank, recent: snapshot.data![8], sprintStream: snapshot.data![9], blitzStream: snapshot.data![10]),
|
_TwoRecordsThingy(sprint: snapshot.data![1].sprint, blitz: snapshot.data![1].blitz, rank: snapshot.data![1].league.percentileRank, recent: SingleplayerStream(userId: "userId", records: [], type: "recent"), sprintStream: SingleplayerStream(userId: "userId", records: [], type: "40l"), blitzStream: SingleplayerStream(userId: "userId", records: [], type: "blitz")),
|
||||||
_OtherThingy(zen: snapshot.data![1].zen, bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![6],)
|
_OtherThingy(zen: snapshot.data![1].zen, bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![2])
|
||||||
] : [
|
] : [
|
||||||
TLThingy(
|
TLThingy(
|
||||||
tl: snapshot.data![0].tlSeason1,
|
tl: snapshot.data![0].tlSeason1,
|
||||||
|
@ -693,7 +693,7 @@ class _NavDrawerState extends State<NavDrawer> {
|
||||||
class _TLRecords extends StatelessWidget {
|
class _TLRecords extends StatelessWidget {
|
||||||
final String userID;
|
final String userID;
|
||||||
final Function changePlayer;
|
final Function changePlayer;
|
||||||
final List<TetraLeagueAlphaRecord> data;
|
final List<BetaRecord> data;
|
||||||
final bool wasActiveInTL;
|
final bool wasActiveInTL;
|
||||||
final bool oldMathcesHere;
|
final bool oldMathcesHere;
|
||||||
final bool separateScrollController;
|
final bool separateScrollController;
|
||||||
|
@ -732,7 +732,7 @@ class _TLRecords extends StatelessWidget {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
var accentColor = data[index].endContext.firstWhere((element) => element.userId == userID).success ? Colors.green : Colors.red;
|
var accentColor = data[index].results.leaderboard.firstWhere((element) => element.id == userID).wins > data[index].results.leaderboard.firstWhere((element) => element.id != userID).wins ? Colors.green : Colors.red;
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
|
@ -741,19 +741,19 @@ class _TLRecords extends StatelessWidget {
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Text("${data[index].endContext.firstWhere((element) => element.userId == userID).points} : ${data[index].endContext.firstWhere((element) => element.userId != userID).points}",
|
leading: Text("${data[index].results.leaderboard.firstWhere((element) => element.id == userID).wins} : ${data[index].results.leaderboard.firstWhere((element) => element.id != userID).wins}",
|
||||||
style: bigScreen ? const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, shadows: textShadow) : const TextStyle(fontSize: 28, shadows: textShadow)),
|
style: bigScreen ? const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, shadows: textShadow) : const TextStyle(fontSize: 28, shadows: textShadow)),
|
||||||
title: Text("vs. ${data[index].endContext.firstWhere((element) => element.userId != userID).username}"),
|
title: Text("vs. ${data[index].results.leaderboard.firstWhere((element) => element.id != userID).username}"),
|
||||||
subtitle: Text(timestamp(data[index].timestamp), style: const TextStyle(color: Colors.grey)),
|
subtitle: Text(timestamp(data[index].ts), style: const TextStyle(color: Colors.grey)),
|
||||||
trailing: TrailingStats(
|
trailing: TrailingStats(
|
||||||
data[index].endContext.firstWhere((element) => element.userId == userID).secondary,
|
data[index].results.leaderboard.firstWhere((element) => element.id == userID).stats.apm,
|
||||||
data[index].endContext.firstWhere((element) => element.userId == userID).tertiary,
|
data[index].results.leaderboard.firstWhere((element) => element.id == userID).stats.pps,
|
||||||
data[index].endContext.firstWhere((element) => element.userId == userID).extra,
|
data[index].results.leaderboard.firstWhere((element) => element.id == userID).stats.vs,
|
||||||
data[index].endContext.firstWhere((element) => element.userId != userID).secondary,
|
data[index].results.leaderboard.firstWhere((element) => element.id != userID).stats.apm,
|
||||||
data[index].endContext.firstWhere((element) => element.userId != userID).tertiary,
|
data[index].results.leaderboard.firstWhere((element) => element.id != userID).stats.pps,
|
||||||
data[index].endContext.firstWhere((element) => element.userId != userID).extra
|
data[index].results.leaderboard.firstWhere((element) => element.id != userID).stats.vs,
|
||||||
),
|
),
|
||||||
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => TlMatchResultView(record: data[index], initPlayerId: userID))),
|
onTap: () => {if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.nanow)))} //Navigator.push(context, MaterialPageRoute(builder: (context) => TlMatchResultView(record: data[index], initPlayerId: userID))),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1015,17 +1015,17 @@ class _TwoRecordsThingy extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
late MapEntry closestAverageBlitz;
|
late MapEntry closestAverageBlitz;
|
||||||
late bool blitzBetterThanClosestAverage;
|
late bool blitzBetterThanClosestAverage;
|
||||||
bool? blitzBetterThanRankAverage = (rank != null && rank != "z" && blitz != null) ? blitz!.endContext.score > blitzAverages[rank]! : null;
|
bool? blitzBetterThanRankAverage = (rank != null && rank != "z" && blitz != null) ? blitz!.stats.score > blitzAverages[rank]! : null;
|
||||||
late MapEntry closestAverageSprint;
|
late MapEntry closestAverageSprint;
|
||||||
late bool sprintBetterThanClosestAverage;
|
late bool sprintBetterThanClosestAverage;
|
||||||
bool? sprintBetterThanRankAverage = (rank != null && rank != "z" && sprint != null) ? sprint!.endContext.finalTime < sprintAverages[rank]! : null;
|
bool? sprintBetterThanRankAverage = (rank != null && rank != "z" && sprint != null) ? sprint!.stats.finalTime < sprintAverages[rank]! : null;
|
||||||
if (sprint != null) {
|
if (sprint != null) {
|
||||||
closestAverageSprint = sprintAverages.entries.singleWhere((element) => element.value == sprintAverages.values.reduce((a, b) => (a-sprint!.endContext.finalTime).abs() < (b -sprint!.endContext.finalTime).abs() ? a : b));
|
closestAverageSprint = sprintAverages.entries.singleWhere((element) => element.value == sprintAverages.values.reduce((a, b) => (a-sprint!.stats.finalTime).abs() < (b -sprint!.stats.finalTime).abs() ? a : b));
|
||||||
sprintBetterThanClosestAverage = sprint!.endContext.finalTime < closestAverageSprint.value;
|
sprintBetterThanClosestAverage = sprint!.stats.finalTime < closestAverageSprint.value;
|
||||||
}
|
}
|
||||||
if (blitz != null){
|
if (blitz != null){
|
||||||
closestAverageBlitz = blitzAverages.entries.singleWhere((element) => element.value == blitzAverages.values.reduce((a, b) => (a-blitz!.endContext.score).abs() < (b -blitz!.endContext.score).abs() ? a : b));
|
closestAverageBlitz = blitzAverages.entries.singleWhere((element) => element.value == blitzAverages.values.reduce((a, b) => (a-blitz!.stats.score).abs() < (b -blitz!.stats.score).abs() ? a : b));
|
||||||
blitzBetterThanClosestAverage = blitz!.endContext.score > closestAverageBlitz.value;
|
blitzBetterThanClosestAverage = blitz!.stats.score > closestAverageBlitz.value;
|
||||||
}
|
}
|
||||||
return SingleChildScrollView(child: Padding(
|
return SingleChildScrollView(child: Padding(
|
||||||
padding: const EdgeInsets.only(top: 20.0),
|
padding: const EdgeInsets.only(top: 20.0),
|
||||||
|
@ -1047,19 +1047,19 @@ class _TwoRecordsThingy extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Text(t.sprint, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
Text(t.sprint, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
||||||
RichText(text: TextSpan(
|
RichText(text: TextSpan(
|
||||||
text: sprint != null ? get40lTime(sprint!.endContext.finalTime.inMicroseconds) : "---",
|
text: sprint != null ? get40lTime(sprint!.stats.finalTime.inMicroseconds) : "---",
|
||||||
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500, color: sprint != null ? Colors.white : Colors.grey),
|
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500, color: sprint != null ? Colors.white : Colors.grey),
|
||||||
//children: [TextSpan(text: get40lTime(record!.endContext.finalTime.inMicroseconds), style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))]
|
//children: [TextSpan(text: get40lTime(record!.stats.finalTime.inMicroseconds), style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))]
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (sprint != null) RichText(text: TextSpan(
|
if (sprint != null) RichText(text: TextSpan(
|
||||||
text: "",
|
text: "",
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
||||||
children: [
|
children: [
|
||||||
if (rank != null && rank != "z") TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(sprint!.endContext.finalTime, sprintAverages[rank]!), verdict: sprintBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
if (rank != null && rank != "z") TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(sprint!.stats.finalTime, sprintAverages[rank]!), verdict: sprintBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
||||||
color: sprintBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
color: sprintBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
||||||
))
|
))
|
||||||
else TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(sprint!.endContext.finalTime, closestAverageSprint.value), verdict: sprintBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageSprint.key.toUpperCase())}\n", style: TextStyle(
|
else TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(sprint!.stats.finalTime, closestAverageSprint.value), verdict: sprintBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageSprint.key.toUpperCase())}\n", style: TextStyle(
|
||||||
color: sprintBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
color: sprintBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
||||||
)),
|
)),
|
||||||
if (sprint!.rank != null) TextSpan(text: "№${sprint!.rank}", style: TextStyle(color: getColorOfRank(sprint!.rank!))),
|
if (sprint!.rank != null) TextSpan(text: "№${sprint!.rank}", style: TextStyle(color: getColorOfRank(sprint!.rank!))),
|
||||||
|
@ -1076,14 +1076,14 @@ class _TwoRecordsThingy extends StatelessWidget {
|
||||||
alignment: WrapAlignment.spaceBetween,
|
alignment: WrapAlignment.spaceBetween,
|
||||||
spacing: 20,
|
spacing: 20,
|
||||||
children: [
|
children: [
|
||||||
StatCellNum(playerStat: sprint!.endContext.piecesPlaced, playerStatLabel: t.statCellNum.pieces, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
StatCellNum(playerStat: sprint!.stats.piecesPlaced, playerStatLabel: t.statCellNum.pieces, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||||
StatCellNum(playerStat: sprint!.endContext.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
StatCellNum(playerStat: sprint!.stats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||||
StatCellNum(playerStat: sprint!.endContext.kpp, playerStatLabel: t.statCellNum.kpp, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
StatCellNum(playerStat: sprint!.stats.kpp, playerStatLabel: t.statCellNum.kpp, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (sprint != null) FinesseThingy(sprint?.endContext.finesse, sprint?.endContext.finessePercentage),
|
if (sprint != null) FinesseThingy(sprint?.stats.finesse, sprint?.stats.finessePercentage),
|
||||||
if (sprint != null) LineclearsThingy(sprint!.endContext.clears, sprint!.endContext.lines, sprint!.endContext.holds, sprint!.endContext.tSpins),
|
if (sprint != null) LineclearsThingy(sprint!.stats.clears, sprint!.stats.lines, sprint!.stats.holds, sprint!.stats.tSpins),
|
||||||
if (sprint != null) Text("${sprint!.endContext.inputs} KP • ${f2.format(sprint!.endContext.kps)} KPS"),
|
if (sprint != null) Text("${sprint!.stats.inputs} KP • ${f2.format(sprint!.stats.kps)} KPS"),
|
||||||
if (sprint != null) Wrap(
|
if (sprint != null) Wrap(
|
||||||
alignment: WrapAlignment.spaceBetween,
|
alignment: WrapAlignment.spaceBetween,
|
||||||
crossAxisAlignment: WrapCrossAlignment.start,
|
crossAxisAlignment: WrapCrossAlignment.start,
|
||||||
|
@ -1101,10 +1101,10 @@ class _TwoRecordsThingy extends StatelessWidget {
|
||||||
for (int i = 1; i < sprintStream.records.length; i++) ListTile(
|
for (int i = 1; i < sprintStream.records.length; i++) ListTile(
|
||||||
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: sprintStream.records[i]))),
|
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: sprintStream.records[i]))),
|
||||||
leading: Text("#${i+1}", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9) ),
|
leading: Text("#${i+1}", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9) ),
|
||||||
title: Text(get40lTime(sprintStream.records[i].endContext.finalTime.inMicroseconds),
|
title: Text(get40lTime(sprintStream.records[i].stats.finalTime.inMicroseconds),
|
||||||
style: const TextStyle(fontSize: 18)),
|
style: const TextStyle(fontSize: 18)),
|
||||||
subtitle: Text(timestamp(sprintStream.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
|
subtitle: Text(timestamp(sprintStream.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
|
||||||
trailing: SpTrailingStats(sprintStream.records[i].endContext)
|
trailing: SpTrailingStats(sprintStream.records[i].stats, sprintStream.records[i].gamemode)
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1128,7 +1128,7 @@ class _TwoRecordsThingy extends StatelessWidget {
|
||||||
text: "",
|
text: "",
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white),
|
style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white),
|
||||||
children: [
|
children: [
|
||||||
TextSpan(text: blitz != null ? NumberFormat.decimalPattern().format(blitz!.endContext.score) : "---"),
|
TextSpan(text: blitz != null ? NumberFormat.decimalPattern().format(blitz!.stats.score) : "---"),
|
||||||
//WidgetSpan(child: Image.asset("res/icons/kagari.png", height: 48))
|
//WidgetSpan(child: Image.asset("res/icons/kagari.png", height: 48))
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
@ -1139,10 +1139,10 @@ class _TwoRecordsThingy extends StatelessWidget {
|
||||||
text: "",
|
text: "",
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
||||||
children: [
|
children: [
|
||||||
if (rank != null && rank != "z") TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(blitz!.endContext.score, blitzAverages[rank]!), verdict: blitzBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
if (rank != null && rank != "z") TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(blitz!.stats.score, blitzAverages[rank]!), verdict: blitzBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
||||||
color: blitzBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
color: blitzBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
||||||
))
|
))
|
||||||
else TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(blitz!.endContext.score, closestAverageBlitz.value), verdict: blitzBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageBlitz.key.toUpperCase())}\n", style: TextStyle(
|
else TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(blitz!.stats.score, closestAverageBlitz.value), verdict: blitzBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageBlitz.key.toUpperCase())}\n", style: TextStyle(
|
||||||
color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
||||||
)),
|
)),
|
||||||
TextSpan(text: timestamp(blitz!.timestamp)),
|
TextSpan(text: timestamp(blitz!.timestamp)),
|
||||||
|
@ -1162,14 +1162,14 @@ class _TwoRecordsThingy extends StatelessWidget {
|
||||||
crossAxisAlignment: WrapCrossAlignment.start,
|
crossAxisAlignment: WrapCrossAlignment.start,
|
||||||
spacing: 20,
|
spacing: 20,
|
||||||
children: [
|
children: [
|
||||||
StatCellNum(playerStat: blitz!.endContext.level, playerStatLabel: t.statCellNum.level, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
StatCellNum(playerStat: blitz!.stats.level, playerStatLabel: t.statCellNum.level, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||||
StatCellNum(playerStat: blitz!.endContext.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
StatCellNum(playerStat: blitz!.stats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||||
StatCellNum(playerStat: blitz!.endContext.spp, playerStatLabel: t.statCellNum.spp, fractionDigits: 2, isScreenBig: true, higherIsBetter: true)
|
StatCellNum(playerStat: blitz!.stats.spp, playerStatLabel: t.statCellNum.spp, fractionDigits: 2, isScreenBig: true, higherIsBetter: true)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (blitz != null) FinesseThingy(blitz?.endContext.finesse, blitz?.endContext.finessePercentage),
|
if (blitz != null) FinesseThingy(blitz?.stats.finesse, blitz?.stats.finessePercentage),
|
||||||
if (blitz != null) LineclearsThingy(blitz!.endContext.clears, blitz!.endContext.lines, blitz!.endContext.holds, blitz!.endContext.tSpins),
|
if (blitz != null) LineclearsThingy(blitz!.stats.clears, blitz!.stats.lines, blitz!.stats.holds, blitz!.stats.tSpins),
|
||||||
if (blitz != null) Text("${blitz!.endContext.piecesPlaced} P • ${blitz!.endContext.inputs} KP • ${f2.format(blitz!.endContext.kpp)} KPP • ${f2.format(blitz!.endContext.kps)} KPS"),
|
if (blitz != null) Text("${blitz!.stats.piecesPlaced} P • ${blitz!.stats.inputs} KP • ${f2.format(blitz!.stats.kpp)} KPP • ${f2.format(blitz!.stats.kps)} KPS"),
|
||||||
if (blitz != null) Wrap(
|
if (blitz != null) Wrap(
|
||||||
alignment: WrapAlignment.spaceBetween,
|
alignment: WrapAlignment.spaceBetween,
|
||||||
crossAxisAlignment: WrapCrossAlignment.start,
|
crossAxisAlignment: WrapCrossAlignment.start,
|
||||||
|
@ -1187,10 +1187,10 @@ class _TwoRecordsThingy extends StatelessWidget {
|
||||||
for (int i = 1; i < blitzStream.records.length; i++) ListTile(
|
for (int i = 1; i < blitzStream.records.length; i++) ListTile(
|
||||||
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: blitzStream.records[i]))),
|
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: blitzStream.records[i]))),
|
||||||
leading: Text("#${i+1}", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9) ),
|
leading: Text("#${i+1}", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9) ),
|
||||||
title: Text("${NumberFormat.decimalPattern().format(blitzStream.records[i].endContext.score)} points",
|
title: Text("${NumberFormat.decimalPattern().format(blitzStream.records[i].stats.score)} points",
|
||||||
style: const TextStyle(fontSize: 18)),
|
style: const TextStyle(fontSize: 18)),
|
||||||
subtitle: Text(timestamp(blitzStream.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
|
subtitle: Text(timestamp(blitzStream.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
|
||||||
trailing: SpTrailingStats(blitzStream.records[i].endContext)
|
trailing: SpTrailingStats(blitzStream.records[i].stats, blitzStream.records[i].gamemode)
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1277,7 +1277,9 @@ class _OtherThingy extends StatelessWidget {
|
||||||
Map<String, String> gametypes = {
|
Map<String, String> gametypes = {
|
||||||
"40l": t.sprint,
|
"40l": t.sprint,
|
||||||
"blitz": t.blitz,
|
"blitz": t.blitz,
|
||||||
"5mblast": "5,000,000 Blast"
|
"5mblast": "5,000,000 Blast",
|
||||||
|
"zenith": "Quick Play",
|
||||||
|
"zenithex": "Quick Play Expert",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Individuly handle each entry type
|
// Individuly handle each entry type
|
||||||
|
@ -1306,7 +1308,15 @@ class _OtherThingy extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
TextSpan(text: "${gametypes[news.data["gametype"]]} ", style: const TextStyle(fontWeight: FontWeight.bold)),
|
TextSpan(text: "${gametypes[news.data["gametype"]]} ", style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
TextSpan(text: t.newsParts.personalbestMiddle),
|
TextSpan(text: t.newsParts.personalbestMiddle),
|
||||||
TextSpan(text: news.data["gametype"] == "blitz" ? NumberFormat.decimalPattern().format(news.data["result"]) : get40lTime((news.data["result"]*1000).floor()), style: const TextStyle(fontWeight: FontWeight.bold)),
|
TextSpan(text: switch (news.data["gametype"]){
|
||||||
|
"blitz" => NumberFormat.decimalPattern().format(news.data["result"]),
|
||||||
|
"40l" => get40lTime((news.data["result"]*1000).floor()),
|
||||||
|
"5mblast" => get40lTime((news.data["result"]*1000).floor()),
|
||||||
|
"zenith" => "${f2.format(news.data["result"])} m.",
|
||||||
|
_ => "unknown"
|
||||||
|
},
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold)
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,714 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/material.dart' hide Badge;
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
||||||
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
|
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||||
|
import 'package:tetra_stats/utils/relative_timestamps.dart';
|
||||||
|
import 'package:tetra_stats/utils/text_shadow.dart';
|
||||||
|
import 'package:tetra_stats/views/compare_view.dart';
|
||||||
|
import 'package:tetra_stats/widgets/stat_sell_num.dart';
|
||||||
|
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
||||||
|
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||||
|
import 'package:tetra_stats/main.dart';
|
||||||
|
import 'package:tetra_stats/widgets/tl_thingy.dart';
|
||||||
|
import 'package:tetra_stats/widgets/user_thingy.dart';
|
||||||
|
|
||||||
|
class MainView extends StatefulWidget {
|
||||||
|
final String? player;
|
||||||
|
/// The very first view, that user see when he launch this programm.
|
||||||
|
/// By default it loads my or defined in preferences user stats, but
|
||||||
|
/// if [player] username or id provided, it loads his stats. Also it hides menu drawer and three dots menu.
|
||||||
|
const MainView({super.key, this.player});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MainView> createState() => _MainState();
|
||||||
|
}
|
||||||
|
|
||||||
|
TetrioPlayer testPlayer = TetrioPlayer(
|
||||||
|
userId: "6098518e3d5155e6ec429cdc",
|
||||||
|
username: "dan63",
|
||||||
|
registrationTime: DateTime(2002, 2, 25, 9, 30, 01),
|
||||||
|
avatarRevision: 1704835194288,
|
||||||
|
bannerRevision: 1661462402700,
|
||||||
|
role: "sysop",
|
||||||
|
country: "BY",
|
||||||
|
state: DateTime(1970),
|
||||||
|
badges: [
|
||||||
|
Badge(badgeId: "kod_founder", label: "Убил оска", ts: DateTime(2023, 6, 27, 18, 51, 49)),
|
||||||
|
Badge(badgeId: "kod_by_founder", label: "Убит оском", ts: DateTime(2023, 6, 27, 18, 51, 51)),
|
||||||
|
Badge(badgeId: "5mblast_1", label: "5M Blast Winner"),
|
||||||
|
Badge(badgeId: "20tsd", label: "20 TSD"),
|
||||||
|
Badge(badgeId: "allclear", label: "10PC's"),
|
||||||
|
Badge(badgeId: "100player", label: "Won some shit"),
|
||||||
|
Badge(badgeId: "founder", label: "osk"),
|
||||||
|
Badge(badgeId: "early-supporter", label: "Sus"),
|
||||||
|
Badge(badgeId: "bugbounty", label: "Break some ribbons"),
|
||||||
|
Badge(badgeId: "infdev", label: "Closed player")
|
||||||
|
],
|
||||||
|
friendCount: 69,
|
||||||
|
gamesPlayed: 13747,
|
||||||
|
gamesWon: 6523,
|
||||||
|
gameTime: Duration(days: 79, minutes: 28, seconds: 23, microseconds: 637591),
|
||||||
|
xp: 1415239,
|
||||||
|
supporterTier: 2,
|
||||||
|
verified: true,
|
||||||
|
connections: null,
|
||||||
|
tlSeason1: TetraLeagueAlpha(timestamp: DateTime(1970), gamesPlayed: 28, gamesWon: 14, bestRank: "x", decaying: false, rating: 23500.6194, rank: "x", percentileRank: "x", percentile: 0.00, standing: 1, standingLocal: 1, nextAt: -1, prevAt: 500),
|
||||||
|
distinguishment: Distinguishment(type: "twc", detail: "2023"),
|
||||||
|
bio: "кровбер не в палку, без последнего тспина - 32 атаки. кровбер не в палку, без первого тсм и последнего тспина - 30 атаки. кровбер в палку с б2б - 38 атаки.(5 б2б)(не знаю от чего зависит) кровбер в палку с б2б - 36 атаки.(5 б2б)(не знаю от чего зависит)"
|
||||||
|
);
|
||||||
|
News testNews = News("6098518e3d5155e6ec429cdc", [
|
||||||
|
NewsEntry(type: "personalbest", data: {"gametype": "40l", "result": 23.232}, timestamp: DateTime(2002, 2, 25, 10, 30, 01)),
|
||||||
|
NewsEntry(type: "personalbest", data: {"gametype": "blitz", "result": 23.232}, timestamp: DateTime(2002, 2, 25, 10, 30, 02)),
|
||||||
|
NewsEntry(type: "personalbest", data: {"gametype": "5mblast", "result": 23.232}, timestamp: DateTime(2002, 2, 25, 10, 30, 03)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(body: Row(
|
||||||
|
children: [
|
||||||
|
NavigationRail(
|
||||||
|
destinations: [
|
||||||
|
NavigationRailDestination(
|
||||||
|
icon: Icon(Icons.favorite_border),
|
||||||
|
selectedIcon: Icon(Icons.favorite),
|
||||||
|
label: Text('First'),
|
||||||
|
),
|
||||||
|
NavigationRailDestination(
|
||||||
|
icon: Icon(Icons.bookmark_border),
|
||||||
|
selectedIcon: Icon(Icons.book),
|
||||||
|
label: Text('Second'),
|
||||||
|
),
|
||||||
|
NavigationRailDestination(
|
||||||
|
icon: Icon(Icons.star_border),
|
||||||
|
selectedIcon: Icon(Icons.star),
|
||||||
|
label: Text('Third'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
selectedIndex: 0
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 450.0,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
NewUserThingy(player: testPlayer, showStateTimestamp: false, setState: setState),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(4.0, 0.0, 4.0, 0.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(child: ElevatedButton.icon(onPressed: (){print("ok, and?");}, icon: Icon(Icons.person_add), label: Text(t.track), style: ButtonStyle(shape: MaterialStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(12.0), right: Radius.zero)))))),
|
||||||
|
Expanded(child: ElevatedButton.icon(onPressed: (){print("ok, and?");}, icon: Icon(Icons.balance), label: Text(t.compare), style: ButtonStyle(shape: MaterialStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.zero, right: Radius.circular(12.0)))))))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
surfaceTintColor: theme.colorScheme.surface,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 0.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text("Badges", style: TextStyle(fontFamily: "Eurostile Round Extended")),
|
||||||
|
Spacer(),
|
||||||
|
Text(intf.format(testPlayer.badges.length))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
for (var badge in testPlayer.badges)
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(badge.label, style: const TextStyle(fontFamily: "Eurostile Round Extended")),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: ListBody(
|
||||||
|
children: [
|
||||||
|
Wrap(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
spacing: 25,
|
||||||
|
children: [
|
||||||
|
Image.asset("res/tetrio_badges/${badge.badgeId}.png"),
|
||||||
|
Text(badge.ts != null
|
||||||
|
? t.obtainDate(date: timestamp(badge.ts!))
|
||||||
|
: t.assignedManualy),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: Text(t.popupActions.ok),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
tooltip: badge.label,
|
||||||
|
icon: Image.asset(
|
||||||
|
"res/tetrio_badges/${badge.badgeId}.png",
|
||||||
|
height: 32,
|
||||||
|
width: 32,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Image.network(
|
||||||
|
kIsWeb ? "https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioBadge&badge=${badge.badgeId}" : "https://tetr.io/res/badges/${badge.badgeId}.png",
|
||||||
|
height: 32,
|
||||||
|
width: 32,
|
||||||
|
errorBuilder:(context, error, stackTrace) {
|
||||||
|
return Image.asset("res/icons/kagari.png", height: 32, width: 32);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (testPlayer.distinguishment != null) DistinguishmentThingy(testPlayer.distinguishment!),
|
||||||
|
if (testPlayer.bio != null) Card(
|
||||||
|
surfaceTintColor: theme.colorScheme.surface,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Spacer(),
|
||||||
|
Text(t.bio, style: TextStyle(fontFamily: "Eurostile Round Extended")),
|
||||||
|
Spacer()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: MarkdownBody(data: testPlayer.bio!, styleSheet: MarkdownStyleSheet(textAlign: WrapAlignment.center)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
//if (testNews != null && testNews!.news.isNotEmpty)
|
||||||
|
Expanded(child: NewsThingy(testNews))
|
||||||
|
],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 450.0,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NewsThingy extends StatelessWidget{
|
||||||
|
final News news;
|
||||||
|
|
||||||
|
NewsThingy(this.news);
|
||||||
|
|
||||||
|
ListTile getNewsTile(NewsEntry news){
|
||||||
|
Map<String, String> gametypes = {
|
||||||
|
"40l": t.sprint,
|
||||||
|
"blitz": t.blitz,
|
||||||
|
"5mblast": "5,000,000 Blast"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Individuly handle each entry type
|
||||||
|
switch (news.type) {
|
||||||
|
case "leaderboard":
|
||||||
|
return ListTile(
|
||||||
|
title: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: const TextStyle(fontFamily: 'Eurostile Round', fontSize: 16, color: Colors.white),
|
||||||
|
text: t.newsParts.leaderboardStart,
|
||||||
|
children: [
|
||||||
|
TextSpan(text: "№${news.data["rank"]} ", style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
TextSpan(text: t.newsParts.leaderboardMiddle),
|
||||||
|
TextSpan(text: "№${gametypes[news.data["gametype"]]}", style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
subtitle: Text(timestamp(news.timestamp)),
|
||||||
|
);
|
||||||
|
case "personalbest":
|
||||||
|
return ListTile(
|
||||||
|
title: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: const TextStyle(fontFamily: 'Eurostile Round', fontSize: 16, color: Colors.white),
|
||||||
|
text: t.newsParts.personalbest,
|
||||||
|
children: [
|
||||||
|
TextSpan(text: "${gametypes[news.data["gametype"]]} ", style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
TextSpan(text: t.newsParts.personalbestMiddle),
|
||||||
|
TextSpan(text: news.data["gametype"] == "blitz" ? NumberFormat.decimalPattern().format(news.data["result"]) : get40lTime((news.data["result"]*1000).floor()), style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
subtitle: Text(timestamp(news.timestamp)),
|
||||||
|
leading: Image.asset(
|
||||||
|
"res/icons/improvement-local.png",
|
||||||
|
height: 48,
|
||||||
|
width: 48,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Image.asset("res/icons/kagari.png", height: 64, width: 64);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case "badge":
|
||||||
|
return ListTile(
|
||||||
|
title: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: const TextStyle(fontFamily: 'Eurostile Round', fontSize: 16, color: Colors.white),
|
||||||
|
text: t.newsParts.badgeStart,
|
||||||
|
children: [
|
||||||
|
TextSpan(text: "${news.data["label"]} ", style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
TextSpan(text: t.newsParts.badgeEnd)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
subtitle: Text(timestamp(news.timestamp)),
|
||||||
|
leading: Image.asset(
|
||||||
|
"res/tetrio_badges/${news.data["type"]}.png",
|
||||||
|
height: 48,
|
||||||
|
width: 48,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Image.asset("res/icons/kagari.png", height: 64, width: 64);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case "rankup":
|
||||||
|
return ListTile(
|
||||||
|
title: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: const TextStyle(fontFamily: 'Eurostile Round', fontSize: 16, color: Colors.white),
|
||||||
|
text: t.newsParts.rankupStart,
|
||||||
|
children: [
|
||||||
|
TextSpan(text: t.newsParts.rankupMiddle(r: news.data["rank"].toString().toUpperCase()), style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
TextSpan(text: t.newsParts.rankupEnd)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
subtitle: Text(timestamp(news.timestamp)),
|
||||||
|
leading: Image.asset(
|
||||||
|
"res/tetrio_tl_alpha_ranks/${news.data["rank"]}.png",
|
||||||
|
height: 48,
|
||||||
|
width: 48,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Image.asset("res/icons/kagari.png", height: 64, width: 64);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case "supporter":
|
||||||
|
return ListTile(
|
||||||
|
title: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: const TextStyle(fontFamily: 'Eurostile Round', fontSize: 16, color: Colors.white),
|
||||||
|
text: t.newsParts.supporterStart,
|
||||||
|
children: [
|
||||||
|
TextSpan(text: t.newsParts.tetoSupporter, style: const TextStyle(fontWeight: FontWeight.bold))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
subtitle: Text(timestamp(news.timestamp)),
|
||||||
|
leading: Image.asset(
|
||||||
|
"res/icons/supporter-tag.png",
|
||||||
|
height: 48,
|
||||||
|
width: 48,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Image.asset("res/icons/kagari.png", height: 64, width: 64);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case "supporter_gift":
|
||||||
|
return ListTile(
|
||||||
|
title: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: const TextStyle(fontFamily: 'Eurostile Round', fontSize: 16, color: Colors.white),
|
||||||
|
text: t.newsParts.supporterGiftStart,
|
||||||
|
children: [
|
||||||
|
TextSpan(text: t.newsParts.tetoSupporter, style: const TextStyle(fontWeight: FontWeight.bold))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
subtitle: Text(timestamp(news.timestamp)),
|
||||||
|
leading: Image.asset(
|
||||||
|
"res/icons/supporter-tag.png",
|
||||||
|
height: 48,
|
||||||
|
width: 48,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Image.asset("res/icons/kagari.png", height: 64, width: 64);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
default: // if type is unknown
|
||||||
|
return ListTile(
|
||||||
|
title: Text(t.newsParts.unknownNews(type: news.type)),
|
||||||
|
subtitle: Text(timestamp(news.timestamp)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
surfaceTintColor: theme.colorScheme.surface,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Spacer(),
|
||||||
|
Text(t.news, style: TextStyle(fontFamily: "Eurostile Round Extended")),
|
||||||
|
Spacer()
|
||||||
|
]
|
||||||
|
),
|
||||||
|
for (NewsEntry entry in news.news) getNewsTile(entry)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class DistinguishmentThingy extends StatelessWidget{
|
||||||
|
final Distinguishment distinguishment;
|
||||||
|
|
||||||
|
DistinguishmentThingy(this.distinguishment, {super.key});
|
||||||
|
|
||||||
|
List<InlineSpan> getDistinguishmentTitle(String? text) {
|
||||||
|
// TWC champions don't have header in their distinguishments
|
||||||
|
if (distinguishment.type == "twc") return [const TextSpan(text: "TETR.IO World Champion", style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: Colors.yellowAccent))];
|
||||||
|
// In case if it missing for some other reason, return this
|
||||||
|
if (text == null) return [const TextSpan(text: "Header is missing", style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: Colors.redAccent))];
|
||||||
|
|
||||||
|
// Handling placeholders for logos
|
||||||
|
var exploded = text.split(" "); // wtf PHP reference?
|
||||||
|
List<InlineSpan> result = [];
|
||||||
|
for (String shit in exploded){
|
||||||
|
switch (shit) { // if %% thingy was found, insert svg of icon
|
||||||
|
case "%osk%":
|
||||||
|
result.add(WidgetSpan(child: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8),
|
||||||
|
child: SvgPicture.asset("res/icons/osk.svg", height: 28),
|
||||||
|
)));
|
||||||
|
break;
|
||||||
|
case "%tetrio%":
|
||||||
|
result.add(WidgetSpan(child: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8),
|
||||||
|
child: SvgPicture.asset("res/icons/tetrio-logo.svg", height: 28),
|
||||||
|
)));
|
||||||
|
break;
|
||||||
|
default: // if not, insert text span
|
||||||
|
result.add(TextSpan(text: " $shit", style: const TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: Colors.white)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Distinguishment title is barely predictable thing.
|
||||||
|
/// Receives [text], which is footer and returns sets of widgets for RichText widget
|
||||||
|
String getDistinguishmentSubtitle(String? text){
|
||||||
|
// TWC champions don't have footer in their distinguishments
|
||||||
|
if (distinguishment.type == "twc") return "${distinguishment.detail} TETR.IO World Championship";
|
||||||
|
// In case if it missing for some other reason, return this
|
||||||
|
if (text == null) return "Footer is missing";
|
||||||
|
// If everything ok, return as it is
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getCardTint(String type, String detail){
|
||||||
|
switch(type){
|
||||||
|
case "staff":
|
||||||
|
switch(detail){
|
||||||
|
case "founder": return Color(0xAAFD82D4);
|
||||||
|
case "kagarin": return Color(0xAAFF0060);
|
||||||
|
case "team": return Color(0xAAFACC2E);
|
||||||
|
case "team-minor": return Color(0xAAF5BD45);
|
||||||
|
case "administrator": return Color(0xAAFF4E8A);
|
||||||
|
case "globalmod": return Color(0xAAE878FF);
|
||||||
|
case "communitymod": return Color(0xAA4E68FB);
|
||||||
|
case "alumni": return Color(0xAA6057DB);
|
||||||
|
default: return theme.colorScheme.surface;
|
||||||
|
}
|
||||||
|
case "champion":
|
||||||
|
switch (detail){
|
||||||
|
case "blitz":
|
||||||
|
case "40l": return Color(0xAACCF5F6);
|
||||||
|
case "league": return Color(0xAAFFDB31);
|
||||||
|
}
|
||||||
|
case "twc": return Color(0xAAFFDB31);
|
||||||
|
default: return theme.colorScheme.surface;
|
||||||
|
}
|
||||||
|
return theme.colorScheme.surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
surfaceTintColor: getCardTint(distinguishment.type, distinguishment.detail??"null"),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Spacer(),
|
||||||
|
Text(t.distinguishment, style: TextStyle(fontFamily: "Eurostile Round Extended")),
|
||||||
|
Spacer()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
RichText(
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
text: TextSpan(
|
||||||
|
style: DefaultTextStyle.of(context).style,
|
||||||
|
children: getDistinguishmentTitle(distinguishment.header),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(getDistinguishmentSubtitle(distinguishment.footer), style: const TextStyle(fontSize: 18), textAlign: TextAlign.center),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NewUserThingy extends StatelessWidget {
|
||||||
|
final TetrioPlayer player;
|
||||||
|
final bool showStateTimestamp;
|
||||||
|
final Function setState;
|
||||||
|
|
||||||
|
const NewUserThingy({super.key, required this.player, required this.showStateTimestamp, required this.setState});
|
||||||
|
|
||||||
|
Color roleColor(String role){
|
||||||
|
switch (role){
|
||||||
|
case "sysop":
|
||||||
|
return Color.fromARGB(255, 23, 165, 133);
|
||||||
|
case "admin":
|
||||||
|
return Color.fromARGB(255, 255, 78, 138);
|
||||||
|
case "mod":
|
||||||
|
return Color.fromARGB(255, 204, 128, 242);
|
||||||
|
case "halfmod":
|
||||||
|
return Color.fromARGB(255, 95, 118, 254);
|
||||||
|
case "bot":
|
||||||
|
return Color.fromARGB(255, 60, 93, 55);
|
||||||
|
case "banned":
|
||||||
|
return Color.fromARGB(255, 248, 28, 28);
|
||||||
|
default:
|
||||||
|
return Colors.white10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final t = Translations.of(context);
|
||||||
|
return LayoutBuilder(builder: (context, constraints) {
|
||||||
|
bool bigScreen = constraints.maxWidth > 768;
|
||||||
|
double pfpHeight = 128;
|
||||||
|
int xpTableID = 0;
|
||||||
|
|
||||||
|
while (player.xp > xpTableScuffed.values.toList()[xpTableID]) {
|
||||||
|
xpTableID++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
surfaceTintColor: theme.colorScheme.surface,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
constraints: BoxConstraints(maxWidth: 960),
|
||||||
|
height: player.bannerRevision != null ? 218.0 : 138.0,
|
||||||
|
child: Stack(
|
||||||
|
//clipBehavior: Clip.none,
|
||||||
|
children: [
|
||||||
|
if (player.bannerRevision != null) Image.network(kIsWeb ? "https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioBanner&user=${player.userId}&rv=${player.bannerRevision}" : "https://tetr.io/user-content/banners/${player.userId}.jpg?rv=${player.bannerRevision}",
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
height: 120,
|
||||||
|
//width: 450,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Container();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: player.bannerRevision != null ? 90.0 : 10.0,
|
||||||
|
left: 16.0,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(1000),
|
||||||
|
child: player.role == "banned"
|
||||||
|
? Image.asset("res/avatars/tetrio_banned.png", fit: BoxFit.fitHeight, height: pfpHeight,)
|
||||||
|
: player.avatarRevision != null
|
||||||
|
? Image.network(kIsWeb ? "https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioProfilePicture&user=${player.userId}&rv=${player.avatarRevision}" : "https://tetr.io/user-content/avatars/${player.userId}.jpg?rv=${player.avatarRevision}",
|
||||||
|
// TODO: osk banner can cause memory leak
|
||||||
|
fit: BoxFit.fitHeight, height: 128, errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Image.asset("res/avatars/tetrio_anon.png", fit: BoxFit.fitHeight, height: pfpHeight);
|
||||||
|
})
|
||||||
|
: Image.asset("res/avatars/tetrio_anon.png", fit: BoxFit.fitHeight, height: pfpHeight),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: player.bannerRevision != null ? 120.0 : 40.0,
|
||||||
|
left: 160.0,
|
||||||
|
child: Text(player.username,
|
||||||
|
//softWrap: true,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: "Eurostile Round Extended",
|
||||||
|
fontSize: 28,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: player.bannerRevision != null ? 160.0 : 80.0,
|
||||||
|
left: 160.0,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
|
child: Chip(label: Text(player.role.toUpperCase(), style: TextStyle(shadows: textShadow),), padding: EdgeInsets.all(0.0), color: MaterialStatePropertyAll(roleColor(player.role))),
|
||||||
|
),
|
||||||
|
RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: TextStyle(fontFamily: "Eurostile Round"),
|
||||||
|
children:
|
||||||
|
[
|
||||||
|
if (player.friendCount > 0) WidgetSpan(child: Icon(Icons.person), alignment: PlaceholderAlignment.middle, baseline: TextBaseline.alphabetic),
|
||||||
|
if (player.friendCount > 0) TextSpan(text: "${intf.format(player.friendCount)} "),
|
||||||
|
if (player.supporterTier > 0) WidgetSpan(child: Icon(player.supporterTier > 1 ? Icons.star : Icons.star_border, color: player.supporterTier > 1 ? Colors.yellowAccent : Colors.white), alignment: PlaceholderAlignment.middle, baseline: TextBaseline.alphabetic),
|
||||||
|
if (player.supporterTier > 0) TextSpan(text: player.supporterTier.toString(), style: TextStyle(color: player.supporterTier > 1 ? Colors.yellowAccent : Colors.white)),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: player.bannerRevision != null ? 193.0 : 113.0,
|
||||||
|
left: 160.0,
|
||||||
|
child: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: TextStyle(fontFamily: "Eurostile Round"),
|
||||||
|
children: [
|
||||||
|
if (player.country != null) TextSpan(text: "${t.countries[player.country]} • "),
|
||||||
|
TextSpan(text: "${player.registrationTime == null ? t.wasFromBeginning : '${timestamp(player.registrationTime!)}'}", style: TextStyle(color: Colors.grey))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: player.bannerRevision != null ? 126.0 : 46.0,
|
||||||
|
right: 16.0,
|
||||||
|
child: RichText(
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
text: TextSpan(
|
||||||
|
style: TextStyle(fontFamily: "Eurostile Round"),
|
||||||
|
children: [
|
||||||
|
TextSpan(text: "Level ${intf.format(player.level.floor())}", recognizer: TapGestureRecognizer()..onTap = (){
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) => AlertDialog(
|
||||||
|
title: Text("Level ${intf.format(player.level.floor())}"),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: ListBody(children: [
|
||||||
|
Text(
|
||||||
|
"${NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2).format(player.xp)} XP",
|
||||||
|
style: const TextStyle(fontFamily: "Eurostile Round", fontWeight: FontWeight.bold)
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 8, 0, 8),
|
||||||
|
child: SfLinearGauge(
|
||||||
|
minimum: 0,
|
||||||
|
maximum: 1,
|
||||||
|
interval: 1,
|
||||||
|
ranges: [
|
||||||
|
LinearGaugeRange(startValue: 0, endValue: player.level - player.level.floor(), color: Colors.cyanAccent),
|
||||||
|
LinearGaugeRange(startValue: 0, endValue: (player.xp / xpTableScuffed.values.toList()[xpTableID]), color: Colors.redAccent, position: LinearElementPosition.cross)
|
||||||
|
],
|
||||||
|
showTicks: true,
|
||||||
|
showLabels: false
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text("${t.statCellNum.xpProgress}: ${((player.level - player.level.floor()) * 100).toStringAsFixed(2)} %"),
|
||||||
|
Text("${t.statCellNum.xpFrom0ToLevel(n: xpTableScuffed.keys.toList()[xpTableID])}: ${((player.xp / xpTableScuffed.values.toList()[xpTableID]) * 100).toStringAsFixed(2)} % (${NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(xpTableScuffed.values.toList()[xpTableID] - player.xp)} ${t.statCellNum.xpLeft})")
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: Text("OK"),
|
||||||
|
onPressed: () {Navigator.of(context).pop();}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
TextSpan(text:"\n"),
|
||||||
|
TextSpan(text: player.gameTime.isNegative ? "-h --m" : playtime(player.gameTime), style: TextStyle(color: player.gameTime.isNegative ? Colors.grey : Colors.white), recognizer: TapGestureRecognizer()..onTap = (){
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) => AlertDialog(
|
||||||
|
title: Text(t.exactGametime),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: ListBody(children: [
|
||||||
|
Text(
|
||||||
|
//"${intf.format(testPlayer.gameTime.inDays)} d\n${nonsecs.format(testPlayer.gameTime.inHours%24)} h\n${nonsecs.format(testPlayer.gameTime.inMinutes%60)} m\n${nonsecs.format(testPlayer.gameTime.inSeconds%60)} s\n${nonsecs3.format(testPlayer.gameTime.inMilliseconds%1000)} ms\n${nonsecs.format(testPlayer.gameTime.inMicroseconds%1000)} μs",
|
||||||
|
"${intf.format(testPlayer.gameTime.inDays)}d ${nonsecs.format(testPlayer.gameTime.inHours%24)}h ${nonsecs.format(testPlayer.gameTime.inMinutes%60)}m ${nonsecs.format(testPlayer.gameTime.inSeconds%60)}s ${nonsecs3.format(testPlayer.gameTime.inMilliseconds%1000)}ms ${nonsecs.format(testPlayer.gameTime.inMicroseconds%1000)}μs",
|
||||||
|
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 24)
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: Text("OK"),
|
||||||
|
onPressed: () {Navigator.of(context).pop();}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
TextSpan(text:"\n"),
|
||||||
|
TextSpan(text: "${player.gamesWon > -1 ? intf.format(player.gamesWon) : "---"}", style: TextStyle(color: player.gamesWon > -1 ? Colors.white : Colors.grey)),
|
||||||
|
TextSpan(text: "/${player.gamesPlayed > -1 ? intf.format(player.gamesPlayed) : "---"}", style: TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey)),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Row(
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
// children: [
|
||||||
|
// ElevatedButton.icon(onPressed: (){print("ok, and?");}, icon: Icon(Icons.person_add), label: Text(t.track), style: ButtonStyle(shape: MaterialStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(8), right: Radius.zero))))),
|
||||||
|
// ElevatedButton.icon(onPressed: (){print("ok, and?");}, icon: Icon(Icons.balance), label: Text(t.compare), style: ButtonStyle(shape: MaterialStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.zero, right: Radius.circular(8))))))
|
||||||
|
// ]
|
||||||
|
// )
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ class SingleplayerRecordView extends StatelessWidget {
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("${
|
title: Text("${
|
||||||
switch (record.endContext.gameType){
|
switch (record.gamemode){
|
||||||
"40l" => t.sprint,
|
"40l" => t.sprint,
|
||||||
"blitz" => t.blitz,
|
"blitz" => t.blitz,
|
||||||
String() => "5000000 Blast",
|
String() => "5000000 Blast",
|
||||||
|
|
|
@ -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: TLThingy(tl: widget.state.tlSeason1!, userID: widget.state.userId, states: const [], hidePreSeasonThingy: true,))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ 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))),
|
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))),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
icon: const Icon(Icons.delete_forever),
|
icon: const Icon(Icons.delete_forever),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
|
|
@ -25,7 +25,7 @@ class RecentSingleplayerGames extends StatelessWidget{
|
||||||
for(RecordSingle record in recent.records) ListTile(
|
for(RecordSingle record in recent.records) ListTile(
|
||||||
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: record))),
|
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: record))),
|
||||||
leading: Text(
|
leading: Text(
|
||||||
switch (record.endContext.gameType){
|
switch (record.gamemode){
|
||||||
"40l" => "40L",
|
"40l" => "40L",
|
||||||
"blitz" => "BLZ",
|
"blitz" => "BLZ",
|
||||||
"5mblast" => "5MB",
|
"5mblast" => "5MB",
|
||||||
|
@ -34,15 +34,15 @@ class RecentSingleplayerGames extends StatelessWidget{
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9)
|
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9)
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
switch (record.endContext.gameType){
|
switch (record.gamemode){
|
||||||
"40l" => get40lTime(record.endContext.finalTime.inMicroseconds),
|
"40l" => get40lTime(record.stats.finalTime.inMicroseconds),
|
||||||
"blitz" => t.blitzScore(p: NumberFormat.decimalPattern().format(record.endContext.score)),
|
"blitz" => t.blitzScore(p: NumberFormat.decimalPattern().format(record.stats.score)),
|
||||||
"5mblast" => get40lTime(record.endContext.finalTime.inMicroseconds),
|
"5mblast" => get40lTime(record.stats.finalTime.inMicroseconds),
|
||||||
String() => "huh",
|
String() => "huh",
|
||||||
},
|
},
|
||||||
style: const TextStyle(fontSize: 18)),
|
style: const TextStyle(fontSize: 18)),
|
||||||
subtitle: Text(timestamp(record.timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
|
subtitle: Text(timestamp(record.timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
|
||||||
trailing: SpTrailingStats(record.endContext)
|
trailing: SpTrailingStats(record.stats, record.gamemode)
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -36,16 +36,16 @@ class SingleplayerRecord extends StatelessWidget {
|
||||||
if (record == null) return Center(child: Text(t.noRecord, textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)));
|
if (record == null) return Center(child: Text(t.noRecord, textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)));
|
||||||
late MapEntry closestAverageBlitz;
|
late MapEntry closestAverageBlitz;
|
||||||
late bool blitzBetterThanClosestAverage;
|
late bool blitzBetterThanClosestAverage;
|
||||||
bool? blitzBetterThanRankAverage = (rank != null && rank != "z") ? record!.endContext.score > blitzAverages[rank]! : null;
|
bool? blitzBetterThanRankAverage = (rank != null && rank != "z") ? record!.stats.score > blitzAverages[rank]! : null;
|
||||||
late MapEntry closestAverageSprint;
|
late MapEntry closestAverageSprint;
|
||||||
late bool sprintBetterThanClosestAverage;
|
late bool sprintBetterThanClosestAverage;
|
||||||
bool? sprintBetterThanRankAverage = (rank != null && rank != "z") ? record!.endContext.finalTime < sprintAverages[rank]! : null;
|
bool? sprintBetterThanRankAverage = (rank != null && rank != "z") ? record!.stats.finalTime < sprintAverages[rank]! : null;
|
||||||
if (record!.endContext.gameType == "40l") {
|
if (record!.gamemode == "40l") {
|
||||||
closestAverageSprint = sprintAverages.entries.singleWhere((element) => element.value == sprintAverages.values.reduce((a, b) => (a-record!.endContext.finalTime).abs() < (b -record!.endContext.finalTime).abs() ? a : b));
|
closestAverageSprint = sprintAverages.entries.singleWhere((element) => element.value == sprintAverages.values.reduce((a, b) => (a-record!.stats.finalTime).abs() < (b -record!.stats.finalTime).abs() ? a : b));
|
||||||
sprintBetterThanClosestAverage = record!.endContext.finalTime < closestAverageSprint.value;
|
sprintBetterThanClosestAverage = record!.stats.finalTime < closestAverageSprint.value;
|
||||||
}else if (record!.endContext.gameType == "blitz"){
|
}else if (record!.gamemode == "blitz"){
|
||||||
closestAverageBlitz = blitzAverages.entries.singleWhere((element) => element.value == blitzAverages.values.reduce((a, b) => (a-record!.endContext.score).abs() < (b -record!.endContext.score).abs() ? a : b));
|
closestAverageBlitz = blitzAverages.entries.singleWhere((element) => element.value == blitzAverages.values.reduce((a, b) => (a-record!.stats.score).abs() < (b -record!.stats.score).abs() ? a : b));
|
||||||
blitzBetterThanClosestAverage = record!.endContext.score > closestAverageBlitz.value;
|
blitzBetterThanClosestAverage = record!.stats.score > closestAverageBlitz.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
|
@ -61,20 +61,20 @@ class SingleplayerRecord extends StatelessWidget {
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (record!.endContext.gameType == "40l") Padding(padding: const EdgeInsets.only(right: 8.0),
|
if (record!.gamemode == "40l") Padding(padding: const EdgeInsets.only(right: 8.0),
|
||||||
child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageSprint.key}.png", height: 96)
|
child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageSprint.key}.png", height: 96)
|
||||||
),
|
),
|
||||||
if (record!.endContext.gameType == "blitz") Padding(padding: const EdgeInsets.only(right: 8.0),
|
if (record!.gamemode == "blitz") Padding(padding: const EdgeInsets.only(right: 8.0),
|
||||||
child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageBlitz.key}.png", height: 96)
|
child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageBlitz.key}.png", height: 96)
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (record!.endContext.gameType == "40l" && !hideTitle) Text(t.sprint, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
if (record!.gamemode == "40l" && !hideTitle) Text(t.sprint, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
||||||
if (record!.endContext.gameType == "blitz" && !hideTitle) Text(t.blitz, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
if (record!.gamemode == "blitz" && !hideTitle) Text(t.blitz, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
||||||
RichText(text: TextSpan(
|
RichText(text: TextSpan(
|
||||||
text: record!.endContext.gameType == "40l" ? get40lTime(record!.endContext.finalTime.inMicroseconds) : NumberFormat.decimalPattern().format(record!.endContext.score),
|
text: record!.gamemode == "40l" ? get40lTime(record!.stats.finalTime.inMicroseconds) : NumberFormat.decimalPattern().format(record!.stats.score),
|
||||||
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 36 : 32, fontWeight: FontWeight.w500, color: Colors.white),
|
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 36 : 32, fontWeight: FontWeight.w500, color: Colors.white),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -82,16 +82,16 @@ class SingleplayerRecord extends StatelessWidget {
|
||||||
text: "",
|
text: "",
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
||||||
children: [
|
children: [
|
||||||
if (record!.endContext.gameType == "40l" && (rank != null && rank != "z")) TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(record!.endContext.finalTime, sprintAverages[rank]!), verdict: sprintBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
if (record!.gamemode == "40l" && (rank != null && rank != "z")) TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(record!.stats.finalTime, sprintAverages[rank]!), verdict: sprintBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
||||||
color: sprintBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
color: sprintBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
||||||
))
|
))
|
||||||
else if (record!.endContext.gameType == "40l" && (rank == null || rank == "z")) TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(record!.endContext.finalTime, closestAverageSprint.value), verdict: sprintBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageSprint.key.toUpperCase())}\n", style: TextStyle(
|
else if (record!.gamemode == "40l" && (rank == null || rank == "z")) TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(record!.stats.finalTime, closestAverageSprint.value), verdict: sprintBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageSprint.key.toUpperCase())}\n", style: TextStyle(
|
||||||
color: sprintBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
color: sprintBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
||||||
))
|
))
|
||||||
else if (record!.endContext.gameType == "blitz" && (rank != null && rank != "z")) TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(record!.endContext.score, blitzAverages[rank]!), verdict: blitzBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
else if (record!.gamemode == "blitz" && (rank != null && rank != "z")) TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(record!.stats.score, blitzAverages[rank]!), verdict: blitzBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
||||||
color: blitzBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
color: blitzBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
||||||
))
|
))
|
||||||
else if (record!.endContext.gameType == "blitz" && (rank == null || rank == "z")) TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(record!.endContext.score, closestAverageBlitz.value), verdict: blitzBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageBlitz.key.toUpperCase())}\n", style: TextStyle(
|
else if (record!.gamemode == "blitz" && (rank == null || rank == "z")) TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(record!.stats.score, closestAverageBlitz.value), verdict: blitzBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageBlitz.key.toUpperCase())}\n", style: TextStyle(
|
||||||
color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
||||||
)),
|
)),
|
||||||
if (record!.rank != null) TextSpan(text: "№${record!.rank}", style: TextStyle(color: getColorOfRank(record!.rank!))),
|
if (record!.rank != null) TextSpan(text: "№${record!.rank}", style: TextStyle(color: getColorOfRank(record!.rank!))),
|
||||||
|
@ -103,29 +103,29 @@ class SingleplayerRecord extends StatelessWidget {
|
||||||
],),
|
],),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (record!.endContext.gameType == "40l") Wrap(
|
if (record!.gamemode == "40l") Wrap(
|
||||||
alignment: WrapAlignment.spaceBetween,
|
alignment: WrapAlignment.spaceBetween,
|
||||||
spacing: 20,
|
spacing: 20,
|
||||||
children: [
|
children: [
|
||||||
StatCellNum(playerStat: record!.endContext.piecesPlaced, playerStatLabel: t.statCellNum.pieces, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
StatCellNum(playerStat: record!.stats.piecesPlaced, playerStatLabel: t.statCellNum.pieces, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||||
StatCellNum(playerStat: record!.endContext.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
StatCellNum(playerStat: record!.stats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||||
StatCellNum(playerStat: record!.endContext.kpp, playerStatLabel: t.statCellNum.kpp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
StatCellNum(playerStat: record!.stats.kpp, playerStatLabel: t.statCellNum.kpp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (record!.endContext.gameType == "blitz") Wrap(
|
if (record!.gamemode == "blitz") Wrap(
|
||||||
alignment: WrapAlignment.spaceBetween,
|
alignment: WrapAlignment.spaceBetween,
|
||||||
crossAxisAlignment: WrapCrossAlignment.start,
|
crossAxisAlignment: WrapCrossAlignment.start,
|
||||||
spacing: 20,
|
spacing: 20,
|
||||||
children: [
|
children: [
|
||||||
StatCellNum(playerStat: record!.endContext.level, playerStatLabel: t.statCellNum.level, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
StatCellNum(playerStat: record!.stats.level, playerStatLabel: t.statCellNum.level, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||||
StatCellNum(playerStat: record!.endContext.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
StatCellNum(playerStat: record!.stats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||||
StatCellNum(playerStat: record!.endContext.spp, playerStatLabel: t.statCellNum.spp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true)
|
StatCellNum(playerStat: record!.stats.spp, playerStatLabel: t.statCellNum.spp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
FinesseThingy(record?.endContext.finesse, record?.endContext.finessePercentage),
|
FinesseThingy(record?.stats.finesse, record?.stats.finessePercentage),
|
||||||
LineclearsThingy(record!.endContext.clears, record!.endContext.lines, record!.endContext.holds, record!.endContext.tSpins),
|
LineclearsThingy(record!.stats.clears, record!.stats.lines, record!.stats.holds, record!.stats.tSpins),
|
||||||
if (record!.endContext.gameType == "40l") Text("${record!.endContext.inputs} KP • ${f2.format(record!.endContext.kps)} KPS"),
|
if (record!.gamemode == "40l") Text("${record!.stats.inputs} KP • ${f2.format(record!.stats.kps)} KPS"),
|
||||||
if (record!.endContext.gameType == "blitz") Text("${record!.endContext.piecesPlaced} P • ${record!.endContext.inputs} KP • ${f2.format(record!.endContext.kpp)} KPP • ${f2.format(record!.endContext.kps)} KPS"),
|
if (record!.gamemode == "blitz") Text("${record!.stats.piecesPlaced} P • ${record!.stats.inputs} KP • ${f2.format(record!.stats.kpp)} KPP • ${f2.format(record!.stats.kps)} KPS"),
|
||||||
if (record != null) Wrap(
|
if (record != null) Wrap(
|
||||||
alignment: WrapAlignment.spaceBetween,
|
alignment: WrapAlignment.spaceBetween,
|
||||||
crossAxisAlignment: WrapCrossAlignment.start,
|
crossAxisAlignment: WrapCrossAlignment.start,
|
||||||
|
@ -141,15 +141,15 @@ class SingleplayerRecord extends StatelessWidget {
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9)
|
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9)
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
switch (stream!.records[i].endContext.gameType){
|
switch (stream!.records[i].gamemode){
|
||||||
"40l" => get40lTime(stream!.records[i].endContext.finalTime.inMicroseconds),
|
"40l" => get40lTime(stream!.records[i].stats.finalTime.inMicroseconds),
|
||||||
"blitz" => t.blitzScore(p: NumberFormat.decimalPattern().format(stream!.records[i].endContext.score)),
|
"blitz" => t.blitzScore(p: NumberFormat.decimalPattern().format(stream!.records[i].stats.score)),
|
||||||
"5mblast" => get40lTime(stream!.records[i].endContext.finalTime.inMicroseconds),
|
"5mblast" => get40lTime(stream!.records[i].stats.finalTime.inMicroseconds),
|
||||||
String() => "huh",
|
String() => "huh",
|
||||||
},
|
},
|
||||||
style: const TextStyle(fontSize: 18)),
|
style: const TextStyle(fontSize: 18)),
|
||||||
subtitle: Text(timestamp(stream!.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
|
subtitle: Text(timestamp(stream!.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
|
||||||
trailing: SpTrailingStats(stream!.records[i].endContext)
|
trailing: SpTrailingStats(stream!.records[i].stats, stream!.records[i].gamemode)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|
|
@ -3,9 +3,10 @@ import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||||
|
|
||||||
class SpTrailingStats extends StatelessWidget{
|
class SpTrailingStats extends StatelessWidget{
|
||||||
final EndContextSingle endContext;
|
final ResultsStats endContext;
|
||||||
|
final String gamemode;
|
||||||
|
|
||||||
const SpTrailingStats(this.endContext, {super.key});
|
const SpTrailingStats(this.endContext, this.gamemode, {super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -16,7 +17,7 @@ class SpTrailingStats extends StatelessWidget{
|
||||||
children: [
|
children: [
|
||||||
Text("${endContext.piecesPlaced} P, ${f2.format(endContext.pps)} PPS", style: style, textAlign: TextAlign.right),
|
Text("${endContext.piecesPlaced} P, ${f2.format(endContext.pps)} PPS", style: style, textAlign: TextAlign.right),
|
||||||
Text("${intf.format(endContext.finessePercentage*100)}% F, ${endContext.finesse?.faults} FF", style: style, textAlign: TextAlign.right),
|
Text("${intf.format(endContext.finessePercentage*100)}% F, ${endContext.finesse?.faults} FF", style: style, textAlign: TextAlign.right),
|
||||||
Text(switch(endContext.gameType){
|
Text(switch(gamemode){
|
||||||
"40l" => "${f2.format(endContext.kps)} KPS, ${f2.format(endContext.kpp)} KPP",
|
"40l" => "${f2.format(endContext.kps)} KPS, ${f2.format(endContext.kpp)} KPP",
|
||||||
"blitz" => "${intf.format(endContext.spp)} SPP, lvl ${endContext.level}",
|
"blitz" => "${intf.format(endContext.spp)} SPP, lvl ${endContext.level}",
|
||||||
"5mblast" => "${intf.format(endContext.spp)} SPP, ${endContext.lines} L",
|
"5mblast" => "${intf.format(endContext.spp)} SPP, ${endContext.lines} L",
|
||||||
|
|
|
@ -27,9 +27,9 @@ class TLRatingThingy extends StatelessWidget{
|
||||||
List<String> formatedGlicko = f4.format(tlData.glicko).split(decimalSeparator);
|
List<String> formatedGlicko = f4.format(tlData.glicko).split(decimalSeparator);
|
||||||
List<String> formatedPercentile = f4.format(tlData.percentile * 100).split(decimalSeparator);
|
List<String> formatedPercentile = f4.format(tlData.percentile * 100).split(decimalSeparator);
|
||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
bool beforeS1end = now.isBefore(seasonEnd);
|
//bool beforeS1end = now.isBefore(seasonEnd);
|
||||||
int daysLeft = seasonEnd.difference(now).inDays;
|
//int daysLeft = seasonEnd.difference(now).inDays;
|
||||||
int safeRD = min(100, (100 + ((tlData.rd! >= 100 && tlData.decaying) ? 7 : max(0, 7 - (lastMatchPlayed != null ? now.difference(lastMatchPlayed!).inDays : 7))) - daysLeft).toInt());
|
//int safeRD = min(100, (100 + ((tlData.rd! >= 100 && tlData.decaying) ? 7 : max(0, 7 - (lastMatchPlayed != null ? now.difference(lastMatchPlayed!).inDays : 7))) - daysLeft).toInt());
|
||||||
return Wrap(
|
return Wrap(
|
||||||
direction: Axis.horizontal,
|
direction: Axis.horizontal,
|
||||||
alignment: WrapAlignment.spaceAround,
|
alignment: WrapAlignment.spaceAround,
|
||||||
|
@ -91,7 +91,7 @@ class TLRatingThingy extends StatelessWidget{
|
||||||
TextSpan(text: " • ${prefs.getInt("ratingMode") == 1 ? "${f2.format(tlData.rating)} TR • RD: " : "Glicko: ${f2.format(tlData.glicko!)}±"}"),
|
TextSpan(text: " • ${prefs.getInt("ratingMode") == 1 ? "${f2.format(tlData.rating)} TR • RD: " : "Glicko: ${f2.format(tlData.glicko!)}±"}"),
|
||||||
TextSpan(text: f2.format(tlData.rd!), style: tlData.decaying ? TextStyle(color: tlData.rd! > 98 ? Colors.red : Colors.yellow) : null),
|
TextSpan(text: f2.format(tlData.rd!), style: tlData.decaying ? TextStyle(color: tlData.rd! > 98 ? Colors.red : Colors.yellow) : null),
|
||||||
if (tlData.decaying) WidgetSpan(child: Icon(Icons.trending_up, color: tlData.rd! > 98 ? Colors.red : Colors.yellow,), alignment: PlaceholderAlignment.middle, baseline: TextBaseline.alphabetic),
|
if (tlData.decaying) WidgetSpan(child: Icon(Icons.trending_up, color: tlData.rd! > 98 ? Colors.red : Colors.yellow,), alignment: PlaceholderAlignment.middle, baseline: TextBaseline.alphabetic),
|
||||||
if (beforeS1end) tlData.rd! <= safeRD ? TextSpan(text: " (Safe)", style: TextStyle(color: Colors.greenAccent)) : TextSpan(text: " (> ${safeRD} RD !!!)", style: TextStyle(color: Colors.redAccent))
|
//if (beforeS1end) tlData.rd! <= safeRD ? TextSpan(text: " (Safe)", style: TextStyle(color: Colors.greenAccent)) : TextSpan(text: " (> ${safeRD} RD !!!)", style: TextStyle(color: Colors.redAccent))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,9 +2,11 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||||
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
||||||
import 'package:tetra_stats/gen/strings.g.dart';
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
|
import 'package:tetra_stats/main.dart';
|
||||||
import 'package:tetra_stats/utils/colors_functions.dart';
|
import 'package:tetra_stats/utils/colors_functions.dart';
|
||||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||||
import 'package:tetra_stats/utils/relative_timestamps.dart';
|
import 'package:tetra_stats/utils/relative_timestamps.dart';
|
||||||
|
@ -24,6 +26,7 @@ class TLThingy extends StatefulWidget {
|
||||||
final List<TetrioPlayer> states;
|
final List<TetrioPlayer> states;
|
||||||
final bool showTitle;
|
final bool showTitle;
|
||||||
final bool bot;
|
final bool bot;
|
||||||
|
final bool hidePreSeasonThingy;
|
||||||
final bool guest;
|
final bool guest;
|
||||||
final double? topTR;
|
final double? topTR;
|
||||||
final PlayerLeaderboardPosition? lbPositions;
|
final PlayerLeaderboardPosition? lbPositions;
|
||||||
|
@ -35,7 +38,7 @@ class TLThingy extends StatefulWidget {
|
||||||
final double? nextRankCutoffGlicko;
|
final double? nextRankCutoffGlicko;
|
||||||
final double? nextRankTarget;
|
final double? nextRankTarget;
|
||||||
final DateTime? lastMatchPlayed;
|
final DateTime? lastMatchPlayed;
|
||||||
const TLThingy({super.key, required this.tl, required this.userID, required this.states, this.showTitle = true, this.bot=false, this.guest=false, this.topTR, this.lbPositions, this.averages, this.nextRankCutoff, this.thatRankCutoff, this.thatRankCutoffGlicko, this.nextRankCutoffGlicko, this.nextRankTarget, this.thatRankTarget, this.lastMatchPlayed});
|
const TLThingy({super.key, required this.tl, required this.userID, required this.states, this.showTitle = true, this.bot=false, this.guest=false, this.hidePreSeasonThingy=false, this.topTR, this.lbPositions, this.averages, this.nextRankCutoff, this.thatRankCutoff, this.thatRankCutoffGlicko, this.nextRankCutoffGlicko, this.nextRankTarget, this.thatRankTarget, this.lastMatchPlayed});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<TLThingy> createState() => _TLThingyState();
|
State<TLThingy> createState() => _TLThingyState();
|
||||||
|
@ -48,7 +51,8 @@ class _TLThingyState extends State<TLThingy> with TickerProviderStateMixin {
|
||||||
late RangeValues _currentRangeValues;
|
late RangeValues _currentRangeValues;
|
||||||
late List<TetrioPlayer> sortedStates;
|
late List<TetrioPlayer> sortedStates;
|
||||||
late Timer _countdownTimer;
|
late Timer _countdownTimer;
|
||||||
Duration seasonLeft = seasonEnd.difference(DateTime.now());
|
//Duration seasonLeft = seasonEnd.difference(DateTime.now());
|
||||||
|
Duration postSeasonLeft = seasonStart.difference(DateTime.now());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -61,7 +65,8 @@ class _TLThingyState extends State<TLThingy> with TickerProviderStateMixin {
|
||||||
Durations.extralong4,
|
Durations.extralong4,
|
||||||
(Timer timer) {
|
(Timer timer) {
|
||||||
setState(() {
|
setState(() {
|
||||||
seasonLeft = seasonEnd.difference(DateTime.now());
|
//seasonLeft = seasonEnd.difference(DateTime.now());
|
||||||
|
postSeasonLeft = seasonStart.difference(DateTime.now());
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -80,6 +85,47 @@ class _TLThingyState extends State<TLThingy> with TickerProviderStateMixin {
|
||||||
String decimalSeparator = f2.symbols.DECIMAL_SEP;
|
String decimalSeparator = f2.symbols.DECIMAL_SEP;
|
||||||
List<String> estTRformated = currentTl.estTr != null ? f2.format(currentTl.estTr!.esttr).split(decimalSeparator) : [];
|
List<String> estTRformated = currentTl.estTr != null ? f2.format(currentTl.estTr!.esttr).split(decimalSeparator) : [];
|
||||||
List<String> estTRaccFormated = currentTl.esttracc != null ? intFDiff.format(currentTl.esttracc!).split(".") : [];
|
List<String> estTRaccFormated = currentTl.esttracc != null ? intFDiff.format(currentTl.esttracc!).split(".") : [];
|
||||||
|
if (DateTime.now().isBefore(seasonStart) && !widget.hidePreSeasonThingy) {
|
||||||
|
return Center(child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(t.postSeason.toUpperCase(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28), textAlign: TextAlign.center),
|
||||||
|
Text(t.seasonStarts, textAlign: TextAlign.center),
|
||||||
|
const Spacer(),
|
||||||
|
Text(countdown(postSeasonLeft), textAlign: TextAlign.center, style: const TextStyle(fontSize: 36.0),),
|
||||||
|
if (prefs.getBool("hideDanMessadge") != true) const Spacer(),
|
||||||
|
if (prefs.getBool("hideDanMessadge") != true) Card(
|
||||||
|
child: Container(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 450.0),
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
t.myMessadgeHeader,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, fontWeight: FontWeight.bold)
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
IconButton(onPressed: (){setState(() {
|
||||||
|
prefs.setBool("hideDanMessadge", true);
|
||||||
|
});}, icon: const Icon(Icons.close))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text(t.myMessadgeBody, textAlign: TextAlign.center),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: Text(t.preSeasonMessage(n: postSeasonLeft.inDays >= 14 ? "1" : "2"), textAlign: TextAlign.center),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
if (currentTl.gamesPlayed == 0) return Center(child: Text(widget.guest ? t.anonTL : widget.bot ? t.botTL : t.neverPlayedTL, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28), textAlign: TextAlign.center,));
|
if (currentTl.gamesPlayed == 0) return Center(child: Text(widget.guest ? t.anonTL : widget.bot ? t.botTL : t.neverPlayedTL, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28), textAlign: TextAlign.center,));
|
||||||
return LayoutBuilder(builder: (context, constraints) {
|
return LayoutBuilder(builder: (context, constraints) {
|
||||||
bool bigScreen = constraints.maxWidth >= 768;
|
bool bigScreen = constraints.maxWidth >= 768;
|
||||||
|
@ -90,8 +136,8 @@ class _TLThingyState extends State<TLThingy> with TickerProviderStateMixin {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
if (widget.showTitle) Text(t.tetraLeague, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
if (widget.showTitle) Text(t.tetraLeague, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
||||||
if (DateTime.now().isBefore(seasonEnd)) Text(t.seasonEnds(countdown: countdown(seasonLeft)))
|
//if (DateTime.now().isBefore(seasonEnd)) Text(t.seasonEnds(countdown: countdown(seasonLeft)))
|
||||||
else Text(t.seasonEnded),
|
//else Text(t.seasonEnded),
|
||||||
if (oldTl != null) Text(t.comparingWith(newDate: timestamp(currentTl.timestamp), oldDate: timestamp(oldTl!.timestamp)),
|
if (oldTl != null) Text(t.comparingWith(newDate: timestamp(currentTl.timestamp), oldDate: timestamp(oldTl!.timestamp)),
|
||||||
textAlign: TextAlign.center,),
|
textAlign: TextAlign.center,),
|
||||||
if (oldTl != null) RangeSlider(values: _currentRangeValues, max: widget.states.length.toDouble(),
|
if (oldTl != null) RangeSlider(values: _currentRangeValues, max: widget.states.length.toDouble(),
|
||||||
|
@ -105,7 +151,7 @@ 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].tlSeason1!;
|
||||||
}
|
}
|
||||||
if (values.end.round() == 0){
|
if (values.end.round() == 0){
|
||||||
oldTl = widget.tl;
|
oldTl = widget.tl;
|
||||||
|
|
|
@ -87,6 +87,12 @@
|
||||||
"verdictBetter": "better",
|
"verdictBetter": "better",
|
||||||
"verdictWorse": "worse",
|
"verdictWorse": "worse",
|
||||||
"smooth": "Smooth",
|
"smooth": "Smooth",
|
||||||
|
"postSeason": "Off-season",
|
||||||
|
"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 against absolutely random player.\nSeason ${n} rules applied",
|
||||||
|
"nanow": "Not avaliable for now...",
|
||||||
"seasonEnds": "Season ends in ${countdown}",
|
"seasonEnds": "Season ends in ${countdown}",
|
||||||
"seasonEnded": "Season has ended",
|
"seasonEnded": "Season has ended",
|
||||||
"gamesUntilRanked": "${left} games until being ranked",
|
"gamesUntilRanked": "${left} games until being ranked",
|
||||||
|
|
|
@ -87,6 +87,12 @@
|
||||||
"verdictBetter": "Лучше",
|
"verdictBetter": "Лучше",
|
||||||
"verdictWorse": "Хуже",
|
"verdictWorse": "Хуже",
|
||||||
"smooth": "Гладкий",
|
"smooth": "Гладкий",
|
||||||
|
"postSeason": "Внесезонье",
|
||||||
|
"seasonStarts": "Сезон начнётся через:",
|
||||||
|
"myMessadgeHeader": "Сообщение от dan63",
|
||||||
|
"myMessadgeBody": "TETR.IO Tetra Channel API был серьёзно изменён после последнего обновления, поэтому некоторый функционал может не работать. Я постараюсь добавить новую статистику (и вернуть старую) как только будут опубликована новая документация по данному API.",
|
||||||
|
"preSeasonMessage": "Прямо сейчас вы можете сыграть безранговый матч до трёх побед против абсолютно рандомного по скиллу игрока.\nПрименяются правила ${n} сезона",
|
||||||
|
"nanow": "Пока недоступно...",
|
||||||
"seasonEnds": "Сезон закончится через ${countdown}",
|
"seasonEnds": "Сезон закончится через ${countdown}",
|
||||||
"seasonEnded": "Сезон закончился",
|
"seasonEnded": "Сезон закончился",
|
||||||
"gamesUntilRanked": "${left} матчей до получения рейтинга",
|
"gamesUntilRanked": "${left} матчей до получения рейтинга",
|
||||||
|
|
Loading…
Reference in New Issue