Compare commits

...

3 Commits

17 changed files with 1143 additions and 1184 deletions

File diff suppressed because it is too large Load Diff

View File

@ -897,11 +897,11 @@ class TetraLeagueAlphaRecord{
TetraLeagueAlphaRecord({required this.replayId, required this.ownId, required this.timestamp, required this.endContext, required this.replayAvalable});
TetraLeagueAlphaRecord.fromJson(Map<String, dynamic> json) {
ownId = json['_id'];
endContext = [EndContextMulti.fromJson(json['endcontext'][0]), EndContextMulti.fromJson(json['endcontext'][1])];
replayId = json['replayid'];
ownId = json['_id']??replayId;
timestamp = DateTime.parse(json['ts']);
replayAvalable = true;
replayAvalable = ownId != replayId;
}
Map<String, dynamic> toJson() {
@ -973,19 +973,19 @@ class EndContextMulti {
EndContextMulti.fromJson(Map<String, dynamic> json) {
userId = json['id'] ?? json['user']['_id'];
username = json['username'] ?? json['user']['username'];
handling = Handling.fromJson(json['handling']);
handling = json['handling'] != null ? Handling.fromJson(json['handling']) : Handling(arr: -1, das: -1, sdf: -1, dcd: 0, cancel: true, safeLock: true);
success = json['success'];
inputs = json['inputs'];
piecesPlaced = json['piecesplaced'];
inputs = json['inputs'] ?? -1;
piecesPlaced = json['piecesplaced'] ?? -1;
naturalOrder = json['naturalorder'];
wins = json['wins'];
points = json['points']['primary'];
secondary = json['points']['secondary'].toDouble();
tertiary = json['points']['tertiary'].toDouble();
secondaryTracking = json['points']['secondaryAvgTracking'].map((e) => e.toDouble()).toList();
tertiaryTracking = json['points']['tertiaryAvgTracking'].map((e) => e.toDouble()).toList();
secondaryTracking = json['points']['secondaryAvgTracking'] != null ? json['points']['secondaryAvgTracking'].map((e) => e.toDouble()).toList() : [];
tertiaryTracking = json['points']['tertiaryAvgTracking'] != null ? json['points']['tertiaryAvgTracking'].map((e) => e.toDouble()).toList() : [];
extra = json['points']['extra']['vs'].toDouble();
extraTracking = json['points']['extraAvgTracking']['aggregatestats___vsscore'].map((e) => e.toDouble()).toList();
extraTracking = json['points']['extraAvgTracking'] != null ? json['points']['extraAvgTracking']['aggregatestats___vsscore'].map((e) => e.toDouble()).toList() : [];
nerdStats = NerdStats(secondary, tertiary, extra);
nerdStatsTracking = [for (int i = 0; i < secondaryTracking.length; i++) NerdStats(secondaryTracking[i], tertiaryTracking[i], extraTracking[i])];
estTr = EstTr(secondary, tertiary, extra, nerdStats.app, nerdStats.dss, nerdStats.dsp, nerdStats.gbe);

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
/// Locales: 2
/// Strings: 1182 (591 per locale)
///
/// Built on 2024-06-16 at 21:03 UTC
/// Built on 2024-07-10 at 15:23 UTC
// coverage:ignore-file
// ignore_for_file: type=lint
@ -321,7 +321,7 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
String get fromBeginning => 'From beginning';
String get calc => 'Calc';
String get calcViewNoValues => 'Enter values to calculate the stats';
String get rankAveragesViewTitle => 'Ranks cutoff and average stats';
String get rankAveragesViewTitle => 'Ranks cutoffs';
String get sprintAndBlitsViewTitle => '40 lines and Blitz averages';
String sprintAndBlitsRelevance({required Object date}) => 'Relevance: ${date}';
String get rank => 'Rank';
@ -1016,7 +1016,7 @@ class _StringsRu implements Translations {
@override String get fromBeginning => 'С начала';
@override String get calc => 'Считать';
@override String get calcViewNoValues => 'Введите значения, чтобы посчитать статистику';
@override String get rankAveragesViewTitle => 'Требования рангов и средние значения';
@override String get rankAveragesViewTitle => 'Требования рангов';
@override String get sprintAndBlitsViewTitle => 'Средние результаты 40 линий и блица';
@override String sprintAndBlitsRelevance({required Object date}) => 'Актуальность: ${date}';
@override String get rank => 'Ранг';
@ -1703,7 +1703,7 @@ extension on Translations {
case 'fromBeginning': return 'From beginning';
case 'calc': return 'Calc';
case 'calcViewNoValues': return 'Enter values to calculate the stats';
case 'rankAveragesViewTitle': return 'Ranks cutoff and average stats';
case 'rankAveragesViewTitle': return 'Ranks cutoffs';
case 'sprintAndBlitsViewTitle': return '40 lines and Blitz averages';
case 'sprintAndBlitsRelevance': return ({required Object date}) => 'Relevance: ${date}';
case 'rank': return 'Rank';
@ -2314,7 +2314,7 @@ extension on _StringsRu {
case 'fromBeginning': return 'С начала';
case 'calc': return 'Считать';
case 'calcViewNoValues': return 'Введите значения, чтобы посчитать статистику';
case 'rankAveragesViewTitle': return 'Требования рангов и средние значения';
case 'rankAveragesViewTitle': return 'Требования рангов';
case 'sprintAndBlitsViewTitle': return 'Средние результаты 40 линий и блица';
case 'sprintAndBlitsRelevance': return ({required Object date}) => 'Актуальность: ${date}';
case 'rank': return 'Ранг';

View File

@ -606,7 +606,7 @@ class TetrioService extends DB {
if (kIsWeb) {
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLMatches", "user": userID});
} else {
url = Uri.https('api.p1nkl0bst3r.xyz', 'tlmatches/$userID');
url = Uri.https('api.p1nkl0bst3r.xyz', 'tlmatches/$userID', {"before": "0", "count": "9000"});
}
try{
@ -614,62 +614,9 @@ class TetrioService extends DB {
switch (response.statusCode) {
case 200:
// that one api returns csv instead of json
List<List<dynamic>> csv = const CsvToListConverter().convert(response.body)..removeAt(0);
List<TetraLeagueAlphaRecord> matches = [];
// parsing data into TetraLeagueAlphaRecord objects
for (var entry in csv){
TetraLeagueAlphaRecord match = TetraLeagueAlphaRecord(
replayId: entry[0].toString(),
ownId: entry[0].toString(), // i gonna disting p1nkl0bst3r entries with it
timestamp: DateTime.parse(entry[1]),
endContext: [
EndContextMulti(
userId: entry[2].toString(),
username: entry[3].toString(),
naturalOrder: 0,
inputs: -1,
piecesPlaced: -1,
handling: Handling(arr: -1, das: -1, sdf: -1, dcd: 0, cancel: true, safeLock: true),
points: entry[4],
wins: entry[4],
secondary: entry[6],
secondaryTracking: [],
tertiary: entry[5],
tertiaryTracking: [],
extra: entry[7],
extraTracking: [],
success: true
),
EndContextMulti(
userId: entry[8].toString(),
username: entry[9].toString(),
naturalOrder: 1,
inputs: -1,
piecesPlaced: -1,
handling: Handling(arr: -1, das: -1, sdf: -1, dcd: 0, cancel: true, safeLock: true),
points: entry[10],
wins: entry[10],
secondary: entry[12],
secondaryTracking: [],
tertiary: entry[11],
tertiaryTracking: [],
extra: entry[13],
extraTracking: [],
success: false
)
],
replayAvalable: false
);
matches.add(match);
}
// trying to dump it to local DB
TetraLeagueAlphaStream fakeStream = TetraLeagueAlphaStream(userId: userID, records: matches);
saveTLMatchesFromStream(fakeStream);
return matches;
TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(jsonDecode(response.body)['data']['records'], userID);
saveTLMatchesFromStream(stream);
return stream.records;
case 404:
developer.log("fetchAndSaveOldTLmatches: Probably, history doesn't exist", name: "services/tetrio_crud", error: response.statusCode);
throw TetrioHistoryNotExist();

View File

@ -306,7 +306,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
});
}
return [me, records, states, tlMatches, compareWith, isTracking, news, topTR, recent, sprint, blitz];
return [me, records, states, tlMatches, compareWith, isTracking, news, topTR, recent, sprint, blitz, tlMatches.elementAtOrNull(0)?.timestamp];
}
/// Triggers widgets rebuild
@ -459,6 +459,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
userID: snapshot.data![0].userId,
states: snapshot.data![2],
topTR: snapshot.data![7]?.tr,
lastMatchPlayed: snapshot.data![11],
bot: snapshot.data![0].role == "bot",
guest: snapshot.data![0].role == "anon",
thatRankCutoff: thatRankCutoff,
@ -1084,7 +1085,7 @@ class _TwoRecordsThingy extends StatelessWidget {
if (sprint != null) FinesseThingy(sprint?.endContext.finesse, sprint?.endContext.finessePercentage),
if (sprint != null) LineclearsThingy(sprint!.endContext.clears, sprint!.endContext.lines, sprint!.endContext.holds, sprint!.endContext.tSpins),
if (sprint != null) Text("${sprint!.endContext.inputs} KP • ${f2.format(sprint!.endContext.kps)} KPS"),
Wrap(
if (sprint != null) Wrap(
alignment: WrapAlignment.spaceBetween,
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 20,
@ -1170,7 +1171,7 @@ class _TwoRecordsThingy extends StatelessWidget {
if (blitz != null) FinesseThingy(blitz?.endContext.finesse, blitz?.endContext.finessePercentage),
if (blitz != null) LineclearsThingy(blitz!.endContext.clears, blitz!.endContext.lines, blitz!.endContext.holds, blitz!.endContext.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"),
Wrap(
if (blitz != null) Wrap(
alignment: WrapAlignment.spaceBetween,
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 20,

View File

@ -49,14 +49,13 @@ class RanksAverages extends State<RankAveragesView> {
child: averages.isEmpty ? const Center(child: Text('Fetching...')) : ListView.builder(
itemCount: averages.length,
itemBuilder: (context, index){
bool bigScreen = MediaQuery.of(context).size.width > 768;
List<String> keys = averages.keys.toList();
return ListTile(
leading: Image.asset("res/tetrio_tl_alpha_ranks/${keys[index]}.png", height: 48),
title: Text(t.players(n: averages[keys[index]]?[1]["players"]), style: const TextStyle(fontFamily: "Eurostile Round Extended")),
subtitle: Text("${f2.format(averages[keys[index]]?[0].apm)} APM, ${f2.format(averages[keys[index]]?[0].pps)} PPS, ${f2.format(averages[keys[index]]?[0].vs)} VS, ${f2.format(averages[keys[index]]?[0].nerdStats.app)} APP, ${f2.format(averages[keys[index]]?[0].nerdStats.vsapm)} VS/APM",
style: const TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey)),
trailing: Text("${f2.format(averages[keys[index]]?[1]["toEnterTR"])} TR", style: bigScreen ? const TextStyle(fontSize: 28) : null),
style: const TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey, fontSize: 13)),
trailing: Text("${f2.format(averages[keys[index]]?[1]["toEnterTR"])} TR", style: const TextStyle(fontSize: 28, fontFamily: "Eurostile Round")),
onTap: (){
if (averages[keys[index]]?[1]["players"] > 0) {
Navigator.push(

View File

@ -288,13 +288,13 @@ class SettingsState extends State<SettingsView> {
subtitle: Text(t.aboutAppText(appName: packageInfo.appName, packageName: packageInfo.packageName, version: packageInfo.version, buildNumber: packageInfo.buildNumber)),
trailing: const Icon(Icons.arrow_right)
),
Wrap(
alignment: WrapAlignment.center,
spacing: 8,
children: [
TextButton(child: Text("Donate to me"), onPressed: (){},),TextButton(child: Text("Donate to NOT me"), onPressed: (){},),TextButton(child: Text("Donate to someone else"), onPressed: (){},),
],
),
// Wrap(
// alignment: WrapAlignment.center,
// spacing: 8,
// children: [
// TextButton(child: Text("Donate to me"), onPressed: (){},),TextButton(child: Text("Donate to NOT me"), onPressed: (){},),TextButton(child: Text("Donate to someone else"), onPressed: (){},),
// ],
// ),
],
)),
);

View File

@ -58,7 +58,7 @@ class TLLeaderboardState extends State<TLLeaderboardView> {
);
},
icon: const Icon(Icons.compress),
tooltip: t.averages,
tooltip: t.rankAveragesViewTitle,
),
],
),
@ -102,11 +102,11 @@ class TLLeaderboardState extends State<TLLeaderboardView> {
),
TextButton(onPressed: (){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RankView(rank: snapshot.data!.getAverageOfRank("")),
),
);
context,
MaterialPageRoute(
builder: (context) => RankView(rank: snapshot.data!.getAverageOfRank("")),
),
);
}, child: Text(t.everyoneAverages,
style: const TextStyle(fontSize: 25)))
],)
@ -184,12 +184,12 @@ class TLLeaderboardState extends State<TLLeaderboardView> {
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 28 : 24, height: 0.9)
),
title: Text(allPlayers[index].username, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round Extended" : "Eurostile Round", height: 0.9)),
subtitle: Text(_sortBy == Stats.tr ? "${f2.format(allPlayers[index].apm)} APM, ${f2.format(allPlayers[index].pps)} PPS, ${f2.format(allPlayers[index].vs)} VS, ${f2.format(allPlayers[index].nerdStats.app)} APP, ${f2.format(allPlayers[index].nerdStats.vsapm)} VS/APM" : "${_f4.format(allPlayers[index].getStatByEnum(_sortBy))} ${chartsShortTitles[_sortBy]}",
style: TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: bigScreen ? null : 12, color: _sortBy == Stats.tr ? Colors.grey : null)),
subtitle: (bigScreen || _sortBy != Stats.tr) ? Text(_sortBy == Stats.tr ? "${f2.format(allPlayers[index].apm)} APM, ${f2.format(allPlayers[index].pps)} PPS, ${f2.format(allPlayers[index].vs)} VS, ${f2.format(allPlayers[index].nerdStats.app)} APP, ${f2.format(allPlayers[index].nerdStats.vsapm)} VS/APM" : "${_f4.format(allPlayers[index].getStatByEnum(_sortBy))} ${chartsShortTitles[_sortBy]}",
style: TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: bigScreen ? null : 13, color: _sortBy == Stats.tr ? Colors.grey : null)) : null,
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text("${f2.format(allPlayers[index].rating)} TR", style: TextStyle(fontSize: bigScreen ? 28 : 22)),
Text("${f2.format(allPlayers[index].rating)} TR", style: const TextStyle(fontSize: 28)),
Image.asset("res/tetrio_tl_alpha_ranks/${allPlayers[index].rank}.png", height: bigScreen ? 48 : 36),
],
),

View File

@ -269,7 +269,7 @@ class TlMatchResultState extends State<TlMatchResultView> {
label: "Sent", higherIsBetter: true),
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].garbage.recived : snapshot.data!.stats[roundSelector][greenSidePlayer].garbage.recived,
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].garbage.recived : snapshot.data!.stats[roundSelector][redSidePlayer].garbage.recived,
label: "Recived", higherIsBetter: true),
label: "Received", higherIsBetter: true),
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].garbage.attack : snapshot.data!.stats[roundSelector][greenSidePlayer].garbage.attack,
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].garbage.attack : snapshot.data!.stats[roundSelector][redSidePlayer].garbage.attack,
label: "Attack", higherIsBetter: true),

View File

@ -126,7 +126,7 @@ class SingleplayerRecord extends StatelessWidget {
LineclearsThingy(record!.endContext.clears, record!.endContext.lines, record!.endContext.holds, record!.endContext.tSpins),
if (record!.endContext.gameType == "40l") Text("${record!.endContext.inputs} KP • ${f2.format(record!.endContext.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"),
Wrap(
if (record != null) Wrap(
alignment: WrapAlignment.spaceBetween,
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 20,

View File

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart';
@ -6,14 +8,16 @@ import 'package:tetra_stats/main.dart' show prefs;
import 'package:tetra_stats/utils/numers_formats.dart';
var fDiff = NumberFormat("+#,###.####;-#,###.####");
DateTime seasonEnd = DateTime.utc(2024, 07, 26);
class TLRatingThingy extends StatelessWidget{
final String userID;
final TetraLeagueAlpha tlData;
final TetraLeagueAlpha? oldTl;
final double? topTR;
final DateTime? lastMatchPlayed;
const TLRatingThingy({super.key, required this.userID, required this.tlData, this.oldTl, this.topTR});
const TLRatingThingy({super.key, required this.userID, required this.tlData, this.oldTl, this.topTR, this.lastMatchPlayed});
@override
Widget build(BuildContext context) {
@ -23,6 +27,11 @@ class TLRatingThingy extends StatelessWidget{
List<String> formatedTR = f4.format(tlData.rating).split(decimalSeparator);
List<String> formatedGlicko = f4.format(tlData.glicko).split(decimalSeparator);
List<String> formatedPercentile = f4.format(tlData.percentile * 100).split(decimalSeparator);
DateTime now = DateTime.now();
bool beforeS1end = now.isBefore(seasonEnd);
int daysLeft = seasonEnd.difference(now).inDays;
print(max(0, 7 - (lastMatchPlayed != null ? now.difference(lastMatchPlayed!).inDays : 7)));
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(
direction: Axis.horizontal,
alignment: WrapAlignment.spaceAround,
@ -83,7 +92,8 @@ class TLRatingThingy extends StatelessWidget{
if (topTR != null) TextSpan(text: " (${f2.format(topTR)} TR)"),
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),
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))
],
),
),

View File

@ -31,7 +31,8 @@ class TLThingy extends StatefulWidget {
final double? nextRankCutoff;
final double? nextRankCutoffGlicko;
final double? nextRankTarget;
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});
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});
@override
State<TLThingy> createState() => _TLThingyState();
@ -92,7 +93,7 @@ class _TLThingyState extends State<TLThingy> {
});
},
),
if (currentTl.gamesPlayed >= 10) TLRatingThingy(userID: widget.userID, tlData: currentTl, oldTl: oldTl, topTR: widget.topTR),
if (currentTl.gamesPlayed > 9) TLRatingThingy(userID: widget.userID, tlData: currentTl, oldTl: oldTl, topTR: widget.topTR, lastMatchPlayed: widget.lastMatchPlayed),
if (currentTl.gamesPlayed > 9) TLProgress(
tlData: currentTl,
previousRankTRcutoff: widget.thatRankCutoff,

View File

@ -2,7 +2,7 @@ name: tetra_stats
description: Track your and other player stats in TETR.IO
publish_to: 'none'
version: 1.6.0+20
version: 1.6.1+21
environment:
sdk: '>=3.0.0'

View File

@ -186,7 +186,7 @@
"fromBeginning": "From beginning",
"calc": "Calc",
"calcViewNoValues": "Enter values to calculate the stats",
"rankAveragesViewTitle": "Ranks cutoff and average stats",
"rankAveragesViewTitle": "Ranks cutoffs",
"sprintAndBlitsViewTitle": "40 lines and Blitz averages",
"sprintAndBlitsRelevance": "Relevance: ${date}",
"rank": "Rank",

View File

@ -186,7 +186,7 @@
"fromBeginning": "С начала",
"calc": "Считать",
"calcViewNoValues": "Введите значения, чтобы посчитать статистику",
"rankAveragesViewTitle": "Требования рангов и средние значения",
"rankAveragesViewTitle": "Требования рангов",
"sprintAndBlitsViewTitle": "Средние результаты 40 линий и блица",
"sprintAndBlitsRelevance": "Актуальность: ${date}",
"rank": "Ранг",

View File

@ -150,7 +150,7 @@
let tip = document.querySelector("#tip");
const tips = [
// Promoting Tetra Stats "native"
"Want a better perfomance?<br><a href=\"ya.ru\">Try out Tetra Stats Native</a>",
"Want a better perfomance?<br><a href=\"https://github.com/dan63047/TetraStats/releases\">Try out Tetra Stats \"Native\"</a>",
"Imagine a world, where Tetra Stats was written in JS",
"Welcome to fullscreen canvas",
@ -172,6 +172,7 @@
let appRunner = await engineInitializer.initializeEngine();
await appRunner.runApp();
preloader.classList.add("hidden");
tip.classList.add("hidden");
}
});
} catch (e){