Compare view improvements, win chance implemented

Also now you can delete states and players from db
Numbers formatting
Ready for v0.1.0
This commit is contained in:
dan63047 2023-06-13 00:55:01 +03:00
parent b72d47e202
commit daefbb9504
11 changed files with 593 additions and 112 deletions

View File

@ -148,6 +148,23 @@ class TetrioService extends DB {
_tetrioStreamController.add(_players);
}
Future<void> deleteState(TetrioPlayer tetrioPlayer) async {
ensureDbIsOpen();
final db = getDatabaseOrThrow();
late List<TetrioPlayer> states;
states = await getPlayer(tetrioPlayer.userId);
_players[tetrioPlayer.userId]!.removeWhere((element) => element.state == tetrioPlayer.state);
states = _players[tetrioPlayer.userId]!;
final Map<String, dynamic> statesJson = {};
for (var e in states) {
statesJson.addEntries({e.state.millisecondsSinceEpoch.toString(): e.toJson()}.entries);
}
db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)},
where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]);
_players[tetrioPlayer.userId]!.add(tetrioPlayer);
_tetrioStreamController.add(_players);
}
Future<List<TetrioPlayer>> getPlayer(String id) async {
ensureDbIsOpen();
final db = getDatabaseOrThrow();

View File

@ -15,7 +15,7 @@ class CalcState extends State<CalcView> {
title: const Text("Stats Calculator"),
),
backgroundColor: Colors.black,
body: const SafeArea(child: Text("Maybe next commit idk... or shoud i think about CRUD??? idk idk")),
body: const SafeArea(child: Text("Next build")),
);
}
}

View File

@ -1,3 +1,4 @@
import 'dart:math';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
@ -8,6 +9,9 @@ TetrioPlayer? theGreenSide;
TetrioPlayer? theRedSide;
final TetrioService teto = TetrioService();
FocusNode greenFocusNode = FocusNode();
FocusNode redFocusNode = FocusNode();
class CompareView extends StatefulWidget {
final TetrioPlayer greenSide;
final TetrioPlayer? redSide;
@ -46,6 +50,16 @@ class CompareState extends State<CompareView> {
setState(() {});
}
double getWinrateByTR(double yourGlicko, double yourRD, double notyourGlicko, double notyourRD) {
double q = 400 * sqrt(1 + 3 * pow(log(10) / (400 * pi), 2));
double k = (notyourGlicko - yourGlicko) / (q * sqrt(pow(yourRD, 2) + pow(notyourRD, 2)));
//return 1 / (1 + pow(10, (notyourGlicko - yourGlicko)) / (400 * sqrt(1 + (3 * pow(0.00575646273, 2) * (pow(yourRD, 2) + pow(notyourRD, 2))) / pow(pi, 2))));
return ((1 /
(1 + pow(10, (notyourGlicko - yourGlicko) / (400 * sqrt(1 + (3 * pow(0.0057564273, 2) * (pow(yourRD, 2) + pow(notyourRD, 2)) / pow(pi, 2))))))));
//return 1 / (1 + pow(10, k));
}
// 1/(1+10^(rating[1]-rating[0])/(400*sqrt(1+(3*Q^2*(RD[0]^2+RD[1]^2))/PI^2)))) wtf where is
void _justUpdate() {
setState(() {});
}
@ -72,9 +86,22 @@ class CompareState extends State<CompareView> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
decoration: 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: PlayerSelector(player: theGreenSide, change: fetchGreenSide, updateState: _justUpdate),
child: PlayerSelector(
player: theGreenSide,
change: fetchGreenSide,
updateState: _justUpdate,
),
),
),
),
const Padding(
@ -82,9 +109,22 @@ class CompareState extends State<CompareView> {
child: Text("VS"),
),
Expanded(
child: Container(
decoration: 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: PlayerSelector(player: theRedSide, change: fetchRedSide, updateState: _justUpdate),
child: PlayerSelector(
player: theRedSide,
change: fetchRedSide,
updateState: _justUpdate,
),
),
),
),
],
@ -99,6 +139,10 @@ class CompareState extends State<CompareView> {
body: theGreenSide != null && theRedSide != null
? ListView(
children: [
if (theGreenSide!.role != "banned" && theRedSide!.role != "banned")
Column(
children: [
CompareRegTimeThingy(greenSide: theGreenSide!.registrationTime, redSide: theRedSide!.registrationTime, label: "Registred"),
CompareThingy(
label: "Level",
greenSide: theGreenSide!.level,
@ -106,6 +150,14 @@ class CompareState extends State<CompareView> {
higherIsBetter: true,
fractionDigits: 2,
),
if (!theGreenSide!.gameTime.isNegative && !theRedSide!.gameTime.isNegative)
CompareThingy(
greenSide: theGreenSide!.gameTime.inMicroseconds / 1000000 / 60 / 60,
redSide: theRedSide!.gameTime.inMicroseconds / 1000000 / 60 / 60,
label: "Hours Played",
higherIsBetter: true,
fractionDigits: 2,
),
if (theGreenSide!.gamesPlayed >= 0 && theRedSide!.gamesPlayed >= 0)
CompareThingy(
label: "Online Games",
@ -126,6 +178,10 @@ class CompareState extends State<CompareView> {
redSide: theRedSide!.friendCount,
higherIsBetter: true,
),
],
)
else
CompareBoolThingy(greenSide: theGreenSide!.role == "banned", redSide: theRedSide!.role == "banned", label: "Banned", trueIsBetter: false),
const Divider(),
theGreenSide!.tlSeason1.gamesPlayed > 0 && theRedSide!.tlSeason1.gamesPlayed > 0
? Column(
@ -217,37 +273,11 @@ class CompareState extends State<CompareView> {
),
],
)
: Padding(
padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
child: Row(children: [
Expanded(
child: Text(
theGreenSide!.tlSeason1.gamesPlayed > 0 ? "Yes" : "No",
style: const TextStyle(fontSize: 22),
textAlign: TextAlign.start,
)),
Column(
children: const [
Text(
"Played Tetra League",
style: TextStyle(fontSize: 22),
textAlign: TextAlign.center,
),
Text(
"---",
style: TextStyle(fontSize: 16),
textAlign: TextAlign.center,
)
],
),
Expanded(
child: Text(
theRedSide!.tlSeason1.gamesPlayed > 0 ? "Yes" : "No",
style: const TextStyle(fontSize: 22),
textAlign: TextAlign.end,
)),
]),
),
: CompareBoolThingy(
greenSide: theGreenSide!.tlSeason1.gamesPlayed > 0,
redSide: theRedSide!.tlSeason1.gamesPlayed > 0,
label: "Played Tetra League",
trueIsBetter: false),
const Divider(),
if (theGreenSide!.tlSeason1.apm != null &&
theRedSide!.tlSeason1.apm != null &&
@ -393,8 +423,8 @@ class CompareState extends State<CompareView> {
},
dataSets: [
RadarDataSet(
fillColor: const Color.fromARGB(117, 105, 240, 175),
borderColor: Colors.greenAccent,
fillColor: const Color.fromARGB(115, 76, 175, 79),
borderColor: Colors.green,
dataEntries: [
RadarEntry(value: theGreenSide!.tlSeason1.apm! * 1),
RadarEntry(value: theGreenSide!.tlSeason1.pps! * 45),
@ -409,8 +439,8 @@ class CompareState extends State<CompareView> {
],
),
RadarDataSet(
fillColor: const Color.fromARGB(117, 255, 82, 82),
borderColor: Colors.redAccent,
fillColor: const Color.fromARGB(115, 244, 67, 54),
borderColor: Colors.red,
dataEntries: [
RadarEntry(value: theRedSide!.tlSeason1.apm! * 1),
RadarEntry(value: theRedSide!.tlSeason1.pps! * 45),
@ -482,8 +512,8 @@ class CompareState extends State<CompareView> {
},
dataSets: [
RadarDataSet(
fillColor: const Color.fromARGB(117, 105, 240, 175),
borderColor: Colors.greenAccent,
fillColor: Color.fromARGB(115, 76, 175, 79),
borderColor: Colors.green,
dataEntries: [
RadarEntry(value: theGreenSide!.tlSeason1.playstyle!.opener),
RadarEntry(value: theGreenSide!.tlSeason1.playstyle!.stride),
@ -492,8 +522,8 @@ class CompareState extends State<CompareView> {
],
),
RadarDataSet(
fillColor: const Color.fromARGB(117, 255, 82, 82),
borderColor: Colors.redAccent,
fillColor: Color.fromARGB(115, 244, 67, 54),
borderColor: Colors.red,
dataEntries: [
RadarEntry(value: theRedSide!.tlSeason1.playstyle!.opener),
RadarEntry(value: theRedSide!.tlSeason1.playstyle!.stride),
@ -533,6 +563,17 @@ class CompareState extends State<CompareView> {
padding: const EdgeInsets.only(bottom: 16),
child: Text("Win Chance", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
),
CompareThingy(
label: "By Glicko",
greenSide: getWinrateByTR(theGreenSide!.tlSeason1.glicko!, theGreenSide!.tlSeason1.rd!, theRedSide!.tlSeason1.glicko!,
theRedSide!.tlSeason1.rd!) *
100,
redSide: getWinrateByTR(theRedSide!.tlSeason1.glicko!, theRedSide!.tlSeason1.rd!, theGreenSide!.tlSeason1.glicko!,
theGreenSide!.tlSeason1.rd!) *
100,
fractionDigits: 2,
higherIsBetter: true,
),
],
)
],
@ -566,9 +607,25 @@ class PlayerSelector extends StatelessWidget {
decoration: const InputDecoration(counter: Offstage()),
onSubmitted: (String value) {
change(value);
},
}),
if (player != null)
Text(
player!.toString(),
style: TextStyle(
shadows: <Shadow>[
Shadow(
offset: Offset(0.0, 0.0),
blurRadius: 3.0,
color: Colors.black,
),
if (player != null) Text(player!.toString())
Shadow(
offset: Offset(0.0, 0.0),
blurRadius: 8.0,
color: Colors.black,
),
],
),
)
],
);
}
@ -598,12 +655,43 @@ class CompareThingy extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [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: [
@ -620,8 +708,216 @@ class CompareThingy extends StatelessWidget {
],
),
Expanded(
child: Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [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: EdgeInsets.all(4),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [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: EdgeInsets.all(4),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [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,
)),
@ -630,3 +926,119 @@ class CompareThingy extends StatelessWidget {
);
}
}
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: EdgeInsets.all(4),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [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: EdgeInsets.all(4),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [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,
),
)),
],
),
);
}
}

View File

@ -1,4 +1,5 @@
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';
@ -16,6 +17,8 @@ late SharedPreferences prefs;
const allowedHeightForPlayerIdInPixels = 40.0;
const allowedHeightForPlayerBioInPixels = 30.0;
const givenTextHeightByScreenPercentage = 0.3;
final NumberFormat timeInSec = NumberFormat("#,###.###s.");
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
class MainView extends StatefulWidget {
const MainView({Key? key}) : super(key: key);
@ -364,10 +367,18 @@ class _RecordThingy extends StatelessWidget {
else if (record!.stream.contains("blitz"))
Text("Blitz", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
if (record!.stream.contains("40l"))
Text(record!.endContext!.finalTime.toString(), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28))
Text(timeInSec.format(record!.endContext!.finalTime.inMicroseconds / 1000000),
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28))
else if (record!.stream.contains("blitz"))
Text(record!.endContext!.score.toString(), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
Text(NumberFormat.decimalPattern().format(record!.endContext!.score),
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
if (record!.rank != null) StatCellNum(playerStat: record!.rank!, playerStatLabel: "Leaderboard Placement", isScreenBig: bigScreen),
Text("Obtained ${dateFormat.format(record!.timestamp!)}",
textAlign: TextAlign.center,
style: const TextStyle(
fontFamily: "Eurostile Round",
fontSize: 16,
)),
Padding(
padding: const EdgeInsets.fromLTRB(0, 48, 0, 48),
child: Wrap(
@ -586,11 +597,9 @@ class _OtherThingy extends StatelessWidget {
child: Column(
children: [
Text("Zen", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
Text("Level ${zen!.level}", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
Text(
"Score ${zen!.score}",
style: const TextStyle(fontSize: 18),
),
Text("Level ${NumberFormat.decimalPattern().format(zen!.level)}",
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
Text("Score ${NumberFormat.decimalPattern().format(zen!.score)}", style: const TextStyle(fontSize: 18)),
],
),
),

View File

@ -108,7 +108,12 @@ class SettingsState extends State<SettingsView> {
const Divider(),
ListTile(
title: const Text("About app"),
subtitle: Text("${_packageInfo.appName} (${_packageInfo.packageName}) Version ${_packageInfo.version} Build ${_packageInfo.buildNumber}"),
subtitle: Text("""
${_packageInfo.appName} (${_packageInfo.packageName}) Version ${_packageInfo.version} Build ${_packageInfo.buildNumber}
Developed by dan63047
Formulas provided by kerrmunism
"""),
),
],
)),

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/views/compare_view.dart';
import 'package:tetra_stats/views/state_view.dart';
class StatesView extends StatefulWidget {
@ -10,6 +12,8 @@ class StatesView extends StatefulWidget {
State<StatefulWidget> createState() => StatesState();
}
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
class StatesState extends State<StatesView> {
@override
Widget build(BuildContext context) {
@ -23,11 +27,16 @@ class StatesState extends State<StatesView> {
itemCount: widget.states.length,
itemBuilder: (context, index) {
return ListTile(
title: Text("On ${widget.states[index].state}"),
title: Text("On ${dateFormat.format(widget.states[index].state)}"),
subtitle: Text("Level ${widget.states[index].level.toStringAsFixed(2)} level, ${widget.states[index].gameTime} of gametime"),
trailing: IconButton(
icon: const Icon(Icons.delete_forever),
onPressed: () {},
onPressed: () {
DateTime nn = widget.states[index].state;
teto.deleteState(widget.states[index]).then((value) => setState(() {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("${dateFormat.format(nn)} state was removed from database!")));
}));
},
),
onTap: () {
Navigator.push(

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/services/tetrio_crud.dart';
import 'package:tetra_stats/views/states_view.dart';
@ -12,6 +13,8 @@ class TrackedPlayersView extends StatefulWidget {
State<StatefulWidget> createState() => TrackedPlayersState();
}
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
class TrackedPlayersState extends State<TrackedPlayersView> {
@override
Widget build(BuildContext context) {
@ -33,12 +36,22 @@ class TrackedPlayersState extends State<TrackedPlayersView> {
List<String> keys = allPlayers.keys.toList();
return NestedScrollView(
headerSliverBuilder: (context, value) {
String howManyPlayers(int numberOfPlayers) => Intl.plural(
numberOfPlayers,
zero: 'Empty list. Press "Track" button in previous view to add current player here',
one: 'There is only one player',
other: 'There are $numberOfPlayers players',
name: 'howManyPeople',
args: [numberOfPlayers],
desc: 'Description of how many people are seen in a place.',
examples: const {'numberOfPeople': 3},
);
return [
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(left: 16),
child: Text(
'There are ${allPlayers.length} players',
howManyPlayers(allPlayers.length),
style: const TextStyle(color: Colors.white, fontSize: 25),
),
)),
@ -50,10 +63,15 @@ class TrackedPlayersState extends State<TrackedPlayersView> {
itemBuilder: (context, index) {
return ListTile(
title: Text("${allPlayers[keys[index]]?.last.username}: ${allPlayers[keys[index]]?.length} states"),
subtitle: Text("From ${allPlayers[keys[index]]?.first.state} until ${allPlayers[keys[index]]?.last.state}"),
subtitle: Text(
"From ${dateFormat.format(allPlayers[keys[index]]!.first.state)} until ${dateFormat.format(allPlayers[keys[index]]!.last.state)}"),
trailing: IconButton(
icon: const Icon(Icons.delete_forever),
onPressed: () {},
onPressed: () {
String nn = allPlayers[keys[index]]!.last.username;
teto.deletePlayer(keys[index]);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("$nn states was removed from database!")));
},
),
onTap: () {
Navigator.push(

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class StatCellNum extends StatelessWidget {
const StatCellNum({super.key, required this.playerStat, required this.playerStatLabel, required this.isScreenBig, this.snackBar, this.fractionDigits});
@ -11,10 +12,11 @@ class StatCellNum extends StatelessWidget {
@override
Widget build(BuildContext context) {
NumberFormat f = NumberFormat.decimalPatternDigits(decimalDigits: fractionDigits ?? 0);
return Column(
children: [
Text(
fractionDigits != null ? playerStat.toStringAsFixed(fractionDigits!) : playerStat.floor().toString(),
f.format(playerStat),
style: TextStyle(
fontFamily: "Eurostile Round Extended",
fontSize: isScreenBig ? 32 : 24,

View File

@ -1,8 +1,12 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:tetra_stats/widgets/stat_sell_num.dart';
var fDiff = NumberFormat("+#,###.###;-#,###.###");
final NumberFormat f2 = NumberFormat.decimalPatternDigits(decimalDigits: 2);
class TLThingy extends StatelessWidget {
final TetraLeagueAlpha tl;
final String userID;
@ -32,10 +36,9 @@ class TLThingy extends StatelessWidget {
: Image.asset("res/tetrio_tl_alpha_ranks/${tl.rank}.png", height: 128),
Column(
children: [
Text("${tl.rating.toStringAsFixed(2)} TR",
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
Text("${f2.format(tl.rating)} TR", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
Text(
"Top ${(tl.percentile * 100).toStringAsFixed(2)}% (${tl.percentileRank.toUpperCase()}) • Top Rank: ${tl.bestRank.toUpperCase()} • Glicko: ${tl.glicko?.toStringAsFixed(2)}±${tl.rd?.toStringAsFixed(2)}${tl.decaying ? ' • Decaying' : ''}",
"Top ${f2.format(tl.percentile * 100)}% (${tl.percentileRank.toUpperCase()}) • Top Rank: ${tl.bestRank.toUpperCase()} • Glicko: ${f2.format(tl.glicko!)}±${f2.format(tl.rd!)}${tl.decaying ? ' • Decaying' : ''}",
textAlign: TextAlign.center,
),
],
@ -45,6 +48,7 @@ class TLThingy extends StatelessWidget {
else
Text("${10 - tl.gamesPlayed} games until being ranked",
softWrap: true,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: "Eurostile Round Extended",
fontSize: bigScreen ? 42 : 28,
@ -116,7 +120,7 @@ class TLThingy extends StatelessWidget {
style: TextStyle(fontSize: 24),
),
Text(
tl.estTr!.esttr.toStringAsFixed(2),
f2.format(tl.estTr!.esttr),
style: const TextStyle(fontSize: 24),
),
],
@ -130,7 +134,7 @@ class TLThingy extends StatelessWidget {
style: TextStyle(fontSize: 24),
),
Text(
tl.esttracc!.toStringAsFixed(2),
fDiff.format(tl.esttracc!),
style: const TextStyle(fontSize: 24),
),
],

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/views/compare_view.dart';
import 'package:intl/intl.dart';
import 'dart:developer' as developer;
import 'package:tetra_stats/widgets/stat_sell_num.dart';
@ -15,6 +16,8 @@ Future<void> copyToClipboard(String text) async {
await Clipboard.setData(ClipboardData(text: text));
}
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
class UserThingy extends StatelessWidget {
final TetrioPlayer player;
final bool showStateTimestamp;
@ -102,7 +105,7 @@ class UserThingy extends StatelessWidget {
],
)),
showStateTimestamp
? Text("Fetched ${player.state}")
? Text("Fetched ${dateFormat.format(player.state)}")
: Wrap(direction: Axis.horizontal, alignment: WrapAlignment.center, spacing: 25, crossAxisAlignment: WrapCrossAlignment.start, children: [
FutureBuilder(
future: teto.isPlayerTracking(player.userId),
@ -209,7 +212,7 @@ class UserThingy extends StatelessWidget {
children: [
Expanded(
child: Text(
"${player.country != null ? "${player.country?.toUpperCase()}" : ""}${player.role.capitalize()} account ${player.registrationTime == null ? "that was from very beginning" : 'created ${player.registrationTime}'}${player.botmaster != null ? " by ${player.botmaster}" : ""} ${player.supporterTier == 0 ? "Not a supporter" : "Supporter tier ${player.supporterTier}"}",
"${player.country != null ? "${player.country?.toUpperCase()}" : ""}${player.role.capitalize()} account ${player.registrationTime == null ? "that was from very beginning" : 'created ${dateFormat.format(player.registrationTime!)}'}${player.botmaster != null ? " by ${player.botmaster}" : ""} ${player.supporterTier == 0 ? "Not a supporter" : "Supporter tier ${player.supporterTier}"}",
textAlign: TextAlign.center,
style: const TextStyle(
fontFamily: "Eurostile Round",
@ -245,7 +248,9 @@ class UserThingy extends StatelessWidget {
spacing: 25,
children: [
Image.asset("res/tetrio_badges/${badge.badgeId}.png"),
Text(badge.ts != null ? "Obtained ${badge.ts}" : "That badge was assigned manualy by TETR.IO admins"),
Text(badge.ts != null
? "Obtained ${dateFormat.format(badge.ts!)}"
: "That badge was assigned manualy by TETR.IO admins"),
],
)
],

View File

@ -14,7 +14,7 @@ publish_to: 'none'
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 0.0.2+2
version: 0.1.0+3
environment:
sdk: '>=2.19.6 <3.0.0'