Smooth graph + unfinished reimplementation of tl progress bar
Also small UI changes
This commit is contained in:
parent
3b16822c1f
commit
5c6c502a57
|
@ -17,6 +17,9 @@ const double appdspWeight = 140;
|
|||
const double vsapmWeight = 60;
|
||||
const double cheeseWeight = 1.25;
|
||||
const double gbeWeight = 315;
|
||||
const List<String> ranks = [
|
||||
"d", "d+", "c-", "c", "c+", "b-", "b", "b+", "a-", "a", "a+", "s-", "s", "s+", "ss", "u", "x"
|
||||
];
|
||||
const Map<String, double> rankCutoffs = {
|
||||
"x": 0.01,
|
||||
"u": 0.05,
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
/// To regenerate, run: `dart run slang`
|
||||
///
|
||||
/// Locales: 2
|
||||
/// Strings: 1120 (560 per locale)
|
||||
/// Strings: 1122 (561 per locale)
|
||||
///
|
||||
/// Built on 2024-03-24 at 14:28 UTC
|
||||
/// Built on 2024-04-11 at 22:23 UTC
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
|
@ -216,6 +216,7 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
|
|||
String verdictGeneral({required Object n, required Object verdict, required Object rank}) => '${n} ${verdict} than ${rank} rank average';
|
||||
String get verdictBetter => 'better';
|
||||
String get verdictWorse => 'worse';
|
||||
String get smooth => 'Smooth';
|
||||
String gamesUntilRanked({required Object left}) => '${left} games until being ranked';
|
||||
String get nerdStats => 'Nerd Stats';
|
||||
String get playersYouTrack => 'Players you track';
|
||||
|
@ -870,6 +871,7 @@ class _StringsRu implements Translations {
|
|||
@override String verdictGeneral({required Object verdict, required Object rank, required Object n}) => '${verdict} среднего ${rank} ранга на ${n}';
|
||||
@override String get verdictBetter => 'Лучше';
|
||||
@override String get verdictWorse => 'Хуже';
|
||||
@override String get smooth => 'Гладкий';
|
||||
@override String gamesUntilRanked({required Object left}) => '${left} матчей до получения рейтинга';
|
||||
@override String get nerdStats => 'Для задротов';
|
||||
@override String get playersYouTrack => 'Отслеживаемые игроки';
|
||||
|
@ -1516,6 +1518,7 @@ extension on Translations {
|
|||
case 'verdictGeneral': return ({required Object n, required Object verdict, required Object rank}) => '${n} ${verdict} than ${rank} rank average';
|
||||
case 'verdictBetter': return 'better';
|
||||
case 'verdictWorse': return 'worse';
|
||||
case 'smooth': return 'Smooth';
|
||||
case 'gamesUntilRanked': return ({required Object left}) => '${left} games until being ranked';
|
||||
case 'nerdStats': return 'Nerd Stats';
|
||||
case 'playersYouTrack': return 'Players you track';
|
||||
|
@ -2096,6 +2099,7 @@ extension on _StringsRu {
|
|||
case 'verdictGeneral': return ({required Object verdict, required Object rank, required Object n}) => '${verdict} среднего ${rank} ранга на ${n}';
|
||||
case 'verdictBetter': return 'Лучше';
|
||||
case 'verdictWorse': return 'Хуже';
|
||||
case 'smooth': return 'Гладкий';
|
||||
case 'gamesUntilRanked': return ({required Object left}) => '${left} матчей до получения рейтинга';
|
||||
case 'nerdStats': return 'Для задротов';
|
||||
case 'playersYouTrack': return 'Отслеживаемые игроки';
|
||||
|
|
|
@ -820,6 +820,7 @@ class CompareThingy extends StatelessWidget {
|
|||
colors: const [Colors.green, Colors.transparent],
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
transform: GradientRotation(0.6),
|
||||
stops: [
|
||||
0.0,
|
||||
higherIsBetter
|
||||
|
@ -830,7 +831,8 @@ class CompareThingy extends StatelessWidget {
|
|||
? 0.6
|
||||
: 0
|
||||
],
|
||||
)),
|
||||
)
|
||||
),
|
||||
child: Text(
|
||||
(prefix ?? "") + f.format(greenSide) + (postfix ?? ""),
|
||||
style: const TextStyle(
|
||||
|
@ -838,7 +840,12 @@ class CompareThingy extends StatelessWidget {
|
|||
shadows: <Shadow>[
|
||||
Shadow(
|
||||
offset: Offset(0.0, 0.0),
|
||||
blurRadius: 3.0,
|
||||
blurRadius: 1.0,
|
||||
color: Colors.black,
|
||||
),
|
||||
Shadow(
|
||||
offset: Offset(0.0, 0.0),
|
||||
blurRadius: 2.0,
|
||||
color: Colors.black,
|
||||
),
|
||||
Shadow(
|
||||
|
@ -874,6 +881,7 @@ class CompareThingy extends StatelessWidget {
|
|||
colors: const [Colors.red, Colors.transparent],
|
||||
begin: Alignment.centerRight,
|
||||
end: Alignment.centerLeft,
|
||||
transform: GradientRotation(-0.6),
|
||||
stops: [
|
||||
0.0,
|
||||
higherIsBetter
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
|
@ -35,6 +36,7 @@ import 'package:go_router/go_router.dart';
|
|||
final TetrioService teto = TetrioService(); // thing, that manadge our local DB
|
||||
int _chartsIndex = 0;
|
||||
bool _gamesPlayedInsteadOfDateAndTime = false;
|
||||
bool _smooth = false;
|
||||
List _historyShortTitles = ["TR", "Glicko", "RD", "APM", "PPS", "VS", "APP", "DS/S", "DS/P", "APP + DS/P", "VS/APM", "Cheese", "GbE", "wAPP", "Area", "eTR", "±eTR", "Opener", "Plonk", "Inf. DS", "Stride"];
|
||||
late ScrollController _scrollController;
|
||||
final NumberFormat _timeInSec = NumberFormat("#,###.###s.", LocaleSettings.currentLocale.languageCode);
|
||||
|
@ -81,11 +83,15 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
TetrioPlayersLeaderboard? everyone;
|
||||
PlayerLeaderboardPosition? meAmongEveryone;
|
||||
TetraLeagueAlpha? rankAverages;
|
||||
double? thatRankCutoff;
|
||||
double? nextRankCutoff;
|
||||
String _searchFor = "6098518e3d5155e6ec429cdc"; // who we looking for
|
||||
String _titleNickname = "dan63047";
|
||||
String _titleNickname = "";
|
||||
/// Each dropdown menu item contains list of dots for the graph
|
||||
var chartsData = <DropdownMenuItem<List<FlSpot>>>[];
|
||||
var chartsDataGamesPlayed = <DropdownMenuItem<List<FlSpot>>>[];
|
||||
List<DropdownMenuItem<List<FlSpot>>> chartsData = [];
|
||||
List<DropdownMenuItem<List<FlSpot>>>? smoothChartsData;
|
||||
List<DropdownMenuItem<List<FlSpot>>> chartsDataGamesPlayed = [];
|
||||
List<DropdownMenuItem<List<FlSpot>>>? smoothChartsDataGamesPlayed;
|
||||
//var tableData = <TableRow>[];
|
||||
final bodyGlobalKey = GlobalKey();
|
||||
bool _showSearchBar = false;
|
||||
|
@ -108,7 +114,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
changePlayer(widget.player!); // it's gonna be user input
|
||||
}else{
|
||||
_getPreferences() // otherwise, checking for preferences
|
||||
.then((value) => changePlayer(prefs.getString("player") ?? "dan63047")); // no preferences - loading me
|
||||
.then((value) => changePlayer(prefs.getString("player") ?? "6098518e3d5155e6ec429cdc")); // no preferences - loading me
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
@ -185,6 +191,11 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
everyone ??= await teto.fetchTLLeaderboard();
|
||||
meAmongEveryone = await compute(everyone!.getLeaderboardPosition, me);
|
||||
if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!);
|
||||
if (me.tlSeason1.rank != "z") {
|
||||
thatRankCutoff = everyone!.cutoffs[me.tlSeason1.rank];
|
||||
nextRankCutoff = everyone!.cutoffs[ranks.indexOf(me.tlSeason1.rank)+1];
|
||||
nextRankCutoff = nextRankCutoff??25000;
|
||||
}
|
||||
}
|
||||
|
||||
if (everyone != null && me.tlSeason1.gamesPlayed > 9) rankAverages = everyone?.averages[me.tlSeason1.percentileRank]?[0];
|
||||
|
@ -307,6 +318,42 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) FlSpot(tl.gamesPlayed.toDouble(), tl.playstyle!.infds)], child: const Text("Inf. DS")),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) FlSpot(tl.gamesPlayed.toDouble(), tl.playstyle!.stride)], child: const Text("Stride")),
|
||||
];
|
||||
if (chartsData[0].value!.length > 200){
|
||||
smoothChartsData = [];
|
||||
smoothChartsDataGamesPlayed = [];
|
||||
for (var chart in chartsData) {
|
||||
int valuesPerDot = (chart.value!.length / 200).floor();
|
||||
int lastDotEntries = chart.value!.length - (valuesPerDot * 199);
|
||||
List<FlSpot> spots = [];
|
||||
for (int i=0; i < 200; i++){
|
||||
double avgX = 0, avgY = 0;
|
||||
for (int k = i * valuesPerDot; k < (i == 199 ? chart.value!.length : i * valuesPerDot + valuesPerDot); k++) {
|
||||
avgX += chart.value![k].x;
|
||||
avgY += chart.value![k].y;
|
||||
}
|
||||
avgX /= i == 199 ? lastDotEntries : valuesPerDot;
|
||||
avgY /= i == 199 ? lastDotEntries : valuesPerDot;
|
||||
spots.add(FlSpot(avgX, avgY));
|
||||
}
|
||||
smoothChartsData!.add(DropdownMenuItem(value: spots, child: chart.child));
|
||||
}
|
||||
for (var chart in chartsDataGamesPlayed) {
|
||||
int valuesPerDot = (chart.value!.length / 200).floor();
|
||||
int lastDotEntries = chart.value!.length - (valuesPerDot * 199);
|
||||
List<FlSpot> spots = [];
|
||||
for (int i=0; i < 200; i++){
|
||||
double avgX = 0, avgY = 0;
|
||||
for (int k = i * valuesPerDot; k < (i == 199 ? chart.value!.length : i * valuesPerDot + valuesPerDot); k++) {
|
||||
avgX += chart.value![k].x;
|
||||
avgY += chart.value![k].y;
|
||||
}
|
||||
avgX /= i == 199 ? lastDotEntries : valuesPerDot;
|
||||
avgY /= i == 199 ? lastDotEntries : valuesPerDot;
|
||||
spots.add(FlSpot(avgX, avgY));
|
||||
}
|
||||
smoothChartsDataGamesPlayed!.add(DropdownMenuItem(value: spots, child: chart.child));
|
||||
}
|
||||
}
|
||||
}else{
|
||||
compareWith = null;
|
||||
chartsData = [];
|
||||
|
@ -465,6 +512,10 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
topTR: snapshot.data![7],
|
||||
bot: snapshot.data![0].role == "bot",
|
||||
guest: snapshot.data![0].role == "anon",
|
||||
thatRankCutoff: thatRankCutoff,
|
||||
thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null,
|
||||
nextRankCutoff: nextRankCutoff,
|
||||
nextRankTarget: snapshot.data![0].tlSeason1.rank != "z" || snapshot.data![0].tlSeason1.rank != "x" ? rankTargets[ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1] : null,
|
||||
averages: rankAverages,
|
||||
lbPositions: meAmongEveryone
|
||||
),
|
||||
|
@ -474,7 +525,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
child: _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, oldMathcesHere: _TLHistoryWasFetched, separateScrollController: true,)
|
||||
),
|
||||
],),
|
||||
_History(chartsData: chartsData, chartsDataGamesPlayed: chartsDataGamesPlayed, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0),
|
||||
_History(chartsData: chartsData, chartsDataGamesPlayed: chartsDataGamesPlayed, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, smoothChartsData: smoothChartsData, smoothChartsDataGamesPlayed: smoothChartsDataGamesPlayed),
|
||||
_TwoRecordsThingy(sprint: snapshot.data![1]['sprint'], blitz: snapshot.data![1]['blitz'], rank: snapshot.data![0].tlSeason1.percentileRank,),
|
||||
_OtherThingy(zen: snapshot.data![1]['zen'], bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![6],)
|
||||
] : [
|
||||
|
@ -489,7 +540,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
lbPositions: meAmongEveryone
|
||||
),
|
||||
_TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, oldMathcesHere: _TLHistoryWasFetched),
|
||||
_History(chartsData: chartsData, chartsDataGamesPlayed: chartsDataGamesPlayed, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0),
|
||||
_History(chartsData: chartsData, chartsDataGamesPlayed: chartsDataGamesPlayed, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, smoothChartsData: smoothChartsData, smoothChartsDataGamesPlayed: smoothChartsDataGamesPlayed),
|
||||
_RecordThingy(record: snapshot.data![1]['sprint'], rank: snapshot.data![0].tlSeason1.percentileRank),
|
||||
_RecordThingy(record: snapshot.data![1]['blitz'], rank: snapshot.data![0].tlSeason1.percentileRank),
|
||||
_OtherThingy(zen: snapshot.data![1]['zen'], bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![6],)
|
||||
|
@ -592,7 +643,7 @@ class _NavDrawerState extends State<NavDrawer> {
|
|||
homePlayerNickname = id;
|
||||
}
|
||||
} else {
|
||||
homePlayerNickname = "dan63047";
|
||||
homePlayerNickname = "dan63";
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
@ -624,7 +675,7 @@ class _NavDrawerState extends State<NavDrawer> {
|
|||
leading: const Icon(Icons.home),
|
||||
title: Text(homePlayerNickname),
|
||||
onTap: () {
|
||||
widget.changePlayer(prefs.getString("player") ?? "dan63047"); // changes player on main view to the one from preferences
|
||||
widget.changePlayer(prefs.getString("player") ?? "6098518e3d5155e6ec429cdc"); // changes player on main view to the one from preferences
|
||||
Navigator.of(context).pop(); // and then NavDrawer closes itself.
|
||||
},
|
||||
),
|
||||
|
@ -753,7 +804,9 @@ class _TLRecords extends StatelessWidget {
|
|||
|
||||
class _History extends StatelessWidget{
|
||||
final List<DropdownMenuItem<List<FlSpot>>> chartsData;
|
||||
final List<DropdownMenuItem<List<FlSpot>>>? smoothChartsData;
|
||||
final List<DropdownMenuItem<List<FlSpot>>> chartsDataGamesPlayed;
|
||||
final List<DropdownMenuItem<List<FlSpot>>>? smoothChartsDataGamesPlayed;
|
||||
final String userID;
|
||||
final Function update;
|
||||
final Function changePlayer;
|
||||
|
@ -761,7 +814,7 @@ class _History extends StatelessWidget{
|
|||
|
||||
/// Widget, that can show history of some stat of the player on the graph.
|
||||
/// Requires player [states], which is list of states and function [update], which rebuild widgets
|
||||
const _History({required this.chartsData, required this.chartsDataGamesPlayed, required this.userID, required this.changePlayer, required this.update, required this.wasActiveInTL});
|
||||
const _History({required this.chartsData, required this.chartsDataGamesPlayed, required this.userID, required this.changePlayer, required this.update, required this.wasActiveInTL, this.smoothChartsData, this.smoothChartsDataGamesPlayed});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -776,6 +829,8 @@ class _History extends StatelessWidget{
|
|||
));
|
||||
}
|
||||
bool bigScreen = MediaQuery.of(context).size.width > 768;
|
||||
var selectedGraph = _gamesPlayedInsteadOfDateAndTime ? chartsDataGamesPlayed[_chartsIndex].value! : chartsData[_chartsIndex].value!;
|
||||
var smoothSelectedGraph = _gamesPlayedInsteadOfDateAndTime ? (smoothChartsDataGamesPlayed?[_chartsIndex].value) : (smoothChartsData?[_chartsIndex].value);
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SingleChildScrollView(
|
||||
|
@ -786,6 +841,7 @@ class _History extends StatelessWidget{
|
|||
children: [
|
||||
Wrap(
|
||||
spacing: 20,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
@ -815,9 +871,21 @@ class _History extends StatelessWidget{
|
|||
),
|
||||
],
|
||||
),
|
||||
if (smoothSelectedGraph != null) Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Checkbox(value: _smooth,
|
||||
checkColor: Colors.black,
|
||||
onChanged: ((value) {
|
||||
_smooth = value!;
|
||||
update();
|
||||
})),
|
||||
Text(t.smooth, style: const TextStyle(color: Colors.white, fontSize: 22))
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
if(chartsData[_chartsIndex].value!.length > 1) _HistoryChartThigy(data: _gamesPlayedInsteadOfDateAndTime ? chartsDataGamesPlayed[_chartsIndex].value! : chartsData[_chartsIndex].value!, yAxisTitle: _historyShortTitles[_chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(), xFormat: NumberFormat.compact())
|
||||
if(chartsData[_chartsIndex].value!.length > 1) _HistoryChartThigy(data: selectedGraph, smoothData: smoothSelectedGraph, smooth: _smooth, yAxisTitle: _historyShortTitles[_chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(), xFormat: NumberFormat.compact())
|
||||
else if (chartsData[_chartsIndex].value!.length <= 1) Center(child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
@ -835,6 +903,8 @@ class _History extends StatelessWidget{
|
|||
|
||||
class _HistoryChartThigy extends StatefulWidget{
|
||||
final List<FlSpot> data;
|
||||
final List<FlSpot>? smoothData;
|
||||
final bool smooth;
|
||||
final String yAxisTitle;
|
||||
final bool bigScreen;
|
||||
final double leftSpace;
|
||||
|
@ -844,7 +914,7 @@ class _HistoryChartThigy extends StatefulWidget{
|
|||
/// Implements graph for the _History widget. Requires [data] which is a list of dots for the graph. [yAxisTitle] used to keep track of changes.
|
||||
/// [bigScreen] tells if screen wide enough, [leftSpace] sets size, reserved for titles on the left from the graph and [yFormat] sets number format
|
||||
/// for left titles
|
||||
const _HistoryChartThigy({required this.data, required this.yAxisTitle, required this.bigScreen, required this.leftSpace, required this.yFormat, this.xFormat});
|
||||
const _HistoryChartThigy({required this.data, this.smoothData, required this.smooth, required this.yAxisTitle, required this.bigScreen, required this.leftSpace, required this.yFormat, this.xFormat});
|
||||
|
||||
@override
|
||||
State<_HistoryChartThigy> createState() => _HistoryChartThigyState();
|
||||
|
@ -1050,8 +1120,26 @@ class _HistoryChartThigyState extends State<_HistoryChartThigy> {
|
|||
children: [
|
||||
LineChart(
|
||||
key: graphKey,
|
||||
curve: Curves.elasticInOut,
|
||||
LineChartData(
|
||||
lineBarsData: [LineChartBarData(spots: widget.data)],
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
show: !_smooth,
|
||||
spots: widget.data,
|
||||
dotData: FlDotData(show: false),
|
||||
isCurved: true,
|
||||
curveSmoothness: 0.35,
|
||||
preventCurveOverShooting: true
|
||||
),
|
||||
if (widget.smoothData != null) LineChartBarData(
|
||||
show: _smooth,
|
||||
spots: widget.smoothData!,
|
||||
dotData: FlDotData(show: false),
|
||||
isCurved: true,
|
||||
curveSmoothness: 0.35,
|
||||
preventCurveOverShooting: true,
|
||||
)
|
||||
],
|
||||
clipData: const FlClipData.all(),
|
||||
borderData: FlBorderData(show: false),
|
||||
gridData: FlGridData(verticalInterval: xInterval),
|
||||
|
@ -1116,29 +1204,6 @@ class _HistoryChartThigyState extends State<_HistoryChartThigy> {
|
|||
}
|
||||
}
|
||||
|
||||
// class _HistoryTableThingy extends StatelessWidget{
|
||||
// final List<TableRow> tableData;
|
||||
|
||||
// const _HistoryTableThingy(this.tableData);
|
||||
// // :tf:
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return LayoutBuilder(builder: (context, constraints){
|
||||
// return Table(
|
||||
// defaultColumnWidth: FixedColumnWidth(75),
|
||||
// columnWidths: {
|
||||
// 0: FixedColumnWidth(170),
|
||||
// 1: FixedColumnWidth(100),
|
||||
// 2: FixedColumnWidth(90),
|
||||
// 18: FixedColumnWidth(100),
|
||||
// 19: FixedColumnWidth(90),
|
||||
// },
|
||||
// children: tableData,
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
class _TwoRecordsThingy extends StatelessWidget {
|
||||
final RecordSingle? sprint;
|
||||
final RecordSingle? blitz;
|
||||
|
|
|
@ -15,6 +15,7 @@ var _chartsShortTitlesDropdowns = <DropdownMenuItem>[for (MapEntry e in chartsSh
|
|||
Stats _chartsX = Stats.tr;
|
||||
Stats _chartsY = Stats.apm;
|
||||
List<DropdownMenuItem> _itemStats = [for (MapEntry e in chartsShortTitles.entries) DropdownMenuItem(value: e.key, child: Text(e.value))];
|
||||
List<_MyScatterSpot> _spots = [];
|
||||
Stats _sortBy = Stats.tr;
|
||||
late List<TetrioPlayerFromLeaderboard> they;
|
||||
bool _reversed = false;
|
||||
|
@ -63,6 +64,7 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
|||
super.initState();
|
||||
previousAxisTitles = _chartsX.toString()+_chartsY.toString();
|
||||
they = TetrioPlayersLeaderboard("lol", []).getStatRanking(widget.rank[1]["entries"]!, _sortBy, reversed: _reversed, country: _country);
|
||||
createSpots();
|
||||
recalculateBoundaries();
|
||||
resetScale();
|
||||
}
|
||||
|
@ -102,6 +104,19 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
|||
}).getStatByEnum(_chartsY).toDouble();
|
||||
}
|
||||
|
||||
void createSpots(){
|
||||
_spots = [
|
||||
for (TetrioPlayerFromLeaderboard entry in widget.rank[1]["entries"])
|
||||
if (entry.apm != 0.0 && entry.vs != 0.0) // prevents from ScatterChart "Offset argument contained a NaN value." exception
|
||||
_MyScatterSpot(
|
||||
entry.getStatByEnum(_chartsX).toDouble(),
|
||||
entry.getStatByEnum(_chartsY).toDouble(),
|
||||
entry.userId,
|
||||
entry.username,
|
||||
dotPainter: FlDotCirclePainter(color: rankColors[entry.rank]??Colors.white, radius: 3))
|
||||
];
|
||||
}
|
||||
|
||||
void resetScale(){
|
||||
maxX = actualMaxX;
|
||||
minX = actualMinX;
|
||||
|
@ -161,6 +176,7 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
|||
double graphStartX = padding.left;
|
||||
double graphEndX = MediaQuery.sizeOf(context).width - padding.right;
|
||||
if (previousAxisTitles != _chartsX.toString()+_chartsY.toString()){
|
||||
createSpots();
|
||||
recalculateBoundaries();
|
||||
resetScale();
|
||||
previousAxisTitles = _chartsX.toString()+_chartsY.toString();
|
||||
|
@ -325,16 +341,7 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
|||
minY: minY,
|
||||
maxY: maxY,
|
||||
clipData: const FlClipData.all(),
|
||||
scatterSpots: [
|
||||
for (TetrioPlayerFromLeaderboard entry in widget.rank[1]["entries"])
|
||||
if (entry.apm != 0.0 && entry.vs != 0.0) // prevents from ScatterChart "Offset argument contained a NaN value." exception
|
||||
_MyScatterSpot(
|
||||
entry.getStatByEnum(_chartsX).toDouble(),
|
||||
entry.getStatByEnum(_chartsY).toDouble(),
|
||||
entry.userId,
|
||||
entry.username,
|
||||
dotPainter: FlDotCirclePainter(color: rankColors[entry.rank]??Colors.white, radius: 3))
|
||||
],
|
||||
scatterSpots: _spots,
|
||||
scatterTouchData: ScatterTouchData(
|
||||
handleBuiltInTouches: false,
|
||||
touchCallback:(touchEvent, touchResponse) {
|
||||
|
|
|
@ -64,7 +64,7 @@ class SettingsState extends State<SettingsView> {
|
|||
defaultNickname = n;
|
||||
}
|
||||
} else {
|
||||
defaultNickname = "dan63047";
|
||||
defaultNickname = "6098518e3d5155e6ec429cdc";
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ class SettingsState extends State<SettingsView> {
|
|||
|
||||
Future<void> _removePlayer() async {
|
||||
await prefs.remove('player');
|
||||
await _setDefaultNickname("dan63047");
|
||||
await _setDefaultNickname("6098518e3d5155e6ec429cdc");
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio_multiplayer_replay.dart';
|
||||
import 'package:tetra_stats/services/crud_exceptions.dart';
|
||||
import 'package:tetra_stats/views/compare_view.dart' show CompareThingy, CompareBoolThingy;
|
||||
|
@ -62,8 +64,11 @@ class TlMatchResultState extends State<TlMatchResultView> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
Widget buildComparison(bool bigScreen, bool showMobileSelector){
|
||||
return FutureBuilder(future: replayData, builder: (context, snapshot){
|
||||
Widget buildComparison(double width, bool showMobileSelector){
|
||||
bool bigScreen = width >= 768;
|
||||
return SizedBox(
|
||||
width: width,
|
||||
child: FutureBuilder(future: replayData, builder: (context, snapshot){
|
||||
late Duration time;
|
||||
late String readableTime;
|
||||
late String reason;
|
||||
|
@ -477,7 +482,8 @@ class TlMatchResultState extends State<TlMatchResultView> {
|
|||
],
|
||||
)
|
||||
);
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildRoundSelector(double width){
|
||||
|
@ -684,21 +690,17 @@ class TlMatchResultState extends State<TlMatchResultView> {
|
|||
return Center(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(maxWidth: 768),
|
||||
child: buildComparison(viewportWidth > 768, true)
|
||||
child: buildComparison(viewportWidth, true)
|
||||
),
|
||||
);
|
||||
} else {
|
||||
double comparisonWidth = viewportWidth - 450 - 16;
|
||||
comparisonWidth = comparisonWidth > 768 ? 768 : comparisonWidth;
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 768,
|
||||
child: buildComparison(true, false)
|
||||
),
|
||||
Container(
|
||||
constraints: const BoxConstraints(maxWidth: 768),
|
||||
child: buildRoundSelector(max(viewportWidth-768-16, 200)),
|
||||
)
|
||||
buildComparison(comparisonWidth, false),
|
||||
buildRoundSelector(450)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,10 @@ class Graphs extends StatelessWidget{
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double attack = apm / 60 * 0.4;
|
||||
double speed = pps / 3.75;
|
||||
double defense = nerdStats.dss * 1.15;
|
||||
double cheese = nerdStats.cheese / 110;
|
||||
return Wrap(
|
||||
direction: Axis.horizontal,
|
||||
alignment: WrapAlignment.center,
|
||||
|
@ -27,7 +31,7 @@ class Graphs extends StatelessWidget{
|
|||
crossAxisAlignment: WrapCrossAlignment.start,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
children: [
|
||||
Padding(
|
||||
if (true) Padding( // vs graph
|
||||
padding: const EdgeInsets.fromLTRB(18, 0, 18, 44),
|
||||
child: SizedBox(
|
||||
height: 310,
|
||||
|
@ -86,7 +90,7 @@ class Graphs extends StatelessWidget{
|
|||
borderColor: Colors.transparent,
|
||||
dataEntries: [
|
||||
const RadarEntry(value: 0),
|
||||
const RadarEntry(value: 0),
|
||||
const RadarEntry(value: 180),
|
||||
const RadarEntry(value: 0),
|
||||
const RadarEntry(value: 0),
|
||||
const RadarEntry(value: 0),
|
||||
|
@ -104,7 +108,60 @@ class Graphs extends StatelessWidget{
|
|||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
Padding( // sq graph
|
||||
padding: const EdgeInsets.fromLTRB(18, 0, 18, 44),
|
||||
child: SizedBox(
|
||||
height: 310,
|
||||
width: 310,
|
||||
child: RadarChart(
|
||||
RadarChartData(
|
||||
radarShape: RadarShape.polygon,
|
||||
tickCount: 4,
|
||||
ticksTextStyle: const TextStyle(color: Colors.white24, fontSize: 10),
|
||||
radarBorderData: const BorderSide(color: Colors.transparent, width: 1),
|
||||
gridBorderData: const BorderSide(color: Colors.white24, width: 1),
|
||||
tickBorderData: const BorderSide(color: Colors.transparent, width: 1),
|
||||
titleTextStyle: const TextStyle(height: 1.1),
|
||||
radarTouchData: RadarTouchData(),
|
||||
getTitle: (index, angle) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return RadarChartTitle(text: 'Attack\n${f2.format(apm)} APM', angle: 0, positionPercentageOffset: 0.05);
|
||||
case 1:
|
||||
return RadarChartTitle(text: 'Speed\n${f2.format(pps)} PPS', angle: 0, positionPercentageOffset: 0.05);
|
||||
case 2:
|
||||
return RadarChartTitle(text: 'Defense\n${f2.format(nerdStats.dss)} DS/S', angle: angle + 180, positionPercentageOffset: 0.05);
|
||||
case 3:
|
||||
return RadarChartTitle(text: 'Cheese\n${f3.format(nerdStats.cheese)}', angle: 0, positionPercentageOffset: 0.05);
|
||||
default:
|
||||
return const RadarChartTitle(text: '');
|
||||
}
|
||||
},
|
||||
dataSets: [
|
||||
RadarDataSet(
|
||||
dataEntries: [
|
||||
RadarEntry(value: attack),
|
||||
RadarEntry(value: speed),
|
||||
RadarEntry(value: defense),
|
||||
RadarEntry(value: cheese),
|
||||
],
|
||||
),
|
||||
RadarDataSet(
|
||||
fillColor: Colors.transparent,
|
||||
borderColor: Colors.transparent,
|
||||
dataEntries: [
|
||||
const RadarEntry(value: 0),
|
||||
const RadarEntry(value: 1.2),
|
||||
const RadarEntry(value: 0),
|
||||
const RadarEntry(value: 0),
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
Padding( // psq graph
|
||||
padding: const EdgeInsets.fromLTRB(18, 0, 18, 44),
|
||||
child: SizedBox(
|
||||
height: 310,
|
||||
|
@ -126,7 +183,7 @@ class Graphs extends StatelessWidget{
|
|||
case 1:
|
||||
return RadarChartTitle(text: 'Stride\n${percentage.format(playstyle.stride)}', angle: 0, positionPercentageOffset: 0.05);
|
||||
case 2:
|
||||
return RadarChartTitle(text: 'Inf Ds\n${percentage.format(playstyle.infds)}', angle: angle + 180, positionPercentageOffset: 0.05);
|
||||
return RadarChartTitle(text: 'Inf DS\n${percentage.format(playstyle.infds)}', angle: angle + 180, positionPercentageOffset: 0.05);
|
||||
case 3:
|
||||
return RadarChartTitle(text: 'Plonk\n${percentage.format(playstyle.plonk)}', angle: 0, positionPercentageOffset: 0.05);
|
||||
default:
|
||||
|
@ -147,19 +204,9 @@ class Graphs extends StatelessWidget{
|
|||
borderColor: Colors.transparent,
|
||||
dataEntries: [
|
||||
const RadarEntry(value: 0),
|
||||
const RadarEntry(value: 1),
|
||||
const RadarEntry(value: 0),
|
||||
const RadarEntry(value: 0),
|
||||
const RadarEntry(value: 0),
|
||||
],
|
||||
),
|
||||
RadarDataSet(
|
||||
fillColor: Colors.transparent,
|
||||
borderColor: Colors.transparent,
|
||||
dataEntries: [
|
||||
const RadarEntry(value: 1),
|
||||
const RadarEntry(value: 1),
|
||||
const RadarEntry(value: 1),
|
||||
const RadarEntry(value: 1),
|
||||
],
|
||||
)
|
||||
],
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
|
||||
class TLProgress extends StatelessWidget{
|
||||
final double tr;
|
||||
final String rank;
|
||||
final int position;
|
||||
final String? nextRank;
|
||||
final String? previousRank;
|
||||
final int nextRankPosition;
|
||||
final int previousRankPosition;
|
||||
final double? nextRankTRcutoff;
|
||||
final double? previousRankTRcutoff;
|
||||
final double? nextRankTRcutoffTarget;
|
||||
final double? previousRankTRcutoffTarget;
|
||||
|
||||
const TLProgress({super.key, required this.tr, required this.rank, required this.position, required this.nextRankPosition, required this.previousRankPosition, this.nextRank, this.previousRank, this.nextRankTRcutoff, this.previousRankTRcutoff, this.nextRankTRcutoffTarget, this.previousRankTRcutoffTarget});
|
||||
|
||||
double getBarPosition(){
|
||||
return 1 - (position - nextRankPosition)/(previousRankPosition - nextRankPosition);
|
||||
}
|
||||
|
||||
double? getBarTR(){
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
print(getBarPosition());
|
||||
// return Container(
|
||||
// alignment: Alignment.centerLeft,
|
||||
// height: 50,
|
||||
// width: MediaQuery.of(context).size.width,
|
||||
// color: Colors.blue,
|
||||
// child: Container(
|
||||
// width: MediaQuery.of(context).size.width / 2,
|
||||
// height: 50,
|
||||
// color: Colors.red,
|
||||
// ),
|
||||
// );
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SfLinearGauge(
|
||||
minimum: 0,
|
||||
maximum: 1,
|
||||
interval: 1,
|
||||
ranges: [LinearGaugeRange(endValue: getBarPosition(), color: Colors.cyanAccent,)],
|
||||
markerPointers: [LinearShapePointer(value: getBarPosition(), position: LinearElementPosition.inside, shapeType: LinearShapePointerType.triangle, color: Colors.white, height: 20),
|
||||
LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: getBarPosition(), child: Text(NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(position)))],
|
||||
isMirrored: true,
|
||||
showTicks: true,
|
||||
showLabels: true
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import 'package:tetra_stats/utils/numers_formats.dart';
|
|||
import 'package:tetra_stats/widgets/gauget_num.dart';
|
||||
import 'package:tetra_stats/widgets/graphs.dart';
|
||||
import 'package:tetra_stats/widgets/stat_sell_num.dart';
|
||||
import 'package:tetra_stats/widgets/tl_progress_bar.dart';
|
||||
|
||||
var fDiff = NumberFormat("+#,###.###;-#,###.###");
|
||||
var intFDiff = NumberFormat("+#,###;-#,###");
|
||||
|
@ -139,20 +140,36 @@ class _TLThingyState extends State<TLThingy> {
|
|||
),
|
||||
],
|
||||
),
|
||||
if (currentTl.gamesPlayed >= 10 && currentTl.rd! < 100 && currentTl.nextAt >=0 && currentTl.prevAt >= 0) Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SfLinearGauge(
|
||||
minimum: currentTl.nextAt.toDouble(),
|
||||
maximum: currentTl.prevAt.toDouble(),
|
||||
interval: currentTl.prevAt.toDouble() - currentTl.nextAt.toDouble(),
|
||||
ranges: [LinearGaugeRange(startValue: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() : currentTl.prevAt.toDouble(), endValue: currentTl.prevAt.toDouble(), color: Colors.cyanAccent,)],
|
||||
markerPointers: [LinearShapePointer(value: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() : currentTl.prevAt.toDouble(), position: LinearElementPosition.inside, shapeType: LinearShapePointerType.triangle, color: Colors.white, height: 20),
|
||||
LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() : currentTl.prevAt.toDouble(), child: Text(NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(currentTl.standing)))],
|
||||
isAxisInversed: true,
|
||||
isMirrored: true,
|
||||
showTicks: true,
|
||||
showLabels: true
|
||||
),
|
||||
if (currentTl.gamesPlayed >= 10 && currentTl.rd! < 100 && currentTl.nextAt >=0 && currentTl.prevAt >= 0)
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(8.0),
|
||||
// child: SfLinearGauge(
|
||||
// minimum: currentTl.nextAt.toDouble(),
|
||||
// maximum: currentTl.prevAt.toDouble(),
|
||||
// interval: currentTl.prevAt.toDouble() - currentTl.nextAt.toDouble(),
|
||||
// ranges: [
|
||||
// LinearGaugeRange(startValue: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() : currentTl.prevAt.toDouble(), endValue: currentTl.prevAt.toDouble(), color: Colors.cyanAccent, position: LinearElementPosition.cross,),
|
||||
// //LinearGaugeRange(startValue: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() + 500.00 : currentTl.prevAt.toDouble(), endValue: currentTl.prevAt.toDouble(), color: Colors.amber, position: LinearElementPosition.inside,)
|
||||
// ],
|
||||
// markerPointers: [LinearShapePointer(value: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() : currentTl.prevAt.toDouble(), position: LinearElementPosition.inside, shapeType: LinearShapePointerType.triangle, color: Colors.white, height: 20),
|
||||
// LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() : currentTl.prevAt.toDouble(), child: Text(NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(currentTl.standing)))],
|
||||
// isAxisInversed: true,
|
||||
// isMirrored: true,
|
||||
// showTicks: true,
|
||||
// showLabels: true
|
||||
// ),
|
||||
// ),
|
||||
TLProgress(
|
||||
tr: currentTl.rating,
|
||||
rank: currentTl.rank,
|
||||
position: currentTl.standing,
|
||||
nextRankPosition: currentTl.nextAt,
|
||||
previousRankPosition: currentTl.prevAt,
|
||||
previousRankTRcutoff: widget.thatRankCutoff,
|
||||
previousRankTRcutoffTarget: widget.thatRankTarget,
|
||||
nextRankTRcutoff: widget.nextRankCutoff,
|
||||
nextRankTRcutoffTarget: widget.nextRankTarget,
|
||||
nextRank: widget.tl.nextRank
|
||||
),
|
||||
if (currentTl.gamesPlayed < 10)
|
||||
Text(t.gamesUntilRanked(left: 10 - currentTl.gamesPlayed),
|
||||
|
@ -323,7 +340,7 @@ class _TLThingyState extends State<TLThingy> {
|
|||
if (oldTl?.estTr?.esttr != null) TextSpan(text: comparef.format(currentTl.estTr!.esttr - oldTl!.estTr!.esttr), style: TextStyle(
|
||||
color: oldTl!.estTr!.esttr > currentTl.estTr!.esttr ? Colors.redAccent : Colors.greenAccent
|
||||
),),
|
||||
if (oldTl?.estTr?.esttr != null && widget.lbPositions?.estTr != null) const TextSpan(text: " • "),
|
||||
if (oldTl?.estTr?.esttr != null || widget.lbPositions?.estTr != null) const TextSpan(text: " • "),
|
||||
if (widget.lbPositions?.estTr != null) TextSpan(text: widget.lbPositions!.estTr!.position >= 1000 ? "${t.top} ${f2.format(widget.lbPositions!.estTr!.percentage*100)}%" : "№${widget.lbPositions!.estTr!.position}", style: TextStyle(color: getColorOfRank(widget.lbPositions!.estTr!.position))),
|
||||
if (widget.lbPositions?.estTr != null) const TextSpan(text: " • "),
|
||||
TextSpan(text: "Glicko: ${f2.format(currentTl.estTr!.estglicko)}")
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
"verdictGeneral": "$n $verdict than $rank rank average",
|
||||
"verdictBetter": "better",
|
||||
"verdictWorse": "worse",
|
||||
"smooth": "Smooth",
|
||||
"gamesUntilRanked": "${left} games until being ranked",
|
||||
"nerdStats": "Nerd Stats",
|
||||
"playersYouTrack": "Players you track",
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
"verdictGeneral": "$verdict среднего $rank ранга на $n",
|
||||
"verdictBetter": "Лучше",
|
||||
"verdictWorse": "Хуже",
|
||||
"smooth": "Гладкий",
|
||||
"gamesUntilRanked": "${left} матчей до получения рейтинга",
|
||||
"nerdStats": "Для задротов",
|
||||
"playersYouTrack": "Отслеживаемые игроки",
|
||||
|
|
Loading…
Reference in New Issue