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); _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 { Future<List<TetrioPlayer>> getPlayer(String id) async {
ensureDbIsOpen(); ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();

View File

@ -15,7 +15,7 @@ class CalcState extends State<CalcView> {
title: const Text("Stats Calculator"), title: const Text("Stats Calculator"),
), ),
backgroundColor: Colors.black, 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:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
@ -8,6 +9,9 @@ TetrioPlayer? theGreenSide;
TetrioPlayer? theRedSide; TetrioPlayer? theRedSide;
final TetrioService teto = TetrioService(); final TetrioService teto = TetrioService();
FocusNode greenFocusNode = FocusNode();
FocusNode redFocusNode = FocusNode();
class CompareView extends StatefulWidget { class CompareView extends StatefulWidget {
final TetrioPlayer greenSide; final TetrioPlayer greenSide;
final TetrioPlayer? redSide; final TetrioPlayer? redSide;
@ -46,6 +50,16 @@ class CompareState extends State<CompareView> {
setState(() {}); 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() { void _justUpdate() {
setState(() {}); setState(() {});
} }
@ -72,9 +86,22 @@ class CompareState extends State<CompareView> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Expanded( Expanded(
child: Padding( child: Container(
padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), decoration: BoxDecoration(
child: PlayerSelector(player: theGreenSide, change: fetchGreenSide, updateState: _justUpdate), 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,
),
),
), ),
), ),
const Padding( const Padding(
@ -82,9 +109,22 @@ class CompareState extends State<CompareView> {
child: Text("VS"), child: Text("VS"),
), ),
Expanded( Expanded(
child: Padding( child: Container(
padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), decoration: BoxDecoration(
child: PlayerSelector(player: theRedSide, change: fetchRedSide, updateState: _justUpdate), 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,
),
),
), ),
), ),
], ],
@ -99,33 +139,49 @@ class CompareState extends State<CompareView> {
body: theGreenSide != null && theRedSide != null body: theGreenSide != null && theRedSide != null
? ListView( ? ListView(
children: [ children: [
CompareThingy( if (theGreenSide!.role != "banned" && theRedSide!.role != "banned")
label: "Level", Column(
greenSide: theGreenSide!.level, children: [
redSide: theRedSide!.level, CompareRegTimeThingy(greenSide: theGreenSide!.registrationTime, redSide: theRedSide!.registrationTime, label: "Registred"),
higherIsBetter: true, CompareThingy(
fractionDigits: 2, label: "Level",
), greenSide: theGreenSide!.level,
if (theGreenSide!.gamesPlayed >= 0 && theRedSide!.gamesPlayed >= 0) redSide: theRedSide!.level,
CompareThingy( higherIsBetter: true,
label: "Online Games", fractionDigits: 2,
greenSide: theGreenSide!.gamesPlayed, ),
redSide: theRedSide!.gamesPlayed, if (!theGreenSide!.gameTime.isNegative && !theRedSide!.gameTime.isNegative)
higherIsBetter: true, CompareThingy(
), greenSide: theGreenSide!.gameTime.inMicroseconds / 1000000 / 60 / 60,
if (theGreenSide!.gamesWon >= 0 && theRedSide!.gamesWon >= 0) redSide: theRedSide!.gameTime.inMicroseconds / 1000000 / 60 / 60,
CompareThingy( label: "Hours Played",
label: "Games Won", higherIsBetter: true,
greenSide: theGreenSide!.gamesWon, fractionDigits: 2,
redSide: theRedSide!.gamesWon, ),
higherIsBetter: true, if (theGreenSide!.gamesPlayed >= 0 && theRedSide!.gamesPlayed >= 0)
), CompareThingy(
CompareThingy( label: "Online Games",
label: "Friends", greenSide: theGreenSide!.gamesPlayed,
greenSide: theGreenSide!.friendCount, redSide: theRedSide!.gamesPlayed,
redSide: theRedSide!.friendCount, higherIsBetter: true,
higherIsBetter: true, ),
), if (theGreenSide!.gamesWon >= 0 && theRedSide!.gamesWon >= 0)
CompareThingy(
label: "Games Won",
greenSide: theGreenSide!.gamesWon,
redSide: theRedSide!.gamesWon,
higherIsBetter: true,
),
CompareThingy(
label: "Friends",
greenSide: theGreenSide!.friendCount,
redSide: theRedSide!.friendCount,
higherIsBetter: true,
),
],
)
else
CompareBoolThingy(greenSide: theGreenSide!.role == "banned", redSide: theRedSide!.role == "banned", label: "Banned", trueIsBetter: false),
const Divider(), const Divider(),
theGreenSide!.tlSeason1.gamesPlayed > 0 && theRedSide!.tlSeason1.gamesPlayed > 0 theGreenSide!.tlSeason1.gamesPlayed > 0 && theRedSide!.tlSeason1.gamesPlayed > 0
? Column( ? Column(
@ -217,37 +273,11 @@ class CompareState extends State<CompareView> {
), ),
], ],
) )
: Padding( : CompareBoolThingy(
padding: const EdgeInsets.fromLTRB(16, 2, 16, 2), greenSide: theGreenSide!.tlSeason1.gamesPlayed > 0,
child: Row(children: [ redSide: theRedSide!.tlSeason1.gamesPlayed > 0,
Expanded( label: "Played Tetra League",
child: Text( trueIsBetter: false),
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,
)),
]),
),
const Divider(), const Divider(),
if (theGreenSide!.tlSeason1.apm != null && if (theGreenSide!.tlSeason1.apm != null &&
theRedSide!.tlSeason1.apm != null && theRedSide!.tlSeason1.apm != null &&
@ -393,8 +423,8 @@ class CompareState extends State<CompareView> {
}, },
dataSets: [ dataSets: [
RadarDataSet( RadarDataSet(
fillColor: const Color.fromARGB(117, 105, 240, 175), fillColor: const Color.fromARGB(115, 76, 175, 79),
borderColor: Colors.greenAccent, borderColor: Colors.green,
dataEntries: [ dataEntries: [
RadarEntry(value: theGreenSide!.tlSeason1.apm! * 1), RadarEntry(value: theGreenSide!.tlSeason1.apm! * 1),
RadarEntry(value: theGreenSide!.tlSeason1.pps! * 45), RadarEntry(value: theGreenSide!.tlSeason1.pps! * 45),
@ -409,8 +439,8 @@ class CompareState extends State<CompareView> {
], ],
), ),
RadarDataSet( RadarDataSet(
fillColor: const Color.fromARGB(117, 255, 82, 82), fillColor: const Color.fromARGB(115, 244, 67, 54),
borderColor: Colors.redAccent, borderColor: Colors.red,
dataEntries: [ dataEntries: [
RadarEntry(value: theRedSide!.tlSeason1.apm! * 1), RadarEntry(value: theRedSide!.tlSeason1.apm! * 1),
RadarEntry(value: theRedSide!.tlSeason1.pps! * 45), RadarEntry(value: theRedSide!.tlSeason1.pps! * 45),
@ -482,8 +512,8 @@ class CompareState extends State<CompareView> {
}, },
dataSets: [ dataSets: [
RadarDataSet( RadarDataSet(
fillColor: const Color.fromARGB(117, 105, 240, 175), fillColor: Color.fromARGB(115, 76, 175, 79),
borderColor: Colors.greenAccent, borderColor: Colors.green,
dataEntries: [ dataEntries: [
RadarEntry(value: theGreenSide!.tlSeason1.playstyle!.opener), RadarEntry(value: theGreenSide!.tlSeason1.playstyle!.opener),
RadarEntry(value: theGreenSide!.tlSeason1.playstyle!.stride), RadarEntry(value: theGreenSide!.tlSeason1.playstyle!.stride),
@ -492,8 +522,8 @@ class CompareState extends State<CompareView> {
], ],
), ),
RadarDataSet( RadarDataSet(
fillColor: const Color.fromARGB(117, 255, 82, 82), fillColor: Color.fromARGB(115, 244, 67, 54),
borderColor: Colors.redAccent, borderColor: Colors.red,
dataEntries: [ dataEntries: [
RadarEntry(value: theRedSide!.tlSeason1.playstyle!.opener), RadarEntry(value: theRedSide!.tlSeason1.playstyle!.opener),
RadarEntry(value: theRedSide!.tlSeason1.playstyle!.stride), RadarEntry(value: theRedSide!.tlSeason1.playstyle!.stride),
@ -533,6 +563,17 @@ class CompareState extends State<CompareView> {
padding: const EdgeInsets.only(bottom: 16), padding: const EdgeInsets.only(bottom: 16),
child: Text("Win Chance", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), 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,
),
], ],
) )
], ],
@ -559,16 +600,32 @@ class PlayerSelector extends StatelessWidget {
return Column( return Column(
children: [ children: [
TextField( TextField(
autocorrect: false, autocorrect: false,
enableSuggestions: false, enableSuggestions: false,
maxLength: 25, maxLength: 25,
controller: playerController, controller: playerController,
decoration: const InputDecoration(counter: Offstage()), decoration: const InputDecoration(counter: Offstage()),
onSubmitted: (String value) { onSubmitted: (String value) {
change(value); change(value);
}, }),
), if (player != null)
if (player != null) Text(player!.toString()) Text(
player!.toString(),
style: TextStyle(
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,
),
],
),
)
], ],
); );
} }
@ -598,12 +655,43 @@ class CompareThingy extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Expanded( Expanded(
child: Text( child: Container(
f.format(greenSide), padding: EdgeInsets.all(4),
style: const TextStyle( decoration: BoxDecoration(
fontSize: 22, 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,
), ),
textAlign: TextAlign.start,
)), )),
Column( Column(
children: [ children: [
@ -619,9 +707,217 @@ 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( Expanded(
child: Text( child: Text(
f.format(redSide), 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), style: const TextStyle(fontSize: 22),
textAlign: TextAlign.end, 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:flutter/material.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 'dart:developer' as developer;
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -16,6 +17,8 @@ late SharedPreferences prefs;
const allowedHeightForPlayerIdInPixels = 40.0; const allowedHeightForPlayerIdInPixels = 40.0;
const allowedHeightForPlayerBioInPixels = 30.0; const allowedHeightForPlayerBioInPixels = 30.0;
const givenTextHeightByScreenPercentage = 0.3; const givenTextHeightByScreenPercentage = 0.3;
final NumberFormat timeInSec = NumberFormat("#,###.###s.");
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
class MainView extends StatefulWidget { class MainView extends StatefulWidget {
const MainView({Key? key}) : super(key: key); const MainView({Key? key}) : super(key: key);
@ -364,10 +367,18 @@ class _RecordThingy extends StatelessWidget {
else if (record!.stream.contains("blitz")) else if (record!.stream.contains("blitz"))
Text("Blitz", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), Text("Blitz", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
if (record!.stream.contains("40l")) 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")) 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), 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(
padding: const EdgeInsets.fromLTRB(0, 48, 0, 48), padding: const EdgeInsets.fromLTRB(0, 48, 0, 48),
child: Wrap( child: Wrap(
@ -586,11 +597,9 @@ class _OtherThingy extends StatelessWidget {
child: Column( child: Column(
children: [ children: [
Text("Zen", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), 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("Level ${NumberFormat.decimalPattern().format(zen!.level)}",
Text( style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
"Score ${zen!.score}", Text("Score ${NumberFormat.decimalPattern().format(zen!.score)}", style: const TextStyle(fontSize: 18)),
style: const TextStyle(fontSize: 18),
),
], ],
), ),
), ),

View File

@ -108,7 +108,12 @@ class SettingsState extends State<SettingsView> {
const Divider(), const Divider(),
ListTile( ListTile(
title: const Text("About app"), 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:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.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'; import 'package:tetra_stats/views/state_view.dart';
class StatesView extends StatefulWidget { class StatesView extends StatefulWidget {
@ -10,6 +12,8 @@ class StatesView extends StatefulWidget {
State<StatefulWidget> createState() => StatesState(); State<StatefulWidget> createState() => StatesState();
} }
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
class StatesState extends State<StatesView> { class StatesState extends State<StatesView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -23,11 +27,16 @@ class StatesState extends State<StatesView> {
itemCount: widget.states.length, itemCount: widget.states.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( 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"), subtitle: Text("Level ${widget.states[index].level.toStringAsFixed(2)} level, ${widget.states[index].gameTime} of gametime"),
trailing: IconButton( trailing: IconButton(
icon: const Icon(Icons.delete_forever), 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: () { onTap: () {
Navigator.push( Navigator.push(

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.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/views/states_view.dart'; import 'package:tetra_stats/views/states_view.dart';
@ -12,6 +13,8 @@ class TrackedPlayersView extends StatefulWidget {
State<StatefulWidget> createState() => TrackedPlayersState(); State<StatefulWidget> createState() => TrackedPlayersState();
} }
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
class TrackedPlayersState extends State<TrackedPlayersView> { class TrackedPlayersState extends State<TrackedPlayersView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -33,12 +36,22 @@ class TrackedPlayersState extends State<TrackedPlayersView> {
List<String> keys = allPlayers.keys.toList(); List<String> keys = allPlayers.keys.toList();
return NestedScrollView( return NestedScrollView(
headerSliverBuilder: (context, value) { 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 [ return [
SliverToBoxAdapter( SliverToBoxAdapter(
child: Padding( child: Padding(
padding: const EdgeInsets.only(left: 16), padding: const EdgeInsets.only(left: 16),
child: Text( child: Text(
'There are ${allPlayers.length} players', howManyPlayers(allPlayers.length),
style: const TextStyle(color: Colors.white, fontSize: 25), style: const TextStyle(color: Colors.white, fontSize: 25),
), ),
)), )),
@ -50,10 +63,15 @@ class TrackedPlayersState extends State<TrackedPlayersView> {
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return ListTile(
title: Text("${allPlayers[keys[index]]?.last.username}: ${allPlayers[keys[index]]?.length} states"), 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( trailing: IconButton(
icon: const Icon(Icons.delete_forever), 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: () { onTap: () {
Navigator.push( Navigator.push(

View File

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

View File

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

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
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/views/compare_view.dart'; import 'package:tetra_stats/views/compare_view.dart';
import 'package:intl/intl.dart';
import 'dart:developer' as developer; import 'dart:developer' as developer;
import 'package:tetra_stats/widgets/stat_sell_num.dart'; 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)); await Clipboard.setData(ClipboardData(text: text));
} }
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
class UserThingy extends StatelessWidget { class UserThingy extends StatelessWidget {
final TetrioPlayer player; final TetrioPlayer player;
final bool showStateTimestamp; final bool showStateTimestamp;
@ -102,7 +105,7 @@ class UserThingy extends StatelessWidget {
], ],
)), )),
showStateTimestamp showStateTimestamp
? Text("Fetched ${player.state}") ? Text("Fetched ${dateFormat.format(player.state)}")
: Wrap(direction: Axis.horizontal, alignment: WrapAlignment.center, spacing: 25, crossAxisAlignment: WrapCrossAlignment.start, children: [ : Wrap(direction: Axis.horizontal, alignment: WrapAlignment.center, spacing: 25, crossAxisAlignment: WrapCrossAlignment.start, children: [
FutureBuilder( FutureBuilder(
future: teto.isPlayerTracking(player.userId), future: teto.isPlayerTracking(player.userId),
@ -209,7 +212,7 @@ class UserThingy extends StatelessWidget {
children: [ children: [
Expanded( Expanded(
child: Text( 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, textAlign: TextAlign.center,
style: const TextStyle( style: const TextStyle(
fontFamily: "Eurostile Round", fontFamily: "Eurostile Round",
@ -245,7 +248,9 @@ class UserThingy extends StatelessWidget {
spacing: 25, spacing: 25,
children: [ children: [
Image.asset("res/tetrio_badges/${badge.badgeId}.png"), 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 # 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 # 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. # 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: environment:
sdk: '>=2.19.6 <3.0.0' sdk: '>=2.19.6 <3.0.0'