diff --git a/lib/services/tetrio_crud.dart b/lib/services/tetrio_crud.dart index 21d8f4b..c23d487 100644 --- a/lib/services/tetrio_crud.dart +++ b/lib/services/tetrio_crud.dart @@ -148,6 +148,23 @@ class TetrioService extends DB { _tetrioStreamController.add(_players); } + Future deleteState(TetrioPlayer tetrioPlayer) async { + ensureDbIsOpen(); + final db = getDatabaseOrThrow(); + late List states; + states = await getPlayer(tetrioPlayer.userId); + _players[tetrioPlayer.userId]!.removeWhere((element) => element.state == tetrioPlayer.state); + states = _players[tetrioPlayer.userId]!; + final Map 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> getPlayer(String id) async { ensureDbIsOpen(); final db = getDatabaseOrThrow(); diff --git a/lib/views/calc_view.dart b/lib/views/calc_view.dart index c012a5c..6a6a9be 100644 --- a/lib/views/calc_view.dart +++ b/lib/views/calc_view.dart @@ -15,7 +15,7 @@ class CalcState extends State { 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")), ); } } diff --git a/lib/views/compare_view.dart b/lib/views/compare_view.dart index f2b5b5d..6ecf9d7 100644 --- a/lib/views/compare_view.dart +++ b/lib/views/compare_view.dart @@ -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 { 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 { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: Padding( - padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), - child: PlayerSelector(player: theGreenSide, change: fetchGreenSide, updateState: _justUpdate), + 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, + ), + ), ), ), const Padding( @@ -82,9 +109,22 @@ class CompareState extends State { child: Text("VS"), ), Expanded( - child: Padding( - padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), - child: PlayerSelector(player: theRedSide, change: fetchRedSide, updateState: _justUpdate), + 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, + ), + ), ), ), ], @@ -99,33 +139,49 @@ class CompareState extends State { body: theGreenSide != null && theRedSide != null ? ListView( children: [ - CompareThingy( - label: "Level", - greenSide: theGreenSide!.level, - redSide: theRedSide!.level, - higherIsBetter: true, - fractionDigits: 2, - ), - if (theGreenSide!.gamesPlayed >= 0 && theRedSide!.gamesPlayed >= 0) - CompareThingy( - label: "Online Games", - greenSide: theGreenSide!.gamesPlayed, - redSide: theRedSide!.gamesPlayed, - 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, - ), + if (theGreenSide!.role != "banned" && theRedSide!.role != "banned") + Column( + children: [ + CompareRegTimeThingy(greenSide: theGreenSide!.registrationTime, redSide: theRedSide!.registrationTime, label: "Registred"), + CompareThingy( + label: "Level", + greenSide: theGreenSide!.level, + redSide: theRedSide!.level, + 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", + greenSide: theGreenSide!.gamesPlayed, + redSide: theRedSide!.gamesPlayed, + 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(), theGreenSide!.tlSeason1.gamesPlayed > 0 && theRedSide!.tlSeason1.gamesPlayed > 0 ? Column( @@ -217,37 +273,11 @@ class CompareState extends State { ), ], ) - : 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 { }, 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 { ], ), 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 { }, 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 { ], ), 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 { 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, + ), ], ) ], @@ -559,16 +600,32 @@ class PlayerSelector extends StatelessWidget { return Column( children: [ TextField( - autocorrect: false, - enableSuggestions: false, - maxLength: 25, - controller: playerController, - decoration: const InputDecoration(counter: Offstage()), - onSubmitted: (String value) { - change(value); - }, - ), - if (player != null) Text(player!.toString()) + autocorrect: false, + enableSuggestions: false, + maxLength: 25, + controller: playerController, + decoration: const InputDecoration(counter: Offstage()), + onSubmitted: (String value) { + change(value); + }), + if (player != null) + Text( + player!.toString(), + style: TextStyle( + shadows: [ + 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, children: [ Expanded( - child: Text( - f.format(greenSide), - style: const TextStyle( - fontSize: 22, + 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( + 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( 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( + 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( + 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( + 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( - f.format(redSide), + greenSide.toString(), + style: const TextStyle( + fontSize: 22, + ), + textAlign: TextAlign.start, + )), + Column( + children: [ + Text( + label, + style: const TextStyle( + fontSize: 22, + shadows: [ + 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( + 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( + 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, + ), + )), + ], + ), + ); + } +} diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 6926030..ba2fc8f 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -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)), ], ), ), diff --git a/lib/views/settings_view.dart b/lib/views/settings_view.dart index 4b52a21..db64697 100644 --- a/lib/views/settings_view.dart +++ b/lib/views/settings_view.dart @@ -108,7 +108,12 @@ class SettingsState extends State { 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 +"""), ), ], )), diff --git a/lib/views/states_view.dart b/lib/views/states_view.dart index be48b39..a3058f5 100644 --- a/lib/views/states_view.dart +++ b/lib/views/states_view.dart @@ -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 createState() => StatesState(); } +final DateFormat dateFormat = DateFormat.yMMMd().add_Hms(); + class StatesState extends State { @override Widget build(BuildContext context) { @@ -23,11 +27,16 @@ class StatesState extends State { 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( diff --git a/lib/views/tracked_players_view.dart b/lib/views/tracked_players_view.dart index bcddaf9..7696ef3 100644 --- a/lib/views/tracked_players_view.dart +++ b/lib/views/tracked_players_view.dart @@ -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 createState() => TrackedPlayersState(); } +final DateFormat dateFormat = DateFormat.yMMMd().add_Hms(); + class TrackedPlayersState extends State { @override Widget build(BuildContext context) { @@ -33,12 +36,22 @@ class TrackedPlayersState extends State { List 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 { 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( diff --git a/lib/widgets/stat_sell_num.dart b/lib/widgets/stat_sell_num.dart index ff55952..04e6439 100644 --- a/lib/widgets/stat_sell_num.dart +++ b/lib/widgets/stat_sell_num.dart @@ -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, diff --git a/lib/widgets/tl_thingy.dart b/lib/widgets/tl_thingy.dart index bb3f28c..bcdf7ab 100644 --- a/lib/widgets/tl_thingy.dart +++ b/lib/widgets/tl_thingy.dart @@ -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), ), ], diff --git a/lib/widgets/user_thingy.dart b/lib/widgets/user_thingy.dart index f45833a..79451cf 100644 --- a/lib/widgets/user_thingy.dart +++ b/lib/widgets/user_thingy.dart @@ -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 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"), ], ) ], diff --git a/pubspec.yaml b/pubspec.yaml index 37ed8aa..653fbbe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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'