teto service now caching data + TL match view
Also added weights constants for nerd stats
This commit is contained in:
parent
5d5523ce06
commit
ffbe76e5cc
|
@ -5,6 +5,18 @@ import 'dart:developer' as developer;
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
const double noTrRd = 60.9;
|
||||||
|
const double apmWeight = 1;
|
||||||
|
const double ppsWeight = 45;
|
||||||
|
const double vsWeight = 0.444;
|
||||||
|
const double appWeight = 185;
|
||||||
|
const double dssWeight = 175;
|
||||||
|
const double dspWeight = 450;
|
||||||
|
const double appdspWeight = 140;
|
||||||
|
const double vsapmWeight = 60;
|
||||||
|
const double cheeseWeight = 1.25;
|
||||||
|
const double gbeWeight = 315;
|
||||||
|
|
||||||
Duration doubleSecondsToDuration(double value) {
|
Duration doubleSecondsToDuration(double value) {
|
||||||
value = value * 1000000;
|
value = value * 1000000;
|
||||||
return Duration(microseconds: value.floor());
|
return Duration(microseconds: value.floor());
|
||||||
|
@ -578,42 +590,46 @@ class TetraLeagueAlphaRecord{
|
||||||
}
|
}
|
||||||
|
|
||||||
class EndContextMulti {
|
class EndContextMulti {
|
||||||
String? userId;
|
late String userId;
|
||||||
String? username;
|
late String username;
|
||||||
int? naturalOrder;
|
late int naturalOrder;
|
||||||
int? inputs;
|
late int inputs;
|
||||||
int? piecesPlaced;
|
late int piecesPlaced;
|
||||||
Handling? handling;
|
late Handling handling;
|
||||||
int? points;
|
late int points;
|
||||||
int? wins;
|
late int wins;
|
||||||
double? secondary;
|
late double secondary;
|
||||||
List<double>? secondaryTracking;
|
late List<double> secondaryTracking;
|
||||||
double? tertiary;
|
late double tertiary;
|
||||||
List<double>? tertiaryTracking;
|
late List<double> tertiaryTracking;
|
||||||
double? extra;
|
late double extra;
|
||||||
List<double>? extraTracking;
|
late List<double> extraTracking;
|
||||||
bool? success;
|
late bool success;
|
||||||
|
late NerdStats nerdStats;
|
||||||
|
late EstTr estTr;
|
||||||
|
late Playstyle playstyle;
|
||||||
|
|
||||||
EndContextMulti(
|
EndContextMulti(
|
||||||
{this.userId,
|
{required this.userId,
|
||||||
this.naturalOrder,
|
required this.username,
|
||||||
this.inputs,
|
required this.naturalOrder,
|
||||||
this.piecesPlaced,
|
required this.inputs,
|
||||||
this.handling,
|
required this.piecesPlaced,
|
||||||
this.points,
|
required this.handling,
|
||||||
this.wins,
|
required this.points,
|
||||||
this.secondary,
|
required this.wins,
|
||||||
this.secondaryTracking,
|
required this.secondary,
|
||||||
this.tertiary,
|
required this.secondaryTracking,
|
||||||
this.tertiaryTracking,
|
required this.tertiary,
|
||||||
this.extra,
|
required this.tertiaryTracking,
|
||||||
this.extraTracking,
|
required this.extra,
|
||||||
this.success});
|
required this.extraTracking,
|
||||||
|
required this.success});
|
||||||
|
|
||||||
EndContextMulti.fromJson(Map<String, dynamic> json) {
|
EndContextMulti.fromJson(Map<String, dynamic> json) {
|
||||||
userId = json['user']['_id'];
|
userId = json['user']['_id'];
|
||||||
username = json['user']['username'];
|
username = json['user']['username'];
|
||||||
handling = json['handling'] != null ? Handling.fromJson(json['handling']) : null;
|
handling = Handling.fromJson(json['handling']);
|
||||||
success = json['success'];
|
success = json['success'];
|
||||||
inputs = json['inputs'];
|
inputs = json['inputs'];
|
||||||
piecesPlaced = json['piecesplaced'];
|
piecesPlaced = json['piecesplaced'];
|
||||||
|
@ -626,15 +642,16 @@ class EndContextMulti {
|
||||||
tertiaryTracking = json['points']['tertiaryAvgTracking'].cast<double>();
|
tertiaryTracking = json['points']['tertiaryAvgTracking'].cast<double>();
|
||||||
extra = json['points']['extra']['vs'];
|
extra = json['points']['extra']['vs'];
|
||||||
extraTracking = json['points']['extraAvgTracking']['aggregatestats___vsscore'].cast<double>();
|
extraTracking = json['points']['extraAvgTracking']['aggregatestats___vsscore'].cast<double>();
|
||||||
|
nerdStats = NerdStats(secondary, tertiary, extra);
|
||||||
|
estTr = EstTr(secondary, tertiary, extra, noTrRd, nerdStats.app, nerdStats.dss, nerdStats.dsp, nerdStats.gbe);
|
||||||
|
playstyle = Playstyle(secondary, tertiary, nerdStats.app, nerdStats.vsapm, nerdStats.dsp, nerdStats.gbe, estTr.srarea, estTr.statrank);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final Map<String, dynamic> data = <String, dynamic>{};
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
data['user']['_id'] = userId;
|
data['user']['_id'] = userId;
|
||||||
data['user']['username'] = username;
|
data['user']['username'] = username;
|
||||||
if (handling != null) {
|
data['handling'] = handling.toJson();
|
||||||
data['handling'] = handling!.toJson();
|
|
||||||
}
|
|
||||||
data['success'] = success;
|
data['success'] = success;
|
||||||
data['inputs'] = inputs;
|
data['inputs'] = inputs;
|
||||||
data['piecesplaced'] = piecesPlaced;
|
data['piecesplaced'] = piecesPlaced;
|
||||||
|
|
|
@ -29,6 +29,8 @@ const String createTetrioUsersToTrack = '''
|
||||||
|
|
||||||
class TetrioService extends DB {
|
class TetrioService extends DB {
|
||||||
Map<String, List<TetrioPlayer>> _players = {};
|
Map<String, List<TetrioPlayer>> _players = {};
|
||||||
|
final Map<String, TetrioPlayer> _playersCache = {};
|
||||||
|
final Map<String, TetraLeagueAlphaStream> _tlStreamsCache = {}; // i'm trying to respect oskware api It should look something like {"cached_until": TetrioPlayer}
|
||||||
static final TetrioService _shared = TetrioService._sharedInstance();
|
static final TetrioService _shared = TetrioService._sharedInstance();
|
||||||
factory TetrioService() => _shared;
|
factory TetrioService() => _shared;
|
||||||
late final StreamController<Map<String, List<TetrioPlayer>>> _tetrioStreamController;
|
late final StreamController<Map<String, List<TetrioPlayer>>> _tetrioStreamController;
|
||||||
|
@ -41,16 +43,16 @@ class TetrioService extends DB {
|
||||||
@override
|
@override
|
||||||
Future<void> open() async {
|
Future<void> open() async {
|
||||||
await super.open();
|
await super.open();
|
||||||
await _cachePlayers();
|
await _loadPlayers();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<Map<String, List<TetrioPlayer>>> get allPlayers => _tetrioStreamController.stream;
|
Stream<Map<String, List<TetrioPlayer>>> get allPlayers => _tetrioStreamController.stream;
|
||||||
|
|
||||||
Future<void> _cachePlayers() async {
|
Future<void> _loadPlayers() async {
|
||||||
final allPlayers = await getAllPlayers();
|
final allPlayers = await getAllPlayers();
|
||||||
_players = allPlayers.toList().first; // ???
|
_players = allPlayers.toList().first; // ???
|
||||||
_tetrioStreamController.add(_players);
|
_tetrioStreamController.add(_players);
|
||||||
developer.log("_cachePlayers: $_players", name: "services/tetrio_crud");
|
developer.log("_loadPlayers: $_players", name: "services/tetrio_crud");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deletePlayer(String id) async {
|
Future<void> deletePlayer(String id) async {
|
||||||
|
@ -72,6 +74,19 @@ class TetrioService extends DB {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<TetraLeagueAlphaStream> getTLStream(String userID) async {
|
Future<TetraLeagueAlphaStream> getTLStream(String userID) async {
|
||||||
|
try{
|
||||||
|
var cached = _tlStreamsCache.entries.firstWhere((element) => element.value.userId == userID);
|
||||||
|
if (DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true).isAfter(DateTime.now())){
|
||||||
|
developer.log("getTLStream: Stream $userID retrieved from cache, that expires ${DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true)}", name: "services/tetrio_crud");
|
||||||
|
return cached.value;
|
||||||
|
}else{
|
||||||
|
_tlStreamsCache.remove(cached.key);
|
||||||
|
developer.log("getTLStream: Cached stream $userID expired (${DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true)})", name: "services/tetrio_crud");
|
||||||
|
}
|
||||||
|
}catch(e){
|
||||||
|
developer.log("getTLStream: Trying to retrieve stream $userID", name: "services/tetrio_crud");
|
||||||
|
}
|
||||||
|
|
||||||
var url = Uri.https('ch.tetr.io', 'api/streams/league_userrecent_${userID.toLowerCase().trim()}');
|
var url = Uri.https('ch.tetr.io', 'api/streams/league_userrecent_${userID.toLowerCase().trim()}');
|
||||||
final response = await http.get(url);
|
final response = await http.get(url);
|
||||||
|
|
||||||
|
@ -83,13 +98,15 @@ class TetrioService extends DB {
|
||||||
// await ensureDbIsOpen();
|
// await ensureDbIsOpen();
|
||||||
// storeState(player);
|
// storeState(player);
|
||||||
// }
|
// }
|
||||||
|
developer.log("getTLStream: $userID stream retrieved and cached", name: "services/tetrio_crud");
|
||||||
|
_tlStreamsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = stream;
|
||||||
return stream;
|
return stream;
|
||||||
} else {
|
} else {
|
||||||
developer.log("getTLStream User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
developer.log("getTLStream User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||||
throw Exception("User doesn't exist");
|
throw Exception("User doesn't exist");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
developer.log("getTLStream Failed to fetch player", name: "services/tetrio_crud", error: response.statusCode);
|
developer.log("getTLStream Failed to fetch stream", name: "services/tetrio_crud", error: response.statusCode);
|
||||||
throw Exception('Failed to fetch player');
|
throw Exception('Failed to fetch player');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,6 +225,19 @@ class TetrioService extends DB {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<TetrioPlayer> fetchPlayer(String user, bool addToDB) async {
|
Future<TetrioPlayer> fetchPlayer(String user, bool addToDB) async {
|
||||||
|
try{
|
||||||
|
var cached = _playersCache.entries.firstWhere((element) => element.value.userId == user || element.value.username == user);
|
||||||
|
if (DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true).isAfter(DateTime.now())){
|
||||||
|
developer.log("fetchPlayer: User $user retrieved from cache, that expires ${DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true)}", name: "services/tetrio_crud");
|
||||||
|
return cached.value;
|
||||||
|
}else{
|
||||||
|
_playersCache.remove(cached.key);
|
||||||
|
developer.log("fetchPlayer: Cached user $user expired (${DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true)})", name: "services/tetrio_crud");
|
||||||
|
}
|
||||||
|
}catch(e){
|
||||||
|
developer.log("fetchPlayer: Trying to retrieve $user", name: "services/tetrio_crud");
|
||||||
|
}
|
||||||
|
|
||||||
var url = Uri.https('ch.tetr.io', 'api/users/${user.toLowerCase().trim()}');
|
var url = Uri.https('ch.tetr.io', 'api/users/${user.toLowerCase().trim()}');
|
||||||
final response = await http.get(url);
|
final response = await http.get(url);
|
||||||
|
|
||||||
|
@ -219,13 +249,15 @@ class TetrioService extends DB {
|
||||||
await ensureDbIsOpen();
|
await ensureDbIsOpen();
|
||||||
storeState(player);
|
storeState(player);
|
||||||
}
|
}
|
||||||
|
developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud");
|
||||||
|
_playersCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = player;
|
||||||
return player;
|
return player;
|
||||||
} else {
|
} else {
|
||||||
developer.log("fetchTetrioPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
developer.log("fetchPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||||
throw Exception("User doesn't exist");
|
throw Exception("User doesn't exist");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
developer.log("fetchTetrioPlayer Failed to fetch player", name: "services/tetrio_crud", error: response.statusCode);
|
developer.log("fetchPlayer Failed to fetch player", name: "services/tetrio_crud", error: response.statusCode);
|
||||||
throw Exception('Failed to fetch player');
|
throw Exception('Failed to fetch player');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -547,43 +547,43 @@ class CompareState extends State<CompareView> {
|
||||||
RadarEntry(
|
RadarEntry(
|
||||||
value: theGreenSide!
|
value: theGreenSide!
|
||||||
.tlSeason1.apm! *
|
.tlSeason1.apm! *
|
||||||
1),
|
apmWeight),
|
||||||
RadarEntry(
|
RadarEntry(
|
||||||
value: theGreenSide!
|
value: theGreenSide!
|
||||||
.tlSeason1.pps! *
|
.tlSeason1.pps! *
|
||||||
45),
|
ppsWeight),
|
||||||
RadarEntry(
|
RadarEntry(
|
||||||
value: theGreenSide!
|
value: theGreenSide!
|
||||||
.tlSeason1.vs! *
|
.tlSeason1.vs! *
|
||||||
0.444),
|
vsWeight),
|
||||||
RadarEntry(
|
RadarEntry(
|
||||||
value: theGreenSide!.tlSeason1
|
value: theGreenSide!.tlSeason1
|
||||||
.nerdStats!.app *
|
.nerdStats!.app *
|
||||||
185),
|
appWeight),
|
||||||
RadarEntry(
|
RadarEntry(
|
||||||
value: theGreenSide!.tlSeason1
|
value: theGreenSide!.tlSeason1
|
||||||
.nerdStats!.dss *
|
.nerdStats!.dss *
|
||||||
175),
|
dssWeight),
|
||||||
RadarEntry(
|
RadarEntry(
|
||||||
value: theGreenSide!.tlSeason1
|
value: theGreenSide!.tlSeason1
|
||||||
.nerdStats!.dsp *
|
.nerdStats!.dsp *
|
||||||
450),
|
dspWeight),
|
||||||
RadarEntry(
|
RadarEntry(
|
||||||
value: theGreenSide!.tlSeason1
|
value: theGreenSide!.tlSeason1
|
||||||
.nerdStats!.appdsp *
|
.nerdStats!.appdsp *
|
||||||
140),
|
appdspWeight),
|
||||||
RadarEntry(
|
RadarEntry(
|
||||||
value: theGreenSide!.tlSeason1
|
value: theGreenSide!.tlSeason1
|
||||||
.nerdStats!.vsapm *
|
.nerdStats!.vsapm *
|
||||||
60),
|
vsapmWeight),
|
||||||
RadarEntry(
|
RadarEntry(
|
||||||
value: theGreenSide!.tlSeason1
|
value: theGreenSide!.tlSeason1
|
||||||
.nerdStats!.cheese *
|
.nerdStats!.cheese *
|
||||||
1.25),
|
cheeseWeight),
|
||||||
RadarEntry(
|
RadarEntry(
|
||||||
value: theGreenSide!.tlSeason1
|
value: theGreenSide!.tlSeason1
|
||||||
.nerdStats!.gbe *
|
.nerdStats!.gbe *
|
||||||
315),
|
gbeWeight),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
RadarDataSet(
|
RadarDataSet(
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'dart:developer' as developer;
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||||
import 'package:tetra_stats/services/tetrio_crud.dart';
|
import 'package:tetra_stats/services/tetrio_crud.dart';
|
||||||
import 'package:tetra_stats/services/crud_exceptions.dart';
|
import 'package:tetra_stats/services/crud_exceptions.dart';
|
||||||
|
import 'package:tetra_stats/views/tl_match_view.dart' show TlMatchResultView;
|
||||||
import 'package:tetra_stats/widgets/stat_sell_num.dart';
|
import 'package:tetra_stats/widgets/stat_sell_num.dart';
|
||||||
import 'package:tetra_stats/widgets/tl_thingy.dart';
|
import 'package:tetra_stats/widgets/tl_thingy.dart';
|
||||||
import 'package:tetra_stats/widgets/user_thingy.dart';
|
import 'package:tetra_stats/widgets/user_thingy.dart';
|
||||||
|
@ -81,7 +81,6 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
_getPreferences()
|
_getPreferences()
|
||||||
.then((value) => changePlayer(prefs.getString("player") ?? "dan63047"));
|
.then((value) => changePlayer(prefs.getString("player") ?? "dan63047"));
|
||||||
super.initState();
|
super.initState();
|
||||||
developer.log("Main view initialized", name: "main_view");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -89,7 +88,6 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
_tabController.dispose();
|
_tabController.dispose();
|
||||||
_scrollController.dispose();
|
_scrollController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
developer.log("Main view disposed", name: "main_view");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _getPreferences() async {
|
Future<void> _getPreferences() async {
|
||||||
|
@ -155,10 +153,6 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
),
|
),
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
||||||
// const PopupMenuItem(
|
|
||||||
// value: "/compare",
|
|
||||||
// child: Text('Compare'),
|
|
||||||
// ),
|
|
||||||
const PopupMenuItem(
|
const PopupMenuItem(
|
||||||
value: "/states",
|
value: "/states",
|
||||||
child: Text('Show stored data'),
|
child: Text('Show stored data'),
|
||||||
|
@ -182,7 +176,6 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
child: FutureBuilder<TetrioPlayer>(
|
child: FutureBuilder<TetrioPlayer>(
|
||||||
future: me,
|
future: me,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
developer.log("builder ($context): $snapshot", name: "main_view");
|
|
||||||
switch (snapshot.connectionState) {
|
switch (snapshot.connectionState) {
|
||||||
case ConnectionState.none:
|
case ConnectionState.none:
|
||||||
return const Center(
|
return const Center(
|
||||||
|
@ -225,10 +218,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
isScrollable: true,
|
isScrollable: true,
|
||||||
tabs: myTabs,
|
tabs: myTabs,
|
||||||
onTap: (int tabId) {
|
onTap: (int tabId) {
|
||||||
setState(() {
|
setState(() {});
|
||||||
developer.log("Tab changed to $tabId",
|
|
||||||
name: "main_view");
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -350,8 +340,6 @@ class _NavDrawerState extends State<NavDrawer> {
|
||||||
leading: const Icon(Icons.home),
|
leading: const Icon(Icons.home),
|
||||||
title: Text(homePlayerNickname),
|
title: Text(homePlayerNickname),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
developer.log("Navigator changed player",
|
|
||||||
name: "main_view");
|
|
||||||
widget.changePlayer(
|
widget.changePlayer(
|
||||||
prefs.getString("player") ?? "dan63047");
|
prefs.getString("player") ?? "dan63047");
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
@ -367,8 +355,6 @@ class _NavDrawerState extends State<NavDrawer> {
|
||||||
title: Text(
|
title: Text(
|
||||||
allPlayers[keys[index]]?.last.username as String),
|
allPlayers[keys[index]]?.last.username as String),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
developer.log("Navigator changed player",
|
|
||||||
name: "main_view");
|
|
||||||
widget.changePlayer(keys[index]);
|
widget.changePlayer(keys[index]);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
|
@ -401,10 +387,7 @@ class _TLRecords extends StatelessWidget {
|
||||||
child: CircularProgressIndicator(color: Colors.white));
|
child: CircularProgressIndicator(color: Colors.white));
|
||||||
case ConnectionState.done:
|
case ConnectionState.done:
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
return Text(snapshot.error.toString(),
|
return Text(snapshot.error.toString(), style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28));
|
||||||
style: TextStyle(
|
|
||||||
fontFamily: "Eurostile Round Extended",
|
|
||||||
fontSize: 28));
|
|
||||||
} else {
|
} else {
|
||||||
return ListView(
|
return ListView(
|
||||||
physics: const ClampingScrollPhysics(),
|
physics: const ClampingScrollPhysics(),
|
||||||
|
@ -414,7 +397,7 @@ class _TLRecords extends StatelessWidget {
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontFamily: "Eurostile Round Extended",
|
fontFamily: "Eurostile Round Extended",
|
||||||
fontSize: 28,)),
|
fontSize: 28,)),
|
||||||
title: Text("vs. ${value.endContext.firstWhere((element) => element.userId != userID).username!}"),
|
title: Text("vs. ${value.endContext.firstWhere((element) => element.userId != userID).username}"),
|
||||||
subtitle: Text(dateFormat.format(value.timestamp!)),
|
subtitle: Text(dateFormat.format(value.timestamp!)),
|
||||||
trailing: Column(mainAxisAlignment: MainAxisAlignment.end,
|
trailing: Column(mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
|
@ -422,14 +405,14 @@ class _TLRecords extends StatelessWidget {
|
||||||
Text("${f2.format(value.endContext.firstWhere((element) => element.userId == userID).tertiary)} : ${f2.format(value.endContext.firstWhere((element) => element.userId != userID).tertiary)} PPS", style: TextStyle(height: 1.1)),
|
Text("${f2.format(value.endContext.firstWhere((element) => element.userId == userID).tertiary)} : ${f2.format(value.endContext.firstWhere((element) => element.userId != userID).tertiary)} PPS", style: TextStyle(height: 1.1)),
|
||||||
Text("${f2.format(value.endContext.firstWhere((element) => element.userId == userID).extra)} : ${f2.format(value.endContext.firstWhere((element) => element.userId != userID).extra)} VS", style: TextStyle(height: 1.1)),
|
Text("${f2.format(value.endContext.firstWhere((element) => element.userId == userID).extra)} : ${f2.format(value.endContext.firstWhere((element) => element.userId != userID).extra)} VS", style: TextStyle(height: 1.1)),
|
||||||
]),
|
]),
|
||||||
onTap: (){},
|
onTap: (){Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => TlMatchResultView(record: value, initPlayerId: userID),
|
||||||
|
),
|
||||||
|
);},
|
||||||
)]
|
)]
|
||||||
: [
|
: [const Text("No records",style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28))],
|
||||||
Text("No records",
|
|
||||||
style: TextStyle(
|
|
||||||
fontFamily: "Eurostile Round Extended",
|
|
||||||
fontSize: 28))
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,887 @@
|
||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||||
|
|
||||||
|
|
||||||
|
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
|
||||||
|
|
||||||
|
class TlMatchResultView extends StatefulWidget {
|
||||||
|
final TetraLeagueAlphaRecord record;
|
||||||
|
final String initPlayerId;
|
||||||
|
const TlMatchResultView({Key? key, required this.record, required this.initPlayerId})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => TlMatchResultState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class TlMatchResultState extends State<TlMatchResultView> {
|
||||||
|
late ScrollController _scrollController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState(){
|
||||||
|
_scrollController = ScrollController();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
bool bigScreen = MediaQuery.of(context).size.width > 768;
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(
|
||||||
|
"${widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).username.toUpperCase()} vs. ${widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).username.toUpperCase()} in TL match ${dateFormat.format(widget.record.timestamp!)}"),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
body: SafeArea(
|
||||||
|
child: NestedScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
|
headerSliverBuilder: (context, value) {
|
||||||
|
return [
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 16, 16, 32),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [Colors.green, Colors.transparent],
|
||||||
|
begin: Alignment.bottomCenter,
|
||||||
|
end: Alignment.topCenter,
|
||||||
|
stops: [0.0, 0.4],
|
||||||
|
)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
|
||||||
|
child: Column(children: [
|
||||||
|
Text(widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).username, style: const TextStyle(
|
||||||
|
fontFamily: "Eurostile Round Extended",
|
||||||
|
fontSize: 28)),
|
||||||
|
Text(widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).points.toString(), style: const TextStyle(
|
||||||
|
fontFamily: "Eurostile Round Extended",
|
||||||
|
fontSize: 42))
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.only(top: 16),
|
||||||
|
child: Text("VS"),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [Colors.red, Colors.transparent],
|
||||||
|
begin: Alignment.bottomCenter,
|
||||||
|
end: Alignment.topCenter,
|
||||||
|
stops: [0.0, 0.4],
|
||||||
|
)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
|
||||||
|
child: Column(children: [
|
||||||
|
Text(widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).username, style: const TextStyle(
|
||||||
|
fontFamily: "Eurostile Round Extended",
|
||||||
|
fontSize: 28)),
|
||||||
|
Text(widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).points.toString(), style: const TextStyle(
|
||||||
|
fontFamily: "Eurostile Round Extended",
|
||||||
|
fontSize: 42))
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SliverToBoxAdapter(
|
||||||
|
child: Divider(),
|
||||||
|
)
|
||||||
|
];
|
||||||
|
},
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
CompareThingy(
|
||||||
|
label: "APM",
|
||||||
|
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).secondary,
|
||||||
|
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).secondary,
|
||||||
|
fractionDigits: 2,
|
||||||
|
higherIsBetter: true,
|
||||||
|
),
|
||||||
|
CompareThingy(
|
||||||
|
label: "PPS",
|
||||||
|
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).tertiary,
|
||||||
|
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).tertiary,
|
||||||
|
fractionDigits: 2,
|
||||||
|
higherIsBetter: true,
|
||||||
|
),
|
||||||
|
CompareThingy(
|
||||||
|
label: "VS",
|
||||||
|
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).extra,
|
||||||
|
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).extra,
|
||||||
|
fractionDigits: 2,
|
||||||
|
higherIsBetter: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 16),
|
||||||
|
child: Text("Nerd Stats",
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: "Eurostile Round Extended",
|
||||||
|
fontSize: bigScreen ? 42 : 28)),
|
||||||
|
),
|
||||||
|
CompareThingy(
|
||||||
|
label: "APP",
|
||||||
|
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.app,
|
||||||
|
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.app,
|
||||||
|
fractionDigits: 3,
|
||||||
|
higherIsBetter: true,
|
||||||
|
),
|
||||||
|
CompareThingy(
|
||||||
|
label: "VS/APM",
|
||||||
|
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.vsapm,
|
||||||
|
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.vsapm,
|
||||||
|
fractionDigits: 3,
|
||||||
|
higherIsBetter: true,
|
||||||
|
),
|
||||||
|
CompareThingy(
|
||||||
|
label: "DS/S",
|
||||||
|
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.dss,
|
||||||
|
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.dss,
|
||||||
|
fractionDigits: 3,
|
||||||
|
higherIsBetter: true,
|
||||||
|
),
|
||||||
|
CompareThingy(
|
||||||
|
label: "DS/P",
|
||||||
|
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.dsp,
|
||||||
|
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.dsp,
|
||||||
|
fractionDigits: 3,
|
||||||
|
higherIsBetter: true,
|
||||||
|
),
|
||||||
|
CompareThingy(
|
||||||
|
label: "APP + DS/P",
|
||||||
|
greenSide:
|
||||||
|
widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.appdsp,
|
||||||
|
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.appdsp,
|
||||||
|
fractionDigits: 3,
|
||||||
|
higherIsBetter: true,
|
||||||
|
),
|
||||||
|
CompareThingy(
|
||||||
|
label: "Cheese",
|
||||||
|
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.cheese,
|
||||||
|
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.cheese,
|
||||||
|
fractionDigits: 2,
|
||||||
|
higherIsBetter: true,
|
||||||
|
),
|
||||||
|
CompareThingy(
|
||||||
|
label: "Garbage Eff.",
|
||||||
|
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.gbe,
|
||||||
|
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.gbe,
|
||||||
|
fractionDigits: 3,
|
||||||
|
higherIsBetter: true,
|
||||||
|
),
|
||||||
|
CompareThingy(
|
||||||
|
label: "Weighted APP",
|
||||||
|
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.nyaapp,
|
||||||
|
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.nyaapp,
|
||||||
|
fractionDigits: 3,
|
||||||
|
higherIsBetter: true,
|
||||||
|
),
|
||||||
|
CompareThingy(
|
||||||
|
label: "Area",
|
||||||
|
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.area,
|
||||||
|
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.area,
|
||||||
|
fractionDigits: 2,
|
||||||
|
higherIsBetter: true,
|
||||||
|
),
|
||||||
|
CompareThingy(
|
||||||
|
label: "Est. of TR",
|
||||||
|
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).estTr.esttr,
|
||||||
|
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).estTr.esttr,
|
||||||
|
fractionDigits: 2,
|
||||||
|
higherIsBetter: true,
|
||||||
|
),
|
||||||
|
Wrap(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
alignment: WrapAlignment.spaceAround,
|
||||||
|
spacing: 25,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.start,
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(20, 20, 20, 20),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 300,
|
||||||
|
width: 300,
|
||||||
|
child: RadarChart(
|
||||||
|
RadarChartData(
|
||||||
|
radarShape: RadarShape.polygon,
|
||||||
|
tickCount: 4,
|
||||||
|
ticksTextStyle: const TextStyle(
|
||||||
|
color: Colors.transparent,
|
||||||
|
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),
|
||||||
|
getTitle: (index, angle) {
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'APM',
|
||||||
|
angle: angle,
|
||||||
|
);
|
||||||
|
case 1:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'PPS',
|
||||||
|
angle: angle,
|
||||||
|
);
|
||||||
|
case 2:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'VS', angle: angle);
|
||||||
|
case 3:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'APP',
|
||||||
|
angle: angle + 180);
|
||||||
|
case 4:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'DS/S',
|
||||||
|
angle: angle + 180);
|
||||||
|
case 5:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'DS/P',
|
||||||
|
angle: angle + 180);
|
||||||
|
case 6:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'APP+DS/P',
|
||||||
|
angle: angle + 180);
|
||||||
|
case 7:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'VS/APM',
|
||||||
|
angle: angle + 180);
|
||||||
|
case 8:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'Cheese', angle: angle);
|
||||||
|
case 9:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'Gb Eff.', angle: angle);
|
||||||
|
default:
|
||||||
|
return const RadarChartTitle(
|
||||||
|
text: '');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataSets: [
|
||||||
|
RadarDataSet(
|
||||||
|
fillColor: const Color.fromARGB(
|
||||||
|
115, 76, 175, 79),
|
||||||
|
borderColor: Colors.green,
|
||||||
|
dataEntries: [
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).secondary * apmWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).tertiary * ppsWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).extra * vsWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.app * appWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.dss * dssWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.dsp * dspWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.appdsp * appdspWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.vsapm * vsapmWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.cheese * cheeseWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.gbe * gbeWeight),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
RadarDataSet(
|
||||||
|
fillColor: const Color.fromARGB(
|
||||||
|
115, 244, 67, 54),
|
||||||
|
borderColor: Colors.red,
|
||||||
|
dataEntries: [
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).secondary * apmWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).tertiary * ppsWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).extra * vsWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.app * appWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.dss * dssWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.dsp * dspWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.appdsp * appdspWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.vsapm * vsapmWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.cheese * cheeseWeight),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.gbe * gbeWeight),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
RadarDataSet(
|
||||||
|
fillColor: Colors.transparent,
|
||||||
|
borderColor: Colors.transparent,
|
||||||
|
dataEntries: [
|
||||||
|
const RadarEntry(value: 0),
|
||||||
|
const RadarEntry(value: 0),
|
||||||
|
const RadarEntry(value: 0),
|
||||||
|
const RadarEntry(value: 0),
|
||||||
|
const RadarEntry(value: 0),
|
||||||
|
const RadarEntry(value: 0),
|
||||||
|
const RadarEntry(value: 0),
|
||||||
|
const RadarEntry(value: 0),
|
||||||
|
const RadarEntry(value: 0),
|
||||||
|
const RadarEntry(value: 0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
swapAnimationDuration: const Duration(
|
||||||
|
milliseconds: 150), // Optional
|
||||||
|
swapAnimationCurve:
|
||||||
|
Curves.linear, // Optional
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(20, 20, 20, 20),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 300,
|
||||||
|
width: 300,
|
||||||
|
child: RadarChart(
|
||||||
|
RadarChartData(
|
||||||
|
radarShape: RadarShape.polygon,
|
||||||
|
tickCount: 4,
|
||||||
|
ticksTextStyle: const TextStyle(
|
||||||
|
color: Colors.transparent,
|
||||||
|
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),
|
||||||
|
getTitle: (index, angle) {
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'Opener',
|
||||||
|
angle: angle,
|
||||||
|
);
|
||||||
|
case 1:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'Stride',
|
||||||
|
angle: angle,
|
||||||
|
);
|
||||||
|
case 2:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'Inf Ds',
|
||||||
|
angle: angle + 180);
|
||||||
|
case 3:
|
||||||
|
return RadarChartTitle(
|
||||||
|
text: 'Plonk', angle: angle);
|
||||||
|
default:
|
||||||
|
return const RadarChartTitle(
|
||||||
|
text: '');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataSets: [
|
||||||
|
RadarDataSet(
|
||||||
|
fillColor: const Color.fromARGB(
|
||||||
|
115, 76, 175, 79),
|
||||||
|
borderColor: Colors.green,
|
||||||
|
dataEntries: [
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyle.opener),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyle.stride),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyle.infds),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyle.plonk),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
RadarDataSet(
|
||||||
|
fillColor: const Color.fromARGB(
|
||||||
|
115, 244, 67, 54),
|
||||||
|
borderColor: Colors.red,
|
||||||
|
dataEntries: [
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyle.opener),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyle.stride),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyle.infds),
|
||||||
|
RadarEntry(value: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyle.plonk),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
RadarDataSet(
|
||||||
|
fillColor: Colors.transparent,
|
||||||
|
borderColor: Colors.transparent,
|
||||||
|
dataEntries: [
|
||||||
|
const RadarEntry(value: 0),
|
||||||
|
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),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
swapAnimationDuration: const Duration(
|
||||||
|
milliseconds: 150), // Optional
|
||||||
|
swapAnimationCurve:
|
||||||
|
Curves.linear, // Optional
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CompareThingy extends StatelessWidget {
|
||||||
|
final num greenSide;
|
||||||
|
final num redSide;
|
||||||
|
final String label;
|
||||||
|
final bool higherIsBetter;
|
||||||
|
final int? fractionDigits;
|
||||||
|
const CompareThingy(
|
||||||
|
{super.key,
|
||||||
|
required this.greenSide,
|
||||||
|
required this.redSide,
|
||||||
|
required this.label,
|
||||||
|
required this.higherIsBetter,
|
||||||
|
this.fractionDigits});
|
||||||
|
|
||||||
|
String verdict(num greenSide, num redSide, int fraction) {
|
||||||
|
var f = NumberFormat("+#,###.##;-#,###.##");
|
||||||
|
f.maximumFractionDigits = fraction;
|
||||||
|
return f.format((greenSide - redSide));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var f = NumberFormat("#,###.##");
|
||||||
|
f.maximumFractionDigits = fractionDigits ?? 0;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: const [Colors.green, Colors.transparent],
|
||||||
|
begin: Alignment.centerLeft,
|
||||||
|
end: Alignment.centerRight,
|
||||||
|
stops: [
|
||||||
|
0.0,
|
||||||
|
higherIsBetter
|
||||||
|
? greenSide > redSide
|
||||||
|
? 0.6
|
||||||
|
: 0
|
||||||
|
: greenSide < redSide
|
||||||
|
? 0.6
|
||||||
|
: 0
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
child: Text(
|
||||||
|
f.format(greenSide),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
shadows: <Shadow>[
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 3.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 8.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(fontSize: 22),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
verdict(greenSide, redSide,
|
||||||
|
fractionDigits != null ? fractionDigits! + 2 : 0),
|
||||||
|
style: const TextStyle(fontSize: 16),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: const [Colors.red, Colors.transparent],
|
||||||
|
begin: Alignment.centerRight,
|
||||||
|
end: Alignment.centerLeft,
|
||||||
|
stops: [
|
||||||
|
0.0,
|
||||||
|
higherIsBetter
|
||||||
|
? redSide > greenSide
|
||||||
|
? 0.6
|
||||||
|
: 0
|
||||||
|
: redSide < greenSide
|
||||||
|
? 0.6
|
||||||
|
: 0
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
child: Text(
|
||||||
|
f.format(redSide),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
shadows: <Shadow>[
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 3.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 8.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CompareBoolThingy extends StatelessWidget {
|
||||||
|
final bool greenSide;
|
||||||
|
final bool redSide;
|
||||||
|
final String label;
|
||||||
|
final bool trueIsBetter;
|
||||||
|
const CompareBoolThingy(
|
||||||
|
{super.key,
|
||||||
|
required this.greenSide,
|
||||||
|
required this.redSide,
|
||||||
|
required this.label,
|
||||||
|
required this.trueIsBetter});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
|
||||||
|
child: Row(children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: const [Colors.green, Colors.transparent],
|
||||||
|
begin: Alignment.centerLeft,
|
||||||
|
end: Alignment.centerRight,
|
||||||
|
stops: [
|
||||||
|
0.0,
|
||||||
|
trueIsBetter
|
||||||
|
? greenSide
|
||||||
|
? 0.6
|
||||||
|
: 0
|
||||||
|
: !greenSide
|
||||||
|
? 0.6
|
||||||
|
: 0
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
child: Text(
|
||||||
|
greenSide ? "Yes" : "No",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
shadows: <Shadow>[
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 3.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 8.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(fontSize: 22),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"---",
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: const [Colors.red, Colors.transparent],
|
||||||
|
begin: Alignment.centerRight,
|
||||||
|
end: Alignment.centerLeft,
|
||||||
|
stops: [
|
||||||
|
0.0,
|
||||||
|
trueIsBetter
|
||||||
|
? redSide
|
||||||
|
? 0.6
|
||||||
|
: 0
|
||||||
|
: !redSide
|
||||||
|
? 0.6
|
||||||
|
: 0
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
child: Text(
|
||||||
|
redSide ? "Yes" : "No",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
shadows: <Shadow>[
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 3.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 8.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CompareDurationThingy extends StatelessWidget {
|
||||||
|
final Duration greenSide;
|
||||||
|
final Duration redSide;
|
||||||
|
final String label;
|
||||||
|
final bool higherIsBetter;
|
||||||
|
const CompareDurationThingy(
|
||||||
|
{super.key,
|
||||||
|
required this.greenSide,
|
||||||
|
required this.redSide,
|
||||||
|
required this.label,
|
||||||
|
required this.higherIsBetter});
|
||||||
|
|
||||||
|
Duration verdict(Duration greenSide, Duration redSide) {
|
||||||
|
return greenSide - redSide;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
greenSide.toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
)),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
shadows: <Shadow>[
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 3.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 8.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
verdict(greenSide, redSide).toString(),
|
||||||
|
style: const TextStyle(fontSize: 16),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
redSide.toString(),
|
||||||
|
style: const TextStyle(fontSize: 22),
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CompareRegTimeThingy extends StatelessWidget {
|
||||||
|
final DateTime? greenSide;
|
||||||
|
final DateTime? redSide;
|
||||||
|
final String label;
|
||||||
|
final int? fractionDigits;
|
||||||
|
const CompareRegTimeThingy(
|
||||||
|
{super.key,
|
||||||
|
required this.greenSide,
|
||||||
|
required this.redSide,
|
||||||
|
required this.label,
|
||||||
|
this.fractionDigits});
|
||||||
|
|
||||||
|
String verdict(DateTime? greenSide, DateTime? redSide) {
|
||||||
|
var f = NumberFormat("#,### days later;#,### days before");
|
||||||
|
String result = "---";
|
||||||
|
if (greenSide != null && redSide != null)
|
||||||
|
result = f.format(greenSide.difference(redSide).inDays);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
DateFormat f = DateFormat.yMMMd();
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: const [Colors.green, Colors.transparent],
|
||||||
|
begin: Alignment.centerLeft,
|
||||||
|
end: Alignment.centerRight,
|
||||||
|
stops: [
|
||||||
|
0.0,
|
||||||
|
greenSide == null
|
||||||
|
? 0.6
|
||||||
|
: redSide != null && greenSide!.isBefore(redSide!)
|
||||||
|
? 0.6
|
||||||
|
: 0
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
child: Text(
|
||||||
|
greenSide != null ? f.format(greenSide!) : "From beginning",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
shadows: <Shadow>[
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 3.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 8.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(fontSize: 22),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
verdict(greenSide, redSide),
|
||||||
|
style: const TextStyle(fontSize: 16),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: const [Colors.red, Colors.transparent],
|
||||||
|
begin: Alignment.centerRight,
|
||||||
|
end: Alignment.centerLeft,
|
||||||
|
stops: [
|
||||||
|
0.0,
|
||||||
|
redSide == null
|
||||||
|
? 0.6
|
||||||
|
: greenSide != null && redSide!.isBefore(greenSide!)
|
||||||
|
? 0.6
|
||||||
|
: 0
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
child: Text(
|
||||||
|
redSide != null ? f.format(redSide!) : "From beginning",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
shadows: <Shadow>[
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 3.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0.0, 0.0),
|
||||||
|
blurRadius: 8.0,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -199,16 +199,16 @@ class TLThingy extends StatelessWidget {
|
||||||
dataSets: [
|
dataSets: [
|
||||||
RadarDataSet(
|
RadarDataSet(
|
||||||
dataEntries: [
|
dataEntries: [
|
||||||
RadarEntry(value: tl.apm! * 1),
|
RadarEntry(value: tl.apm! * apmWeight),
|
||||||
RadarEntry(value: tl.pps! * 45),
|
RadarEntry(value: tl.pps! * ppsWeight),
|
||||||
RadarEntry(value: tl.vs! * 0.444),
|
RadarEntry(value: tl.vs! * vsWeight),
|
||||||
RadarEntry(value: tl.nerdStats!.app * 185),
|
RadarEntry(value: tl.nerdStats!.app * appWeight),
|
||||||
RadarEntry(value: tl.nerdStats!.dss * 175),
|
RadarEntry(value: tl.nerdStats!.dss * dssWeight),
|
||||||
RadarEntry(value: tl.nerdStats!.dsp * 450),
|
RadarEntry(value: tl.nerdStats!.dsp * dspWeight),
|
||||||
RadarEntry(value: tl.nerdStats!.appdsp * 140),
|
RadarEntry(value: tl.nerdStats!.appdsp * appdspWeight),
|
||||||
RadarEntry(value: tl.nerdStats!.vsapm * 60),
|
RadarEntry(value: tl.nerdStats!.vsapm * vsWeight),
|
||||||
RadarEntry(value: tl.nerdStats!.cheese * 1.25),
|
RadarEntry(value: tl.nerdStats!.cheese * cheeseWeight),
|
||||||
RadarEntry(value: tl.nerdStats!.gbe * 315),
|
RadarEntry(value: tl.nerdStats!.gbe * gbeWeight),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
RadarDataSet(
|
RadarDataSet(
|
||||||
|
|
Loading…
Reference in New Issue