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 '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) {
|
||||
value = value * 1000000;
|
||||
return Duration(microseconds: value.floor());
|
||||
|
@ -578,42 +590,46 @@ class TetraLeagueAlphaRecord{
|
|||
}
|
||||
|
||||
class EndContextMulti {
|
||||
String? userId;
|
||||
String? username;
|
||||
int? naturalOrder;
|
||||
int? inputs;
|
||||
int? piecesPlaced;
|
||||
Handling? handling;
|
||||
int? points;
|
||||
int? wins;
|
||||
double? secondary;
|
||||
List<double>? secondaryTracking;
|
||||
double? tertiary;
|
||||
List<double>? tertiaryTracking;
|
||||
double? extra;
|
||||
List<double>? extraTracking;
|
||||
bool? success;
|
||||
late String userId;
|
||||
late String username;
|
||||
late int naturalOrder;
|
||||
late int inputs;
|
||||
late int piecesPlaced;
|
||||
late Handling handling;
|
||||
late int points;
|
||||
late int wins;
|
||||
late double secondary;
|
||||
late List<double> secondaryTracking;
|
||||
late double tertiary;
|
||||
late List<double> tertiaryTracking;
|
||||
late double extra;
|
||||
late List<double> extraTracking;
|
||||
late bool success;
|
||||
late NerdStats nerdStats;
|
||||
late EstTr estTr;
|
||||
late Playstyle playstyle;
|
||||
|
||||
EndContextMulti(
|
||||
{this.userId,
|
||||
this.naturalOrder,
|
||||
this.inputs,
|
||||
this.piecesPlaced,
|
||||
this.handling,
|
||||
this.points,
|
||||
this.wins,
|
||||
this.secondary,
|
||||
this.secondaryTracking,
|
||||
this.tertiary,
|
||||
this.tertiaryTracking,
|
||||
this.extra,
|
||||
this.extraTracking,
|
||||
this.success});
|
||||
{required this.userId,
|
||||
required this.username,
|
||||
required this.naturalOrder,
|
||||
required this.inputs,
|
||||
required this.piecesPlaced,
|
||||
required this.handling,
|
||||
required this.points,
|
||||
required this.wins,
|
||||
required this.secondary,
|
||||
required this.secondaryTracking,
|
||||
required this.tertiary,
|
||||
required this.tertiaryTracking,
|
||||
required this.extra,
|
||||
required this.extraTracking,
|
||||
required this.success});
|
||||
|
||||
EndContextMulti.fromJson(Map<String, dynamic> json) {
|
||||
userId = json['user']['_id'];
|
||||
username = json['user']['username'];
|
||||
handling = json['handling'] != null ? Handling.fromJson(json['handling']) : null;
|
||||
handling = Handling.fromJson(json['handling']);
|
||||
success = json['success'];
|
||||
inputs = json['inputs'];
|
||||
piecesPlaced = json['piecesplaced'];
|
||||
|
@ -626,15 +642,16 @@ class EndContextMulti {
|
|||
tertiaryTracking = json['points']['tertiaryAvgTracking'].cast<double>();
|
||||
extra = json['points']['extra']['vs'];
|
||||
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() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['user']['_id'] = userId;
|
||||
data['user']['username'] = username;
|
||||
if (handling != null) {
|
||||
data['handling'] = handling!.toJson();
|
||||
}
|
||||
data['handling'] = handling.toJson();
|
||||
data['success'] = success;
|
||||
data['inputs'] = inputs;
|
||||
data['piecesplaced'] = piecesPlaced;
|
||||
|
|
|
@ -29,6 +29,8 @@ const String createTetrioUsersToTrack = '''
|
|||
|
||||
class TetrioService extends DB {
|
||||
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();
|
||||
factory TetrioService() => _shared;
|
||||
late final StreamController<Map<String, List<TetrioPlayer>>> _tetrioStreamController;
|
||||
|
@ -41,16 +43,16 @@ class TetrioService extends DB {
|
|||
@override
|
||||
Future<void> open() async {
|
||||
await super.open();
|
||||
await _cachePlayers();
|
||||
await _loadPlayers();
|
||||
}
|
||||
|
||||
Stream<Map<String, List<TetrioPlayer>>> get allPlayers => _tetrioStreamController.stream;
|
||||
|
||||
Future<void> _cachePlayers() async {
|
||||
Future<void> _loadPlayers() async {
|
||||
final allPlayers = await getAllPlayers();
|
||||
_players = allPlayers.toList().first; // ???
|
||||
_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 {
|
||||
|
@ -72,6 +74,19 @@ class TetrioService extends DB {
|
|||
}
|
||||
|
||||
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()}');
|
||||
final response = await http.get(url);
|
||||
|
||||
|
@ -83,13 +98,15 @@ class TetrioService extends DB {
|
|||
// await ensureDbIsOpen();
|
||||
// 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;
|
||||
} else {
|
||||
developer.log("getTLStream User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||
throw Exception("User doesn't exist");
|
||||
}
|
||||
} 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');
|
||||
}
|
||||
}
|
||||
|
@ -208,6 +225,19 @@ class TetrioService extends DB {
|
|||
}
|
||||
|
||||
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()}');
|
||||
final response = await http.get(url);
|
||||
|
||||
|
@ -219,13 +249,15 @@ class TetrioService extends DB {
|
|||
await ensureDbIsOpen();
|
||||
storeState(player);
|
||||
}
|
||||
developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud");
|
||||
_playersCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = player;
|
||||
return player;
|
||||
} 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");
|
||||
}
|
||||
} 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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -547,43 +547,43 @@ class CompareState extends State<CompareView> {
|
|||
RadarEntry(
|
||||
value: theGreenSide!
|
||||
.tlSeason1.apm! *
|
||||
1),
|
||||
apmWeight),
|
||||
RadarEntry(
|
||||
value: theGreenSide!
|
||||
.tlSeason1.pps! *
|
||||
45),
|
||||
ppsWeight),
|
||||
RadarEntry(
|
||||
value: theGreenSide!
|
||||
.tlSeason1.vs! *
|
||||
0.444),
|
||||
vsWeight),
|
||||
RadarEntry(
|
||||
value: theGreenSide!.tlSeason1
|
||||
.nerdStats!.app *
|
||||
185),
|
||||
appWeight),
|
||||
RadarEntry(
|
||||
value: theGreenSide!.tlSeason1
|
||||
.nerdStats!.dss *
|
||||
175),
|
||||
dssWeight),
|
||||
RadarEntry(
|
||||
value: theGreenSide!.tlSeason1
|
||||
.nerdStats!.dsp *
|
||||
450),
|
||||
dspWeight),
|
||||
RadarEntry(
|
||||
value: theGreenSide!.tlSeason1
|
||||
.nerdStats!.appdsp *
|
||||
140),
|
||||
appdspWeight),
|
||||
RadarEntry(
|
||||
value: theGreenSide!.tlSeason1
|
||||
.nerdStats!.vsapm *
|
||||
60),
|
||||
vsapmWeight),
|
||||
RadarEntry(
|
||||
value: theGreenSide!.tlSeason1
|
||||
.nerdStats!.cheese *
|
||||
1.25),
|
||||
cheeseWeight),
|
||||
RadarEntry(
|
||||
value: theGreenSide!.tlSeason1
|
||||
.nerdStats!.gbe *
|
||||
315),
|
||||
gbeWeight),
|
||||
],
|
||||
),
|
||||
RadarDataSet(
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'dart:developer' as developer;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/services/tetrio_crud.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/tl_thingy.dart';
|
||||
import 'package:tetra_stats/widgets/user_thingy.dart';
|
||||
|
@ -81,7 +81,6 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
_getPreferences()
|
||||
.then((value) => changePlayer(prefs.getString("player") ?? "dan63047"));
|
||||
super.initState();
|
||||
developer.log("Main view initialized", name: "main_view");
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -89,7 +88,6 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
_tabController.dispose();
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
developer.log("Main view disposed", name: "main_view");
|
||||
}
|
||||
|
||||
Future<void> _getPreferences() async {
|
||||
|
@ -155,10 +153,6 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
),
|
||||
PopupMenuButton(
|
||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
||||
// const PopupMenuItem(
|
||||
// value: "/compare",
|
||||
// child: Text('Compare'),
|
||||
// ),
|
||||
const PopupMenuItem(
|
||||
value: "/states",
|
||||
child: Text('Show stored data'),
|
||||
|
@ -182,7 +176,6 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
child: FutureBuilder<TetrioPlayer>(
|
||||
future: me,
|
||||
builder: (context, snapshot) {
|
||||
developer.log("builder ($context): $snapshot", name: "main_view");
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.none:
|
||||
return const Center(
|
||||
|
@ -225,10 +218,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
isScrollable: true,
|
||||
tabs: myTabs,
|
||||
onTap: (int tabId) {
|
||||
setState(() {
|
||||
developer.log("Tab changed to $tabId",
|
||||
name: "main_view");
|
||||
});
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -350,8 +340,6 @@ class _NavDrawerState extends State<NavDrawer> {
|
|||
leading: const Icon(Icons.home),
|
||||
title: Text(homePlayerNickname),
|
||||
onTap: () {
|
||||
developer.log("Navigator changed player",
|
||||
name: "main_view");
|
||||
widget.changePlayer(
|
||||
prefs.getString("player") ?? "dan63047");
|
||||
Navigator.of(context).pop();
|
||||
|
@ -367,8 +355,6 @@ class _NavDrawerState extends State<NavDrawer> {
|
|||
title: Text(
|
||||
allPlayers[keys[index]]?.last.username as String),
|
||||
onTap: () {
|
||||
developer.log("Navigator changed player",
|
||||
name: "main_view");
|
||||
widget.changePlayer(keys[index]);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
|
@ -401,10 +387,7 @@ class _TLRecords extends StatelessWidget {
|
|||
child: CircularProgressIndicator(color: Colors.white));
|
||||
case ConnectionState.done:
|
||||
if (snapshot.hasError) {
|
||||
return Text(snapshot.error.toString(),
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: 28));
|
||||
return Text(snapshot.error.toString(), style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28));
|
||||
} else {
|
||||
return ListView(
|
||||
physics: const ClampingScrollPhysics(),
|
||||
|
@ -414,7 +397,7 @@ class _TLRecords extends StatelessWidget {
|
|||
style: const TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
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!)),
|
||||
trailing: Column(mainAxisAlignment: MainAxisAlignment.end,
|
||||
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).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),
|
||||
),
|
||||
);},
|
||||
)]
|
||||
: [
|
||||
Text("No records",
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: 28))
|
||||
],
|
||||
: [const 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: [
|
||||
RadarDataSet(
|
||||
dataEntries: [
|
||||
RadarEntry(value: tl.apm! * 1),
|
||||
RadarEntry(value: tl.pps! * 45),
|
||||
RadarEntry(value: tl.vs! * 0.444),
|
||||
RadarEntry(value: tl.nerdStats!.app * 185),
|
||||
RadarEntry(value: tl.nerdStats!.dss * 175),
|
||||
RadarEntry(value: tl.nerdStats!.dsp * 450),
|
||||
RadarEntry(value: tl.nerdStats!.appdsp * 140),
|
||||
RadarEntry(value: tl.nerdStats!.vsapm * 60),
|
||||
RadarEntry(value: tl.nerdStats!.cheese * 1.25),
|
||||
RadarEntry(value: tl.nerdStats!.gbe * 315),
|
||||
RadarEntry(value: tl.apm! * apmWeight),
|
||||
RadarEntry(value: tl.pps! * ppsWeight),
|
||||
RadarEntry(value: tl.vs! * vsWeight),
|
||||
RadarEntry(value: tl.nerdStats!.app * appWeight),
|
||||
RadarEntry(value: tl.nerdStats!.dss * dssWeight),
|
||||
RadarEntry(value: tl.nerdStats!.dsp * dspWeight),
|
||||
RadarEntry(value: tl.nerdStats!.appdsp * appdspWeight),
|
||||
RadarEntry(value: tl.nerdStats!.vsapm * vsWeight),
|
||||
RadarEntry(value: tl.nerdStats!.cheese * cheeseWeight),
|
||||
RadarEntry(value: tl.nerdStats!.gbe * gbeWeight),
|
||||
],
|
||||
),
|
||||
RadarDataSet(
|
||||
|
|
Loading…
Reference in New Issue