Damage calc update, displaying progress of munching on main view

This commit is contained in:
2025-06-09 23:44:29 +03:00
parent a3ca4e6b57
commit 22286938a2
7 changed files with 897 additions and 745 deletions
+3 -3
View File
@@ -1,4 +1,4 @@
flutter_app:
flutter_app:
command: tetra_stats
arch: x64
parent: /usr/local/lib
@@ -6,10 +6,10 @@ flutter_app:
control:
Package: tetra-stats
Version: 2.0.8
Version: 2.1.0
Architecture: amd64
Essential: no
Priority: optional
Depends:
Maintainer: dan63047
Description: Track your and other player stats in TETR.IO
Description: Track your and other player stats in TETR.IO
+2 -2
View File
@@ -1,9 +1,9 @@
[Desktop Entry]
Version=2.0.8
Version=2.1.0
Name=Tetra Stats
GenericName=Tetra Stats
Comment=Track your and other player stats in TETR.IO
Terminal=false
Type=Application
Categories=Utility
Keywords=TETR.IO;tetrio;stats;
Keywords=TETR.IO;tetrio;stats;
+75 -95
View File
@@ -14,58 +14,20 @@ const double vsapmWeight = 60;
const double cheeseWeight = 1.25;
const double gbeWeight = 315;
const Map<int, double> xpTableScuffed = { // level: xp required
05000: 67009018.4885772,
10000: 763653437.386,
15000: 2337651144.54149,
20000: 4572735210.50902,
25000: 7376166347.04745,
const Map<int, double> xpTableScuffed = {
// level: xp required
05000: 67009018.4885772,
10000: 763653437.386,
15000: 2337651144.54149,
20000: 4572735210.50902,
25000: 7376166347.04745,
30000: 10693620096.2168,
40000: 18728882739.482,
50000: 28468683855.2853
};
const List<String> ranks = [
"d",
"d+",
"c-",
"c",
"c+",
"b-",
"b",
"b+",
"a-",
"a",
"a+",
"s-",
"s",
"s+",
"ss",
"u",
"x",
"x+"
];
const List<String> ranks2 = [
"top1",
"x+",
"x",
"u",
"ss",
"s+",
"s",
"s-",
"a+",
"a",
"a-",
"b+",
"b",
"b-",
"c+",
"c",
"c-",
"d+",
"d"
];
const List<String> ranks = ["d", "d+", "c-", "c", "c+", "b-", "b", "b+", "a-", "a", "a+", "s-", "s", "s+", "ss", "u", "x", "x+"];
const List<String> ranks2 = ["top1", "x+", "x", "u", "ss", "s+", "s", "s-", "a+", "a", "a-", "b+", "b", "b-", "c+", "c", "c-", "d+", "d"];
const Map<String, double> rankCutoffs = {
"x+": 0.002,
"x": 0.01,
@@ -223,46 +185,46 @@ const List<Color> lineClearsColors = [
];
const Map<String, Duration> sprintAverages = {
// based on https://discord.com/channels/673303546107658242/1260605501754839060/1329448681539244094
'x+': Duration(seconds: 19, milliseconds: 223),
'x': Duration(seconds: 24, milliseconds: 832),
'u': Duration(seconds: 32, milliseconds: 586),
'ss': Duration(seconds: 40, milliseconds: 011),
's+': Duration(seconds: 47, milliseconds: 963),
's': Duration(seconds: 54, milliseconds: 413),
's-': Duration(seconds: 61, milliseconds: 740),
'a+': Duration(seconds: 70, milliseconds: 101),
'a': Duration(seconds: 73, milliseconds: 294),
'a-': Duration(seconds: 81, milliseconds: 773),
'b+': Duration(seconds: 88, milliseconds: 647),
'b': Duration(seconds: 97, milliseconds: 699),
'b-': Duration(seconds: 105, milliseconds: 721),
'c+': Duration(seconds: 113, milliseconds: 229),
'c': Duration(seconds: 124, milliseconds: 740),
'c-': Duration(seconds: 129, milliseconds: 382),
'd+': Duration(seconds: 138, milliseconds: 947),
'd': Duration(seconds: 155, milliseconds: 190),
// based on https://discord.com/channels/673303546107658242/1260605501754839060/1379885551263420498
'x+': Duration(seconds: 19, milliseconds: 335),
'x': Duration(seconds: 24, milliseconds: 075),
'u': Duration(seconds: 31, milliseconds: 293),
'ss': Duration(seconds: 40, milliseconds: 283),
's+': Duration(seconds: 47, milliseconds: 511),
's': Duration(seconds: 54, milliseconds: 210),
's-': Duration(seconds: 60, milliseconds: 496),
'a+': Duration(seconds: 68, milliseconds: 767),
'a': Duration(seconds: 74, milliseconds: 417),
'a-': Duration(seconds: 77, milliseconds: 619),
'b+': Duration(seconds: 89, milliseconds: 294),
'b': Duration(seconds: 98, milliseconds: 297),
'b-': Duration(seconds: 105, milliseconds: 210),
'c+': Duration(seconds: 109, milliseconds: 148),
'c': Duration(seconds: 128, milliseconds: 228),
'c-': Duration(seconds: 138, milliseconds: 100),
'd+': Duration(seconds: 143, milliseconds: 767),
'd': Duration(seconds: 163, milliseconds: 389),
};
const Map<String, int> blitzAverages = {
'x+': 886046,
'x': 631014,
'u': 428799,
'ss': 296430,
's+': 212237,
's': 157234,
's-': 122791,
'a+': 103031,
'a': 90174,
'a-': 73474,
'b+': 60655,
'b': 52463,
'b-': 43877,
'c+': 36594,
'c': 34014,
'c-': 29613,
'd+': 31521,
'd': 23437,
'x+': 912418,
'x': 635169,
'u': 449225,
'ss': 294776,
's+': 212162,
's': 148476,
's-': 121513,
'a+': 104008,
'a': 79753,
'a-': 71817,
'b+': 61266,
'b': 51073,
'b-': 45213,
'c+': 40732,
'c': 30889,
'c-': 28593,
'd+': 25075,
'd': 20438,
};
List<DateTime> seasonStarts = [
@@ -276,9 +238,32 @@ List<DateTime> seasonEnds = [
/// Stolen directly from TETR.IO, redone for the sake of me
const List<String> clearNames = ["Zero", "Single", "Double", "Triple", "Quad", "Penta", "Hexa", "Hepta", "Octa", "Ennea", "Deca", "Hendeca", "Dodeca", "Triadeca", "Tessaradeca", "Pentedeca", "Hexadeca", "Heptadeca", "Octadeca", "Enneadeca", "Eicosa", "Kagaris"];
const List<String> clearNames = [
"Zero",
"Single",
"Double",
"Triple",
"Quad",
"Penta",
"Hexa",
"Hepta",
"Octa",
"Ennea",
"Deca",
"Hendeca",
"Dodeca",
"Triadeca",
"Tessaradeca",
"Pentedeca",
"Hexadeca",
"Heptadeca",
"Octadeca",
"Enneadeca",
"Eicosa",
"Kagaris"
];
enum Lineclears{
enum Lineclears {
ZERO,
SINGLE,
DOUBLE,
@@ -298,12 +283,7 @@ enum Lineclears{
TSPIN_PENTA,
}
enum ComboTables{
none,
classic,
modern,
multiplier
}
enum ComboTables { none, classic, modern, multiplier }
Map<ComboTables, String> comboTablesNames = {
ComboTables.none: "None",
@@ -339,7 +319,7 @@ const Map<Lineclears, int> garbage = {
};
const Map<ComboTables, List<int>> combotable = {
ComboTables.none: [0],
ComboTables.classic: [0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5],
ComboTables.modern: [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4]
ComboTables.none: [0],
ComboTables.classic: [0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5],
ComboTables.modern: [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4]
};
+57 -34
View File
@@ -38,7 +38,7 @@ import 'package:tetra_stats/services/sqlite_db_controller.dart';
import 'package:csv/csv.dart';
const String dbName = "TetraStats.db";
const String webVersionDomain = "ts.dan63.by";
const String webVersionDomain = "tsbeta.dan63.by";
const String tetrioUsersTable = "tetrioUsers";
const String tetrioUsersToTrackTable = "tetrioUsersToTrack";
const String tetraLeagueMatchesTable = "tetrioAlphaLeagueMathces";
@@ -185,7 +185,7 @@ class CacheController {
}
} on StateError{
return null;
}
}
if (int.parse(objectEntry.key.substring(id.length)) <= DateTime.now().millisecondsSinceEpoch){
_cache.remove(objectEntry.key);
return null;
@@ -203,6 +203,14 @@ class CacheController {
}
}
class MunchProgress{
List<BetaRecord> avaliable = [];
List<MinomuncherRaw> munched = [];
MinomuncherRaw? result;
toString() => "${munched.length}/${avaliable.length}, $result";
}
class TetrioService extends DB {
final Map<String, String> _players = {};
final _cache = CacheController.init(); // I'm trying to send as less requests, as possible, so i'm caching the results of those requests.
@@ -319,7 +327,7 @@ class TetrioService extends DB {
DateTime now = DateTime.now();
_cache.store(replay, now.millisecondsSinceEpoch + 3600000);
return replay;
// if not 200 - throw a unique for each code exception
// if not 200 - throw a unique for each code exception
case 404:
throw SzyNotFound();
case 403:
@@ -397,16 +405,16 @@ class TetrioService extends DB {
Future<List<MinomuncherRaw>> minomuncherPostReplay(RawReplay replay, {String? id}) async {
List<MinomuncherRaw>? cached = _cache.get(replay.id, List<MinomuncherRaw>);
if (cached != null) return cached;
Uri url;
if (kIsWeb) {
url = Uri.https(webVersionDomain, 'oskware_bridge.php', {"endpoint": "Minomuncher"}); // Not exist for now, TODO
url = Uri.https(webVersionDomain, 'oskware_bridge.php', {"endpoint": "Minomuncher"});
} else {
url = Uri.https('REDACTED', 'api/replay'); // TODO: change it on release to oskware bridge
}
try {
final response = await client.post(
url,
url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
@@ -445,18 +453,33 @@ class TetrioService extends DB {
}
}
Future<MinomuncherRaw> minomuncherMunchByID(String id) async {
Stream<MunchProgress> minomuncherMunchByIDStream(String id) async* {
MunchProgress progress = MunchProgress();
yield progress;
MinomuncherRaw? cached = _cache.get(id, MinomuncherRaw);
if (cached != null) return cached;
TetraLeagueBetaStream stream = await fetchTLStream(id);
List<BetaRecord> avaliable = stream.records;
avaliable.removeWhere((element) => element.stub);
if (avaliable.isEmpty) throw TetrioNoReplays();
List<List<MinomuncherRaw>> raws = [for (BetaRecord e in avaliable.take(10)) await minomuncherPostReplay(await szyGetReplay(e.replayID))];
List<MinomuncherRaw> ourId = [for (List<MinomuncherRaw> e in raws) e.firstWhere((element) => element.nick == id)];
MinomuncherRaw output = ourId.reduce((a, b) => a + b);
_cache.store(output, DateTime.now().millisecondsSinceEpoch + 300000, id: id+"minomuncher");
return output;
if (cached != null){
progress.result = cached;
yield progress;
} else{
TetraLeagueBetaStream stream = await fetchTLStream(id);
List<BetaRecord> avaliable = stream.records;
avaliable.removeWhere((element) => element.stub);
if (avaliable.isEmpty) throw TetrioNoReplays();
progress.avaliable = avaliable.take(10).toList();
yield progress;
for (BetaRecord record in progress.avaliable){
// MinomuncherRaw? cached = _cache.get(record.replayID, MinomuncherRaw);
// if (cached != null){
// progress.result = cached;
// yield progress;
// }
List<MinomuncherRaw> raw = await minomuncherPostReplay(await szyGetReplay(record.replayID));
progress.munched.add(raw.firstWhere((element) => element.nick == id));
yield progress;
}
progress.result = progress.munched.reduce((a, b) => a + b);
yield progress;
}
}
/// Retrieves avaliable Tetra League matches from Tetra Channel api. Returns stream object (fake stream).
@@ -464,7 +487,7 @@ class TetrioService extends DB {
Future<SingleplayerStream> fetchStream(String userID, String stream) async {
SingleplayerStream? cached = _cache.get(stream+userID, SingleplayerStream);
if (cached != null) return cached;
Uri url;
if (kIsWeb) {
url = Uri.https(webVersionDomain, 'oskware_bridge.php', {"endpoint": "singleplayerStream", "user": userID.toLowerCase().trim(), "stream": stream});
@@ -532,7 +555,7 @@ class TetrioService extends DB {
developer.log("fetchTopTR: Probably, player doesn't have top TR", name: "services/tetrio_crud", error: response.statusCode);
_cache.store(result, DateTime.now().millisecondsSinceEpoch + 300000);
return result;
// if not 200 or 404 - throw a unique for each code exception
// if not 200 or 404 - throw a unique for each code exception
case 403:
throw P1nkl0bst3rForbidden();
case 429:
@@ -583,7 +606,7 @@ class TetrioService extends DB {
case 404:
developer.log("fetchCutoffsTetrio: Cutoffs are gone", name: "services/tetrio_crud", error: response.statusCode);
return null;
// if not 200 or 404 - throw a unique for each code exception
// if not 200 or 404 - throw a unique for each code exception
case 403:
throw TetrioForbidden();
case 429:
@@ -630,7 +653,7 @@ class TetrioService extends DB {
case 404:
developer.log("fetchCutoffsBeanserver: Cutoffs are gone", name: "services/tetrio_crud", error: response.statusCode);
return null;
// if not 200 or 404 - throw a unique for each code exception
// if not 200 or 404 - throw a unique for each code exception
case 403:
throw P1nkl0bst3rForbidden();
case 429:
@@ -685,7 +708,7 @@ class TetrioService extends DB {
case 404:
developer.log("fetchCutoffsHistory: Cutoffs are gone", name: "services/tetrio_crud", error: response.statusCode);
return [];
// if not 200 or 404 - throw a unique for each code exception
// if not 200 or 404 - throw a unique for each code exception
case 403:
throw P1nkl0bst3rForbidden();
case 429:
@@ -730,7 +753,7 @@ class TetrioService extends DB {
return result;
case 404:
throw TetrioPlayerNotExist();
// if not 200 or 404 - throw a unique for each code exception
// if not 200 or 404 - throw a unique for each code exception
case 403:
throw TetrioForbidden();
case 429:
@@ -774,7 +797,7 @@ class TetrioService extends DB {
Batch batch = db.batch();
for (List<dynamic> entry in csv){ // each entry is one state
TetraLeague state = TetraLeague(
id: id,
id: id,
timestamp: DateTime.parse(entry[9]),
apm: entry[6] != '' ? entry[6] : null,
pps: entry[7] != '' ? entry[7] : null,
@@ -973,7 +996,7 @@ class TetrioService extends DB {
Future<List<TetrioPlayerFromLeaderboard>> fetchTetrioLeaderboard({String? prisecter, String? lb, String? country}) async {
// TetrioPlayersLeaderboard? cached = _cache.get("league", TetrioPlayersLeaderboard);
// if (cached != null) return cached;
Uri url;
if (kIsWeb) {
url = Uri.https(webVersionDomain, 'oskware_bridge.php', {
@@ -1094,7 +1117,7 @@ class TetrioService extends DB {
Future<News> fetchNews(String userID) async{
News? cached = _cache.get("user_$userID", News);
if (cached != null) return cached;
Uri url;
if (kIsWeb) {
url = Uri.https(webVersionDomain, 'oskware_bridge.php', {"endpoint": "tetrioNews", "user": userID.toLowerCase().trim(), "limit": "100"});
@@ -1142,7 +1165,7 @@ class TetrioService extends DB {
Future<TetraLeagueBetaStream> fetchTLStream(String userID, {String? prisecter}) async {
// TetraLeagueBetaStream? cached = _cache.get(userID, TetraLeagueBetaStream);
// if (cached != null) return cached;
Uri url;
if (kIsWeb) {
url = Uri.https(webVersionDomain, 'oskware_bridge.php', {
@@ -1215,18 +1238,18 @@ class TetrioService extends DB {
await ensureDbIsOpen();
final db = getDatabaseOrThrow();
await db.execute("""
DELETE FROM $tetraLeagueMatchesTable
WHERE
DELETE FROM $tetraLeagueMatchesTable
WHERE
$idCol IN (
SELECT
$idCol
SELECT
$idCol
FROM (
SELECT
SELECT
$idCol,
ROW_NUMBER() OVER (
PARTITION BY $replayID
ORDER BY $replayID) AS row_num
FROM $tetraLeagueMatchesTable
FROM $tetraLeagueMatchesTable
) t
WHERE row_num > 1
);
@@ -1534,7 +1557,7 @@ class TetrioService extends DB {
throw http.ClientException(e.message, e.uri);
}
}
// finally going to obtain
Uri url;
if (kIsWeb) {
File diff suppressed because it is too large Load Diff
+104 -79
View File
@@ -7,6 +7,7 @@ import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:intl/intl.dart';
import 'package:tetra_stats/services/crud_exceptions.dart';
import 'package:tetra_stats/services/tetrio_crud.dart' show MunchProgress;
import 'package:tetra_stats/widgets/apl_ranges.dart';
import 'package:tetra_stats/widgets/apm_pps_ranges.dart';
import 'package:tetra_stats/widgets/clear_types_thingy.dart';
@@ -15,8 +16,7 @@ import 'package:tetra_stats/widgets/etr_thingy.dart';
import 'package:tetra_stats/widgets/kills_deaths_thingy.dart';
import 'package:tetra_stats/widgets/pps_distribution_thingy.dart';
import 'package:tetra_stats/widgets/pps_surge_radars_thingy.dart';
import 'package:tetra_stats/widgets/sankey_graph.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:syncfusion_flutter_gauges/gauges.dart';
import 'package:tetra_stats/data_objects/achievement.dart';
import 'package:tetra_stats/data_objects/cutoff_tetrio.dart';
@@ -138,6 +138,7 @@ Map<Cards, List<ButtonSegment<CardMod>>> modeButtons = {
)
]
};
double freyhoeStreamTextOpacity = 1.0;
class ZenithCard extends StatelessWidget {
final RecordSingle? record;
@@ -290,7 +291,7 @@ class RecordCard extends StatelessWidget {
final double width;
const RecordCard(this.record, this.achievements, this.betterThanRankAverage, this.closestAverage, this.betterThanClosestAverage, this.rank, {this.width = double.infinity});
Widget result(){
TextStyle tableTextStyle = TextStyle(fontSize: width > 768.0 ? 21 : 18);
return Card(
@@ -514,7 +515,7 @@ class RecordSummary extends StatelessWidget{
final double width;
const RecordSummary({super.key, required this.record, this.betterThanRankAverage, this.closestAverage, this.old = false, this.betterThanClosestAverage, this.rank, this.hideRank = false, this.width = double.infinity});
@override
Widget build(BuildContext context) {
return Row(
@@ -669,7 +670,7 @@ class AchievementSummary extends StatelessWidget{
),
);
}
}
class LeagueCard extends StatelessWidget{
@@ -994,73 +995,98 @@ class _DestinationHomeState extends State<DestinationHome> with SingleTickerProv
);
}
Widget getFreyhoeAnalysis(String id, double width){
return FutureBuilder<MinomuncherRaw>(
future: teto.minomuncherMunchByID(id),
void _changefreyhoeStreamTextOpacity() {
setState(() => freyhoeStreamTextOpacity = freyhoeStreamTextOpacity == 0 ? 1.0 : 0.0);
}
Widget freyhoeStreamNotOnTwitch(String id, double width){
return StreamBuilder<MunchProgress>(
stream: teto.minomuncherMunchByIDStream(id),
builder: (context, snapshot) {
switch (snapshot.connectionState){
case ConnectionState.none:
case ConnectionState.waiting:
case ConnectionState.active:
return const Center(child: CircularProgressIndicator());
case ConnectionState.done:
if (snapshot.hasData){
MinomuncherData data = snapshot.data!.data;
const EdgeInsets paddings = const EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 8.0);
return Column(
children: [
Card(
child: Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("Analysis", style: widget.constraints.maxWidth > 768.0 ? Theme.of(context).textTheme.titleLarge : Theme.of(context).textTheme.titleMedium),
Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Text("via MinoMuncher by Freyhoe", textAlign: TextAlign.center, style: TextStyle(fontSize: widget.constraints.maxWidth > 768.0 ? null : 12.0, color: Colors.grey)),
)
],
),
),
),
),
ApmPpsThingy([ApmPps(data.nick, data.openerAPM, data.APM, data.midgameAPM, data.openerPPS, data.PPS, data.midgamePPS)]),
AplThingy([Apl(data.nick, data.upstackAPL, data.downstackAPL, data.cheeseAPL)], width > 768.0),
EffThingy([Eff(data.nick, data.iEfficiency, data.tEfficiency, data.allspinEfficiency)], width > 768.0),
ClearTypesThingy([data.clearTypes], width),
WellColumnsThingy([data.wellColumns], [data.nick], width),
PPSSurgeThingy([data], width),
SankeyThingy([data], width),
Card(
child: Padding(
padding: paddings,
child: SizedBox(
height: 256.0,
width: 256.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(1000),
child: Container(
switch (snapshot.connectionState){
case ConnectionState.none:
case ConnectionState.waiting:
case ConnectionState.active:
return Center(child: Column(
children: [
CircularProgressIndicator(),
AnimatedOpacity(
opacity: freyhoeStreamTextOpacity,
duration: Duration(seconds:1),
onEnd: _changefreyhoeStreamTextOpacity,
child: Text(
snapshot.data!.avaliable.isEmpty ? "Fetching Records..." : "Munching...",
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)
),
),
if (snapshot.data!.avaliable.isNotEmpty) RichText(
text: TextSpan(
style: TextStyle(fontFamily: "Eurostile Round", color: Colors.white),
children: [
TextSpan(text: "${snapshot.data!.munched.length} ", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)),
TextSpan(text: "out of"),
TextSpan(text: " ${snapshot.data!.avaliable.length}\n", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)),
TextSpan(text: "replays")
]
)
),
]
));
case ConnectionState.done:
if (snapshot.hasData){
MinomuncherData data = snapshot.data!.result!.data;
const EdgeInsets paddings = const EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 8.0);
return Column(
children: [
Card(
child: Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("Analysis", style: widget.constraints.maxWidth > 768.0 ? Theme.of(context).textTheme.titleLarge : Theme.of(context).textTheme.titleMedium),
Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Text("via MinoMuncher by Freyhoe", textAlign: TextAlign.center, style: TextStyle(fontSize: widget.constraints.maxWidth > 768.0 ? null : 12.0, color: Colors.grey)),
)
],
),
),
),
),
ApmPpsThingy([ApmPps(data.nick, data.openerAPM, data.APM, data.midgameAPM, data.openerPPS, data.PPS, data.midgamePPS)]),
AplThingy([Apl(data.nick, data.upstackAPL, data.downstackAPL, data.cheeseAPL)], width > 768.0),
EffThingy([Eff(data.nick, data.iEfficiency, data.tEfficiency, data.allspinEfficiency)], width > 768.0),
ClearTypesThingy([data.clearTypes], width),
WellColumnsThingy([data.wellColumns], [data.nick], width),
PPSSurgeThingy([data], width),
SankeyThingy([data], width),
Card(
child: Padding(
padding: paddings,
child: ClipRRect(
borderRadius: BorderRadius.circular(1000),
child: Container(
decoration: BoxDecoration(gradient: RadialGradient(colors: [Colors.black12.withAlpha(100), Colors.black], radius: 0.6)),
child: SfRadialGauge(
axes: [
RadialAxis(
startAngle: 190,
endAngle: 350,
startAngle: 220,
endAngle: 320,
showLabels: false,
showTicks: true,
canScaleToFit: true,
radiusFactor: 1,
centerY: 0.5,
minimum: 0,
maximum: 1,
ranges: [
GaugeRange(startValue: 0, endValue: 0.2, color: Colors.red),
GaugeRange(startValue: 0.2, endValue: 0.4, color: Colors.yellow),
GaugeRange(startValue: 0.4, endValue: 0.6, color: Colors.green),
GaugeRange(startValue: 0.6, endValue: 0.8, color: Colors.blue),
GaugeRange(startValue: 0.8, endValue: 1, color: Colors.purple),
GaugeRange(startValue: 0, endValue: 0.2, color: Colors.blueGrey),
GaugeRange(startValue: 0.2, endValue: 0.4, color: Colors.greenAccent),
GaugeRange(startValue: 0.4, endValue: 0.6, color: Colors.yellowAccent),
GaugeRange(startValue: 0.6, endValue: 0.8, color: Colors.orangeAccent),
GaugeRange(startValue: 0.8, endValue: 1, color: Colors.redAccent),
],
pointers: [
NeedlePointer(
@@ -1092,21 +1118,20 @@ class _DestinationHomeState extends State<DestinationHome> with SingleTickerProv
),
),
),
),
)
),
KillsDeathsThingy([KD(data.nick, data.killStats, data.deathStats)], width),
PPSDistributionThingy([data.ppsSegments], [data.nick], width)
],
);
}
if (snapshot.hasError){
if (snapshot.error is TetrioNoReplays) return SizedBox(height: 720.0, child: ErrorThingy(data: FetchResults(false, null, [], null, null, null, null, null, false, snapshot.error as Exception)));
return SizedBox(height: 720.0, child: FutureError(snapshot));
}
)
),
KillsDeathsThingy([KD(data.nick, data.killStats, data.deathStats)], width),
PPSDistributionThingy([data.ppsSegments], [data.nick], width)
],
);
}
return const Text("what?");
},
if (snapshot.hasError){
if (snapshot.error is TetrioNoReplays) return SizedBox(height: 720.0, child: ErrorThingy(data: FetchResults(false, null, [], null, null, null, null, null, false, snapshot.error as Exception)));
return SizedBox(height: 720.0, child: FutureError(snapshot));
}
}
return const Text("what?");
}
);
}
@@ -1263,7 +1288,7 @@ class _DestinationHomeState extends State<DestinationHome> with SingleTickerProv
)
],
),
)
)
),
],
);
@@ -1309,7 +1334,7 @@ class _DestinationHomeState extends State<DestinationHome> with SingleTickerProv
Cards.overview => getOverviewCard(snapshot.data!.summaries!, (snapshot.data!.averages != null && snapshot.data!.summaries!.league.rank != "z") ? snapshot.data!.averages!.data[snapshot.data!.summaries!.league.rank] : (snapshot.data!.averages != null && snapshot.data!.summaries!.league.percentileRank != "z") ? snapshot.data!.averages!.data[snapshot.data!.summaries!.league.percentileRank] : null, width),
Cards.tetraLeague => switch (cardMod){
CardMod.info => getTetraLeagueCard(snapshot.data!.summaries!.league, snapshot.data!.cutoffs, (snapshot.data!.averages != null && snapshot.data!.summaries!.league.rank != "z") ? snapshot.data!.averages!.data[snapshot.data!.summaries!.league.rank] : (snapshot.data!.averages != null && snapshot.data!.summaries!.league.percentileRank != "z") ? snapshot.data!.averages!.data[snapshot.data!.summaries!.league.percentileRank] : null, snapshot.data!.states, snapshot.data!.playerPos, width, tlAchievements),
CardMod.exRecords => getFreyhoeAnalysis(snapshot.data!.player!.username, width),
CardMod.exRecords => freyhoeStreamNotOnTwitch(snapshot.data!.player!.username, width),
CardMod.ex => getPreviousSeasonsList(snapshot.data!.summaries!.pastLeague, width),
CardMod.records => getRecentTLrecords(widget.constraints, snapshot.data!.player!.userId),
_ => const Center(child: Text("huh?"))
@@ -1402,7 +1427,7 @@ class _DestinationHomeState extends State<DestinationHome> with SingleTickerProv
children: [
Row(
children: [
const Spacer(),
const Spacer(),
Text(t.bio, style: const TextStyle(fontFamily: "Eurostile Round Extended")),
const Spacer()
],
@@ -1493,7 +1518,7 @@ class _DestinationHomeState extends State<DestinationHome> with SingleTickerProv
children: [
Row(
children: [
const Spacer(),
const Spacer(),
Text(t.bio, style: const TextStyle(fontFamily: "Eurostile Round Extended")),
const Spacer()
],
+132 -134
View File
@@ -1,134 +1,132 @@
name: tetra_stats
description: Track your and other player stats in TETR.IO
publish_to: 'none'
version: 2.0.8+49
environment:
sdk: '>=3.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
http:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
transparent_image: ^2.0.1
cupertino_icons: ^1.0.2
vector_math: any
sqflite: ^2.2.8+2
sqflite_common_ffi: any
sqlite3_flutter_libs: ^0.5.15
sqflite_common_ffi_web: '>=0.1.0-dev.1'
path_provider: ^2.0.15
path: ^1.8.2
fl_chart: ^0.66.0
package_info_plus: ^5.0.1
shared_preferences: ^2.1.1
intl: ^0.19.0
syncfusion_flutter_gauges: ^24.1.41
file_selector: ^1.0.1
file_picker: ^6.1.1
slang: ^3.20.0
slang_flutter: ^3.20.0
csv: ^5.0.2
url_launcher: ^6.1.12
flutter_svg: any
window_manager: ^0.3.7
flutter_markdown: ^0.6.18
flutter_colorpicker: ^1.0.3
flutter_layout_grid: ^2.0.0
go_router: ^13.0.0
syncfusion_flutter_charts: ^24.2.9
flutter_to_debian: ^2.0.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.1
flutter_launcher_icons: "^0.13.1"
test: ^1.24.9
flutter_launcher_icons:
android: "launcher_icon"
ios: true
image_path: "res/icons/app.png"
min_sdk_android: 19 # android min sdk min:16, default 21
web:
generate: true
image_path: "res/icons/app.png"
background_color: "#000000"
theme_color: "#000000"
windows:
generate: true
image_path: "res/icons/app.png"
icon_size: 256 # min:48, max:256, default: 48
macos:
generate: true
image_path: "res/icons/app.png"
targets:
$default:
builders:
slang_build_runner:
options:
input_directory: res/i18n
output_directory: lib/i18n
flutter:
uses-material-design: true
assets:
- res/avatars/
- res/icons/
- res/tetrio_tl_alpha_ranks/
- res/images/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
fonts:
- family: Eurostile Round
fonts:
- asset: res/fonts/EurostileRound-Black.ttf
- asset: res/fonts/EurostileRound-BlackItalic.ttf
- asset: res/fonts/EurostileRound-Bold.ttf
- asset: res/fonts/EurostileRound-BoldItalic.ttf
- asset: res/fonts/EurostileRound-Heavy.ttf
weight: 900
- asset: res/fonts/EurostileRound-HeavyItalic.ttf
weight: 900
style: italic
- asset: res/fonts/EurostileRound-Italic.ttf
style: italic
- asset: res/fonts/EurostileRound-Medium.ttf
- asset: res/fonts/EurostileRound-MediumItalic.ttf
weight: 500
style: italic
- asset: res/fonts/EurostileRound-Regular.ttf
- family: Eurostile Round Condensed
fonts:
- asset: res/fonts/EurostileRoundCondensed-Heavy.ttf
- asset: res/fonts/EurostileRoundCondensed-HeavyItalic.ttf
- asset: res/fonts/EurostileRoundCondensed-Italic.ttf
- asset: res/fonts/EurostileRoundCondensed-Regular.ttf
- family: Eurostile Round Extended
fonts:
- asset: res/fonts/EurostileRoundExtended-Black.ttf
- asset: res/fonts/EurostileRoundExtended-BlackItalic.ttf
weight: 900
style: italic
- asset: res/fonts/EurostileRoundExtended-Italic.ttf
style: italic
- asset: res/fonts/EurostileRoundExtended-Medium.ttf
weight: 500
- asset: res/fonts/EurostileRoundExtended-Regular.ttf
name: tetra_stats
description: Track your and other player stats in TETR.IO
publish_to: "none"
version: 2.1.0+50
environment:
sdk: ">=3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
http:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
transparent_image: ^2.0.1
cupertino_icons: ^1.0.2
vector_math: any
sqflite: ^2.2.8+2
sqflite_common_ffi: any
sqlite3_flutter_libs: ^0.5.15
sqflite_common_ffi_web: ">=0.1.0-dev.1"
path_provider: ^2.0.15
path: ^1.8.2
fl_chart: ^0.66.0
package_info_plus: ^5.0.1
shared_preferences: ^2.1.1
intl: ^0.19.0
syncfusion_flutter_gauges: ^24.1.41
file_selector: ^1.0.1
file_picker: ^6.1.1
slang: ^3.20.0
slang_flutter: ^3.20.0
csv: ^5.0.2
url_launcher: ^6.1.12
flutter_svg: any
window_manager: ^0.3.7
flutter_markdown: ^0.6.18
flutter_colorpicker: ^1.0.3
flutter_layout_grid: ^2.0.0
go_router: ^13.0.0
syncfusion_flutter_charts: ^24.2.9
flutter_to_debian: ^2.0.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.1
flutter_launcher_icons: "^0.13.1"
test: ^1.24.9
flutter_launcher_icons:
android: "launcher_icon"
ios: true
image_path: "res/icons/app.png"
min_sdk_android: 19 # android min sdk min:16, default 21
web:
generate: true
image_path: "res/icons/app.png"
background_color: "#000000"
theme_color: "#000000"
windows:
generate: true
image_path: "res/icons/app.png"
icon_size: 256 # min:48, max:256, default: 48
macos:
generate: true
image_path: "res/icons/app.png"
targets:
$default:
builders:
slang_build_runner:
options:
input_directory: res/i18n
output_directory: lib/i18n
flutter:
uses-material-design: true
assets:
- res/avatars/
- res/icons/
- res/tetrio_tl_alpha_ranks/
- res/images/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
fonts:
- family: Eurostile Round
fonts:
- asset: res/fonts/EurostileRound-Black.ttf
- asset: res/fonts/EurostileRound-BlackItalic.ttf
- asset: res/fonts/EurostileRound-Bold.ttf
- asset: res/fonts/EurostileRound-BoldItalic.ttf
- asset: res/fonts/EurostileRound-Heavy.ttf
weight: 900
- asset: res/fonts/EurostileRound-HeavyItalic.ttf
weight: 900
style: italic
- asset: res/fonts/EurostileRound-Italic.ttf
style: italic
- asset: res/fonts/EurostileRound-Medium.ttf
- asset: res/fonts/EurostileRound-MediumItalic.ttf
weight: 500
style: italic
- asset: res/fonts/EurostileRound-Regular.ttf
- family: Eurostile Round Condensed
fonts:
- asset: res/fonts/EurostileRoundCondensed-Heavy.ttf
- asset: res/fonts/EurostileRoundCondensed-HeavyItalic.ttf
- asset: res/fonts/EurostileRoundCondensed-Italic.ttf
- asset: res/fonts/EurostileRoundCondensed-Regular.ttf
- family: Eurostile Round Extended
fonts:
- asset: res/fonts/EurostileRoundExtended-Black.ttf
- asset: res/fonts/EurostileRoundExtended-BlackItalic.ttf
weight: 900
style: italic
- asset: res/fonts/EurostileRoundExtended-Italic.ttf
style: italic
- asset: res/fonts/EurostileRoundExtended-Medium.ttf
weight: 500
- asset: res/fonts/EurostileRoundExtended-Regular.ttf